Pull to refresh

Comments 27

Вопрос в том, каким способом лучше поднимать связанные данные, всё сразу или с отложенной загрузкой?

Я думаю тут должен быть во главе вопрос «зачем», а не «как». Использовать или нет ленивую загрузку (lazy loading) надо решать в каждом конкретном случае.
Например Doctrine по умолчанию использует ленивую загрузку, но позволяет загрузить все сразу когда это необходимо.

при чем последние методы работают одинаково, как для одного объекта, так и для коллекции объектов (похоже на Composite).

Не уловил мысли. Примерно так:
$post = $repository->find(1);
$comments = $post->getComments(); // комментарии к посту с id 1

$posts = $repository->findBy(['author' => 'Вася']);
$comments = $posts->getComments(); // комментарии к постам Васи

Это было бы более чем интересной функциональностью, но я подобного нигде не встречал.
именно это я имел ввиду, наверно так можно сделать если модель и связанная с ней коллекция будут иметь общего предка
ActiveRecord — умеет.
Для PHP смотри реализации в Yii и Kohana, или в составе Limb
UFO just landed and posted this here
Зачем же пускаться в крайности?) Используя ORM'ку, никто не мешает неоптимальные/высоконагруженные части писать/потом переписать на raw sql. Таким образом, можно и соптимизировать в скорости написания/поддержки, и не шибко потерять в производительности.
Ну да, это вечная дилемма разработчика — скорость разработки или скорость работы скрипта, но хочется найти какую-нибудь серебряную пулю, которая бы подходила большинству проектов. А нет куска кода на PHP с реальным и удачным DataMapper-ом?
Да отличный сайт и статьи, спасибо!
Интересно почему во многих примерах про Data Mapper используют явные методы getName(), setName(), а не __get() и __set()?
Еще вопрос, если использовать DM, то где должен располагаться код, реализующий сохранение связей? Вопрос с подковыркой ), к примеру, есть модельTag, теги могут одинаковым способом (меняется только название таблицы связей или fk) привязываться к другим моделям Post, QA, Book. С точки зрения понятности кода, привычнее
$post = new Post;
$post->addTags( [1,2,3] );

$qa = new QA;
$qa->addTags( [1,2,3] );

но с точки зрения дублирования кода, лучше
$post = new Post;
$qa = new QA;
Tag::addTo( $post , [1,2,3]);
Tag::addTo( $qa , [1,2,3]);

Не бейте сильно, хочется разобраться раз и навсегда )).
UFO just landed and posted this here
спасибо за развернутый ответ, плюсануть не могу, к сожалению (
Код, реализующий сохранение связей, должен быть в методах сущности, которая «владеет» этими связями (не в подчинённых объектах).


подумав )), всё таки не могу согласиться с вами до конца. Вариант, когда код привязки располагается в той модели которую привязывают ( и не только модель, но соответствующие шаблоны и контроллеры) имеет очевидные преимущества:
1. Одна и та же модель, как правило привязывается ко всем другим одинаково (по моей статистике), т.е. картинки привязываются одним способом ко всем моделям, теги могут привязываться вторым способом, но всё равно одинаковым для всех моделей.
2. Дисциплинированность разработчиков: описав один раз привязку тегов — можно быть уверенным, что они будут привязываться единообразно ко всем моделям, и таблицы связей с полями будут единообразные.
3. Некоторые модели могут привязываться к десятку других моделей абсолютно одинаково, и в этом случае единственность кода, где это делается, имеет первостепенное значение с точки зрения вероятности ошибок и быстроты внесения правок.
4. Гибкость подключения / отключения новых связей, например, не было сначала тегов, потом появилась в них необходимость — добавили модель и контроллер с кодом привязки и всё — можно привязывать теги к любой модели просто указав у неё наличие связи с тегами ( ну и создав таблицу связей)
UFO just landed and posted this here
По хорошему должно быть что-то вроде
$post = new Post;
$qa = new QA;
$tag1 = new Tag(1);
$tag2 = new Tag(2);
$tag3 = new Tag(3);

$post->addTag($tag1);
$post->addTag($tag2);
$post->addTag($tag3);

$qa->addTag($tag1);
$qa->addTag($tag2);
$qa->addTag($tag3);


Как вариант сама связь

$post->addTags([$tag1, $tag2, $tag3]);
$qa->addTags([$tag1, $tag2, $tag3]);


