Когда использовать ORM фреймворки?

MiksIr

miksir@home:~$
Он не троль, он просто мудак. Есть такая категория - образованные мудаки. Отличаются от обычных людей тем, что у них маленький, по-этому они пропагандируют свою мудачную сущность как могут, используя свои знания, а там, где их не хватает - выдумывая и копируя из гугла. Общаться с такими мудаками не интересно и не продуктивно, ибо они не умеют объяснять что-то и тем более развернуто аргументировать (мудаки же), но стоит оставить на форуме как маркер, что бы не стать таким же мудаком. Духовность, кстати, приближался к этому в свое время, но вовремя остановился.
 

Ragazzo

TDD interested
MiksIr
Ахах, так оно и есть :) "Кто не умеет, тот учит" или как там звучит эта фраза)
<оффтоп>ты кстати вроде хорошо знаешь Yii? на конфу не поедешь, ну YiiConf?</оффтоп>
 

Redjik

Джедай-мастер
Кстати раз уж про yii.

Сделал 2 недели назад средней навроченности магаз на Yii.
Обошелся чистейшим AR, пару мест для выборки пользовался SQL builder ом.

Плейн SQL не юзал вообще. (Хотя запросы,который генерил AR, профилировал).

Все бегает шустро и очень хорошо.

PHP:
	public function relations()
	{

		return array(
				'pages'=>array(self::MANY_MANY,'Page','products_pages(product_id,page_id)'),
				'myproducts'=>array(self::MANY_MANY,'Product','product_product(page_id,to_page_id)'),
				'myanalogs'=>array(self::MANY_MANY,'Product','product_product_analog(to_page_id,page_id)'),
				'imanalog'=>array(self::MANY_MANY,'Product','product_product_analog(page_id,to_page_id)'),
				'images'=>array(self::HAS_MANY,'Image','product_id'),
				'default_image'=>array(self::HAS_ONE,'Image','product_id','on'=>'t.image=default_image.id'),
				'hit_sales'=>array(self::HAS_ONE,'ProductPage','product_id','on'=>'t.id=hit_sales.product_id AND hit_sales.page_id=61'),
				'special_price'=>array(self::HAS_ONE,'ProductPage','product_id','on'=>'t.id=special_price.product_id AND special_price.page_id=38'),
				'recommended'=>array(self::HAS_ONE,'ProductPage','product_id','on'=>'t.id=recommended.product_id AND recommended.page_id=53'),
				'new'=>array(self::HAS_ONE,'ProductPage','product_id','on'=>'t.id=new.product_id AND new.page_id=37'),
		);
	}
Вот связи товара.
(Не пугайтесь hit_sales.product_id AND hit_sales.page_id=61 - я обязательно придумаю как сделать это изящно, сейчас товар может относиться к множеству категорий, и есть ряд служебных категорий 61,38,53 аля новинка, хит продаж.)

К чему все это?
ORM добро - я бы на SQL эти связи прописывал бы и отлаживал не одну неделю.
 

caballero

Новичок
MiksIr
А кроме площадной брани что нибудь можешь сказать?
Я понимаю что с одной извилиной тяжеловато но если постараться авось что нибудь по существу темы прорисуется.

я бы на SQL эти связи прописывал бы и отлаживал не одну неделю.
это просто незнание SQL. С опытом придет, и выбросишь этот код на помойку как страшный сон.
Когда то давно я тоже пользовался билдерами и всякими построителями
даже Access использовал чтобы посмотреть как он нагенерит.
На самом деле там нефиг отлаживать, просто всякие ORM генерят очень громоздкий и неэфективный код.
Это примерно тоже как генерить неким билдером PHP код для построения страницы .
 

Redjik

