Я тут подумал, что лучше писать меньше, но чаще :). Хочется конечно написать большой материал, но времени постоянно не хватает.
Symfony2 и Doctrine2 Repository.
На форуме Игорь Негруца сделал мне замечание по поводу построения DQL запросов прямо в коде контроллеров. Есть такое дело и давно хотелось его поправить.
Для начала нужно в сущности явно указать привязку к репозиторию. В аннотациях это делается так:
/** * \Application\PortfolioBundle\Entity\Project * * @orm:Table(name="portfolio_projects") * @orm:Entity(repositoryClass="Application\PortfolioBundle\Repository\ProjectRepository") */ class Project |
Дальше генерируем наши кастомные репозитории:
/var/www/test/symfony2 $ ./app/console doctrine:generate:repositories PortfolioBundle Generating entity repositories for "PortfolioBundle" > OK generating Application\PortfolioBundle\Repository\ProjectRepository > SKIP no custom repository for Application\PortfolioBundle\Entity\Category |
Теперь попробую порефакторить существующий экшн. Например вот этот:
/** * Display links to prev/next projects * * @param Category $category * @param Project $project * @return \Symfony\Component\HttpFoundation\Response */ public function nearbyProjectsAction($category, $project) { $em = $this->get('doctrine.orm.entity_manager'); // get all projects from this category $query = $em->createQuery('SELECT p FROM PortfolioBundle:Project p JOIN p.categories c WHERE c.id = ?1 ORDER BY p.date DESC'); $query->setParameter(1, $category->getId()); $projects = $query->getResult(); // get next and previous projects from this category $i = 0; $previousProject = null; $nextProject = null; foreach ($projects as $p) { if ($project->getId() == $p->getId()) { $previousProject = isset($projects[$i-1]) ? $projects[$i-1] : null; $nextProject = isset($projects[$i+1]) ? $projects[$i+1] : null; break; } $i++; } return $this->render('PortfolioBundle:Project:nearby-projects.html.php', array( 'category' => $category, 'previousProject' => $previousProject, 'nextProject' => $nextProject, )); } |
Он выводит блок ссылок на предыдущий/следующий проект по отношению к текущему проекту.
Для выборки всех проектов из определенной категории создаю в репозитории метод getProjectsByCategory(). Получается следующее:
<?php namespace Application\PortfolioBundle\Repository; use Doctrine\ORM\EntityRepository; /** * ProjectRepository * * This class was generated by the Doctrine ORM. Add your own custom * repository methods below. */ class ProjectRepository extends EntityRepository { /** * Get all projects from this category * * @param \Application\PortfolioBundle\Entity\Category $category * @return array */ public function getProjectsByCategory(\Application\PortfolioBundle\Entity\Category $category) { $query = $this->getEntityManager() ->createQuery('SELECT p FROM PortfolioBundle:Project p JOIN p.categories c WHERE c.id = ?1 ORDER BY p.date DESC'); $query->setParameter(1, $category->getId()); return $query->getResult(); } } |
А в контроллере просто пишу:
// get all projects from this category $projects = $em->getRepository("PortfolioBundle:Project") ->getProjectsByCategory($category); |
Красота 🙂
Я воспринимал репозитории как дата мапперы, но Саша Стешеко подсказал, что это отдельный паттерн.
И офф. документация доктрины “Custom Repositories”.
Степа, объясни, зачем ты аттачишь картинки с какими-то нерелевантными телками к своим блогозаписям?
напрягают картинки или именно телки :)?
по мне так живей блог смотрится. если вообще без картинок, то скучно и серо.
убрал немного, а то перебор.
Кейт Хадсон оставил. она довольно релевантная )
А по мне все-таки это лишнее ) Да и жена проходя “случайно” мимо обращает внимание )))))
так это позитив ))
думаю был повод улыбнуться 🙂
Девушки в целом ни в коем случае не напрягают, просто они совершенно не в тему в данном контексте. Статья о программинге же.
Так обычно поступают на Хабре: размещают картинку что б человек интереса ради проследовал под кат.
Спасибо за понимание.
Для большей гибкости, я думаю, что между репозитариями и контроллером, должен быть еще один слой (службы, команды).
И контроллер ничего не должен знать о $em. Зачем контроллеру знать о ORM?
Просто логика модели “расплываеться” по контроллерам, и нет возможности подменить реализацию.
В целом правильно думаешь, но меня пока такой уровень абстракции вполне устраивает.
Спасибо за материал. Но я вот гуглю один вопрос и никак не могу найти ответ – возможно, поможете.
Задача достаточно простая: есть Category и Post (1:N)
Как на модели Category определить методы getOldPosts(), getSpamPosts() и т.д.?
Эти методы должны быть в репозитории, а не в доменной модели.
Спасибо, так и сделал.
Вот вопрос на стеке, если интересно: http://stackoverflow.com/questions/13922047/symfony2-doctrine2-how-to-implement-methods-on-entity-to-retrieve-related-ent
Степан, в репозиториях только возможны методы которые извлекают данные или также delete, insert, update?