А с точки зрения дублирования кода можно создать трэйт Taggable с методом addTag/addTags и т. п.

Использовать new или фабрику — дело вкуса и требований к тестируемости кода. Я обычно использую new, поскольку потому как юнит тестами контроллеры (а по моему убеждению инстанцы моделей должны создаваться только в них или в вызывающем их коде)толком не покрыть всё равно, а функциональным без разницы.
Расскажите за что минус, всем интересно, что ни так?
Хорошая подборка стандартных заблуждений. Начиная с каких-то наивных, про findWithComments, findWithCategoriesAndTags…

С незапамятных времён в любом MySQL есть оптимизации специально для формирования вот таких запросов:

select *
from adv_db_stat
where
   (variant_id = %(A)s or -1 = %(A)s)
   and
   (campaign_id = %(B)s or -1 = %(B)s);

Тут мы подставляем два параметра А и B. Если A==-1, то подходит любой A. Аналогично для B. Причём, если вы передадите A=1, B=-1, то запрос:

select *
from adv_db_stat
where
   (variant_id = 1 or -1 = 1)
   and
   (campaign_id = -1 or -1 = -1);

Будет соптимизирован до

select *
from adv_db_stat
where
   variant_id = 1;


То есть ваша findWithComments — это findWithCategoriesAndTags(comment=55, tag=-1).

Ну и так далее.

Я бы советовал всё же не ругать SQL, а пользоваться им.
наверно мы не поняли друг друга, find() — загрузить просто пост без связей, findWithComments — значит загрузить пост с его комментариями
а как ваш пример с этим связан?
Да. С этим не связан. Но тогда ваш пример ещё более искусственный.

Во-первых. Эти две функции возвращают разные типы данных. В этом случае, наличие двух функций весьма оправдано. Многие зыки программирования будут вас всячески подталкивать к тому, что это разные функции. В этом нет ничего плохого.

Во-вторых. Как вы представляете select, который вытащит и пост и все комментарии к нему? Это либо два разных селекта (и это хорошо), либо один безумный селект «все комментарии этого поста и сам пост вместе с каждым комментарием» (так делать не надо).

Одним словом, вас не устраивает то, что SQL (да и многие другие языки) для получения разных типов данных предлагает использовать разные функции? Вы хотите, чтобы одна функция (один запрос) могла возвращать разные структуры данных? И как их потом обрабатывать? Писать следующую функцию, которая может обработать что угодно? Вот теперь я вас точно не понимаю.
Вовсе не обязательно они должны возвращать разные типы данных. просто $result->comments может быть неинициализрирована или инициализированным специальным значением.

SELECT * FROM post LEFT JOIN comment WHERE post.id = comment.post_id

Что безумного?
То что post.* будет повторяться столько раз, сколько комментариев. Если комментариев 1000, то 999 раз всё содержимое поста будет зря гонять трафик и засирать память, процессор, и кэш mysql.
при чем последние методы работают одинаково, как для одного объекта, так и для коллекции объектов

Какое-то у меня недоверие к такому подходу.И если ленивая загрузка в getComment() ещё оправдана для одного объекта, то для коллекции хотя бы метод должен называться по другому, например loadComments(). По-моему выражение $comments = $posts->getComment(); очень неочевидное. Что оно возвращает? Меняется ли $posts: Имхо, лучше $posts->loadComments(); или $posts->fillComments();
согласен с loadComments(), подразумевается, что он загружает данные в коллекцию или в один объект, т.е. и там и там должен быть loadComments().
По хорошему этот метод не должен быть в самом объекте. Да и в коллекции тоже. Если взять пример из первого коммента zloyusr, то я делал бы как-то так:

$post = $repository->find(1);
$comments = $post->geComments(); // lazy load

$post = $repository->find(2);
$repository->loadComments([$post]); // direct load

$posts = $repository->findBy(['author' => 'Вася']);
$repository->loadComments($posts); // direct load

По хорошему этот метод не должен быть в самом объекте.

Его и не будет в объекте модели. Он по сути должен быть в объекте коллекции моделей. Но реализация такого будет я думаю более чем нетривиальна, если делать ее универсальной для всех коллекций.

Ваш вариант с отдельными методами в репозиториях значительно проще в реализации, и достаточно удобен на мой взгляд.
При чем это физически один метод, но сам должен определять в каком контексте он вызван.
Sign up to leave a comment.

Articles