Джедай-мастер
PHP:
SELECT `t`.`id` AS `t0_c0`, `t`.`title` AS `t0_c1`,
`t`.`text` AS `t0_c2`, `t`.`image` AS `t0_c3`, `t`.`alias` AS `t0_c4`,
`t`.`create_date` AS `t0_c5`, `t`.`published` AS `t0_c6`, `t`.`in_stock` AS
`t0_c7`, `t`.`price` AS `t0_c8`, `t`.`discount_price` AS `t0_c9`,
`t`.`rating` AS `t0_c10`, `t`.`votes` AS `t0_c11`, `t`.`seo_description` AS
`t0_c12`, `t`.`seo_keywords` AS `t0_c13`, `t`.`seo_title` AS `t0_c14`,
`t`.`product_code` AS `t0_c15`, `t`.`weight` AS `t0_c16`, `t`.`volume` AS
`t0_c17`, `t`.`quantity_show` AS `t0_c18`, `t`.`quantity_box` AS `t0_c19`,
`t`.`barcode` AS `t0_c20`, `default_image`.`id` AS `t1_c0`,
`default_image`.`product_id` AS `t1_c1`, `default_image`.`filename` AS
`t1_c2`, `images`.`id` AS `t2_c0`, `images`.`product_id` AS `t2_c1`,
`images`.`filename` AS `t2_c2`, `special_price`.`page_id` AS `t3_c0`,
`special_price`.`product_id` AS `t3_c1` FROM `products` `t`  LEFT OUTER
JOIN `images` `default_image` ON (`default_image`.`product_id`=`t`.`id`)
AND (t.image=default_image.id)  LEFT OUTER JOIN `images` `images` ON
(`images`.`product_id`=`t`.`id`)  LEFT OUTER JOIN `products_pages`
`special_price` ON (`special_price`.`product_id`=`t`.`id`) AND
(t.id=special_price.product_id AND special_price.page_id=38)  WHERE (alias
= :alias)
Вот что выдает AR Yii. Покажи изьяны.
 

caballero

Новичок
во первых, логическая часть запроса (то что начинается с FROM) по объему меньше чем код ORM для его генерации
и написать его руками было бы гораздо быстрее и он был бы еще короче.
но даже то что нагенерено гораздо читабельнее чем то что в посте выше.
Во всяком случае это может прочитать любой человек которому придется сопровождать код
потому как SQL он и в Африке SQL

во вторых: t0_c0, t0_c20, t2_c1 и т.д. Коментарии излишни
 

Redjik

Джедай-мастер
Я с доктриной давненько работал, но там dependencies тоже интересно проставляются

Это к тому что dependencies !== код генерирующий логическую часть запроса.
Связи которые там прописаны дают мне
1) Все товары из одной категории
2) Все товары из дочерних категорий - относительно текущий
3) Сортировки по цене, рейтингу, наличию как для категори, так и для всех вложенных
4) Детальную страницу самого товара
5) Различные сортировки товаров в админке, а так же сохранение всех связанных таблиц
6)...

И самое главное - все это делается буквально парой строчек, какие мне dependencies нужны - такие я и подключаю.
А код, вполне себе нормальные alias - защита от дурака.
 

Redjik

Джедай-мастер
во первых, логическая часть запроса (то что начинается с FROM) по объему меньше чем код ORM для его генерации
и написать его руками было бы гораздо быстрее и он был бы еще короче.
во вторых: t0_c0, t0_c20, t2_c1 и т.д. Коментарии излишни
Это придирка по стилю, мы же говорим про производительность вроде - да сама кодогенерация и расфасовывания в обьекты - процесс затратный, но на выходе SQL, который дает AR по explain идентичен тому что напишешь ручками, если не хуже.
 

caballero

Новичок
с производительностью тут повезло
потому как запрос по сути простой - просто несколько таблиц перевязано
на даже при той же производительности ты сам пишешь что расфасока - затратный процес. то есть уже суммарная эфективность ниже. а как оно нагенерит если нужно группировки и подзапросы фиг его знает. О пять же кодогенерация - время сравнимое с генерацией страницы - пройди дебагером и посмотри сколько оно приседаний делает. Для нагруженого проекта - немаловажно.
да и с чем ты explain сравниваешь?

