Если вы работаете над большим проектом на symfony + Doctrine и у вас много моделей и сложных запросов, вам обязательно потребуется способ, как хранить и использовать эти запросы, не теряя преимуществ ООП и более прямым способом, нежели традиционные способы addNamedQuery()
и createNamedQuery()
. Ниже приводится такой способ.
Идея состоит в следующем: мы создаем query-классы для текущей модели. Таким образом, мы можем создавать необходимые методы для реализации бизнес-логики. В теории звучит очень просто. Даввайте попробуем разобрать конкретный пример. Рассмотрим для начала YAML-модель:
BlogAuthor: columns: id: type: integer(4) primary: true autoincrement: true name: type: string(255) relations: Post: type: one class: BlogPost local: id foreign: author_id BlogPost: columns: id: type: integer(4) primary: true autoincrement: true author_id: type: integer(4) notnull: true title: type: string(255) content: type: string(65535) relations: Author: type: one class: BlogAuthor local: author_id foreign: id Comments: type: many class: BlogComment local: id foreign: post_id BlogComment: columns: id: type: integer(4) primary: true autoincrement: true post_id: type: integer(4) notnull: true author: type: string(255) content: type: string(5000) relations: Post: type: one class: BlogPost local: post_id foreign: id
Теперь давайте представим Query-класс, отвечающий таблице BlogPost:
<?php class BlogPostQuery extends Doctrine_Query { static public function create($conn = null, $class = null) { return parent::create($conn, 'BlogPostQuery') ->from('BlogPost p'); } public function addPosts($fields = 'p.*') { return $this->addSelect('p.*'); } public function addComments($fields = 'c.*') { return $this ->addSelect($fields) ->leftJoin('p.Comments c') ->addGroupBy('c.id'); } public function addAuthors($fields = 'a.*') { return $this ->addSelect($fields) ->leftJoin('p.Author a') ->addGroupBy('a.id'); } public function addCommentsCount($alias = 'nb_comments') { return $this ->addSelect(sprintf('COUNT(c.id) as %s', $alias)) ->addGroupBy('c.id'); } public function filterByAuthorName($authorName) { return $this ->andWhere('a.name = ?', $authorName); } }
И как же мы можем использовать этот Query-объект? Вот несколько use-cases:
// Получаем все посты $posts = BlogPostQuery::create() ->addPosts() ->fetchArray(); // Получаем все посты с комментариями $posts = BlogPostQuery::create() ->addPosts() ->addComments() ->fetchArray(); // Получаем все посты с комментариями и числом комментариев для каждого поста $posts = BlogPostQuery::create() ->addPosts() ->addComments() ->addCommentsCount('yataa') ->fetchArray(); // Получаем все посты автора chuck и их комментарии $posts = BlogPostQuery::create() ->addAuthors() ->addPosts() ->addComments() ->filterByAuthorName('chuck') ->fetchArray(); // и т.д...
Конечно же наш пример слишком прост, чтобы понять все возможные преимущества такого подхода, но при работе с десятками i18n объектов этот подход поможет расчистить ваши классы модели, контроллеры и оптимизировать вашу работу.
Некоторые уточнения
Некоторые пользователи давали негативный фидбэк на описанную выше технику, поясняя свою позицию тем, что она будет поощрять использование пользовательских query-объектов напрямую в контроллерах. Это совсем не так, поскольку запросы должны быть использованы только в рамках модели, например, в классе BlogPostTable:
<?php class BlogPostTable extends Doctrine_Table { static public function getPostsWithCommentsByAuthor($authorName) { return BlogPostQuery::create() ->addPosts() ->addComments() ->filterByAuthorName($authorName) ->fetchArray() ; } }
И в контроллере:
class blogActions extends sfActions { public function executeListByAuthor(sfWebRequest $request) { $this->posts = BlogPostTable::getPostsWithCommentsByAuthor( $request->getParameter('author') ); } }
По мотивам http://prendreuncafe.com/blog/post/Optimize-your-Doctrine-Workflow-with-Specialized-Queries. Have fun ))
«По мотивам»… скромненько 😉
)) не нравится формулировка? я указал источник, вроде бы все должны быть довольны
Спасибо, хорошая идея!