in Профессиональное

Оптимизация работы с Doctrine при помощи специализированных запросов

Если вы работаете над большим проектом на 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 ))

Write a Comment

Comment

*

    • )) не нравится формулировка? я указал источник, вроде бы все должны быть довольны