опять же читабельность кода. Не будет же человек выдавать нагора сгенеренный запрос чтобы посмотреть что он делает.
По нижнему варианту я даже не видя БД примерно понял что за выборка, предыдущий вариант - абракадабра какая то.

На самом деле AR штука полезная но он имеет смысл для элементарных операций на уровне сущности-строки таблицы которые легко автоматизируются.

То что выше - не AR - там сборная солянка данных для которых даже создание объекта бессмысленно. Резве что если оно само генерит гриды и тогда пофиг какие там алиаасы в запросе.
Но есть же запросы и для бизнес логики - никуда не денешся. и тут большинство попыток прикрутить ORM (я такое не раз проходил и не только в PHP) заканчивается написанием кусков нативного SQL (даже в твоем примере - разве всякие AND не функциями ORM должны описыватся?). А на фига тогда ORM если часть запросов помимо него.
Только проект будет выглядеть как арлекин.
 

С.

Продвинутый новичок
При том, что caballero огрызается на каждое замечание в свой адрес как шавка, не могу не согласиться с ним. Есть смысл создавать некий API в терминах данного конкретного приложения, возможно эмулирущий объекты данного конкретного приложения. Например:

storage::getBasketContents($basketID);
storage::getProduct($productID);
storage::getProductsByCategory($category);
и т.п.

Внутри драйвера уже следует писать SQL запросы, оптимизированные под конкретную базу. В случае изменения хранилища этот драйвер переписывается в течение часа.

Мостопотамные ORM со своими оверхедами и объектами, никакого отношения не имеющими к данному конкретному приложению -- полный изврат.
 

Redjik

Джедай-мастер
С.

Это не совсем относится к ORM - это больше здравый смысл.

ЗЫ, Именно так лично я и обращаюсь к модели. Yii как раз заточен под это.
PHP:
		$ids_array = Page::getAllChildrenIds($alias);
		$products = Product::getAllProducts($ids_array);
Причем массив берется из кэша.
 

Redjik

Джедай-мастер
С.
кстати, под драйвером, ты имеешь ввиду PDO-образную обертку или экстеншен?
 

С.

Продвинутый новичок
Это не совсем относится к ORM - это больше здравый смысл.
В том-то и дело, что имеется тенденция диффузийно встраивать ORM в приложение и преподносить это как передовую ООПешную штучку. А это жуть! ORM можно в принципе использовать внутри АПИ, описанного мной, но любой здравомыслящий человек ивидит, что и там ему по большому счету нечего делать. Нет места для ORM в приложении! ORM поощрает размазывание компонентности/модульности приложения. А там, где он этого не делает, он создает ничем не обоснованный оверхед. Нет у ORM положительных сторон!
кстати, под драйвером, ты имеешь ввиду PDO-образную обертку или экстеншен?
А какая в принципе разница?
 

Redjik

Джедай-мастер
А какая в принципе разница?
Серв надо перезапускать :D

Я согласен по поводу ORM в целом, но ...
Всему свое место.

Для приложений средней руки, без навороченной логики - оно самое то.
Если без напильники можно очень быстро состряпать CRUD.

Когда я дорасту до проэктов/уровня, когда мне ORM/AR будет мешать, то я вполне справлюсь с этим. И буду уже делать свои обертки с plain Sql и разными интересными запросами.

Как бы сказала Малахова - Это Нормально!

ЗЫ. Поработать бы в какой-нибудь приличной компании, может быстрее бы ума набрался =)
Сдам месяца через два все проэкты и буду в ветку работа плакаться на аутсорс =)
 

AmdY

Пью пиво
Команда форума
ORM нужно использовать когда свои мозги появится и ты себе сможешь обосновать зачем он нужен. вон клоун caballero дмает что это AR или Query Builder, хотя Doctrine это в последнюю очередь sql завторы. Скажу даже страшное, doctrine может вообще не использовать sql, есть и mongodb решение.
ORM даёт объекты, инкапсулируют в себе логику и позволяют гибко наращивать функционал. Допустим решение для хранения древовидных структур с i18n делается элементарно, двумя строчками в Doctrine. Ещё лучше пример - soft delete, зачастую данные не удалёются из базы, а просто помечаются как удалённые и возникает проблема, что кто-то по незнанию может запросить их без WHERE deleted <> 0. Ещё замечательный пример - это кастомные типы полей, например адрескак правило включает в себя набор "страна, город, улица ...", в доктрине достаточно просто завести новый тип и использовать стандпртное решение во всём проекте.
Опять же запрос на который ругался caballero, мы же знаем что в реальных проектах название полей пересекаются. Тогда приходится в запросах вешать алиасы, а доктрина делает это автоматически и складывает в иерархический массив. $user['Addresses'][0]['county']

и т.д и т.д. Если ты не решаешь такие задачи, то ORM действительно не нужна.
 

С.

Продвинутый новичок
ORM даёт объекты, инкапсулируют в себе логику
В силу своей общности ORM не сможет инкупсалировать в себе всю модель данных данного приложения (если модель конечно не самая примитивная). В общем случае это всегда будет ORM+δ. Вот это меня и смущает. Если инкапсулировать ORM+δ в отдельную сущность, то вся прелесть ORM теряется.

Таким образом получается, что ORМ удобна как раз только на простых/типовых/шаблонных моделях данных. Причем таких моделей в законченных приложениях на самом деле не так много. Они рождаются зачастую необоснованно, из-за т.н. "думания таблицами" или какими другими паттернами.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Ещё лучше пример - soft delete, зачастую данные не удалёются из базы, а просто помечаются как удалённые и возникает проблема, что кто-то по незнанию может запросить их без WHERE deleted <> 0.
Решается через
Код:
create view foo_active as
select * from foo
where deleted <> 0;
и выдачу доступа незнающим исключительно к foo_active.

Ещё замечательный пример - это кастомные типы полей, например адрескак правило включает в себя набор "страна, город, улица ...", в доктрине достаточно просто завести новый тип и использовать стандпртное решение во всём проекте.
Как ни странно,
Код:
create type address as (
    country int,
    city text,
    street text
);
тоже можно сделать и использовать во всём проекте. С тем плюсом, что это будет работать и при доступе в базу напрямую, а не через прокладку.

и т.д и т.д. Если ты не решаешь такие задачи, то ORM действительно не нужна.
Несмотря на своеобразный стиль caballero, не могу с ним не согласиться, большая часть предлагаемых прокладкой "решений" замечательно реализуется в базе, и использование прокладок вызвано скорее неумением использовать средства СУБД.
 

caballero

Новичок
При том, что caballero огрызается на каждое замечание в свой адрес как шавка, не могу не согласиться с ним
я огрызаюсь не на замечания а на наезды местной гопоты возомнившей себя програмистами
вон клоун caballero
пример наезда

дмает что это AR или Query Builder
потрудись перечитать тему. Об AR было упоминаие когда обсуждали код с Yii.

ORM даёт объекты, инкапсулируют в себе логику и позволяют гибко наращивать функционал.
это работает пока речь идет о сущностях. Какого рода объект будет если мы хотим выбрать продажи товара по месяцам?
 

caballero

Новичок
есть и mongodb решение
NoSQL решения красиво выглядят на бумаге но хреново на практике

Как раз делаю проект для англичан - какие то румыны заюзали CouchDB для системы логистики, облажались начали переклыдывать на MSSQL. Вторую половину пришлось мне доперекладывать
 

caballero

Новичок
Народ, просто выучите SQL - все вопросы отпадут сами собой
Использование ООП в програме никаким каком не требует всяких ORM. Максимум что есть смысл использовать - это Acive Record для автоматизации рутинной работы с бизнес сущностями.
 
Сверху