2013-02-13 4 views
2

У меня есть отношения OneToMany, в которых одна футбольная команда имеет много игроков. Я хочу перечислить все футбольные команды и показать имя капитана для каждой команды.Symfony2 + Doctrine - Фильтрация

Каждый игрок организация имеет внешний ключ (team_id) и поле «капитан», который установлен на 0 или 1. Я в настоящее время работает следующий запрос:

$teams = $this 
      ->getDoctrine() 
      ->getRepository('FootballWebsiteBundle:Team') 
       ->createQueryBuilder('t') 
      ->setFirstResult(($pageNumber * $resultPerPage) - $resultPerPage) 
      ->setMaxResults($resultPerPage) 
      ->add('where','t.deleted = 0') 
      ->add('orderBy', 't.name DESC') 
      ->getQuery()->getResult(); 

Затем, когда я петля через каждый команда в веточке я бегу team.getTeamCaptain() GetName(), который является фильтром внутри моей сущности команды:.

public function getTeamCaptain() { 
    $them = $this->players->filter(function($p) { 
     return $p->getCaptain() == 1; 
    }); 

    return $them->first(); 
} 

есть ли лучший способ запустить этот запрос?

ответ

4

Прежде всего, вы можете захотеть fetch-join игроков каждой полученной команды, чтобы они не ленились загрузиться во время рендеринга шаблона. Вот DQL:

SELECT 
    t, p 
FROM 
    FootballWebsiteBundle:Team t 
LEFT JOIN 
    t.players p 
WHERE 
    t.deleted = 0 
ORDER BY 
    t.name DESC 

который может быть построен с следующих модифицирующих запросов API вызовов:

$teamsQuery = $this 
     ->getDoctrine() 
     ->getRepository('FootballWebsiteBundle:Team') 
     ->createQueryBuilder('t') 
     ->addSelect('p') 
     ->leftJoin('t.players', 'p') 
     ->add('where','t.deleted = 0') 
     ->add('orderBy', 't.name DESC') 
     ->getQuery() 

Затем обернуть этот запрос в объекта (с setMaxResults и setFirstResultcannot be trusted when fetch-joining):

$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($teamsQuery, true); 

$teamsQuery 
    ->setFirstResult(($pageNumber * $resultPerPage) - $resultPerPage) 
    ->setMaxResults($resultPerPage) 

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

foreach ($paginator as $team) { 
    echo $team->getTeamCaptain() . "\n"; 
} 

Вы также можете получить некоторую дополнительную производительность в методе getTeamCaptain, используя Selectable API:

public function getTeamCaptain() { 
    $criteria = new \Doctrine\Common\Collections\Criteria(); 

    $criteria->andWhere($criteria->expr()->eq('captain', 1)); 

    return $this->players->matching($criteria)->first(); 
} 

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

+0

Кому-то, кто озадачен этим ответом: последний блок отвечает на заданный вопрос, а остальные говорят об улучшении ситуации, которую нужно было улучшить, но на самом деле ее не спрашивали. – Lighthart

+0

@ Lighthart: нет, пользователь попросил способ оптимизации логики. Его первоначальная версия вызвала запросы N * M + 1 из-за ленивой инициализации коллекции 'Team # players', а затем отдельных игроков в этих коллекциях, чтобы проверить, является ли' player.captain == 1'. Оптимизация только 'getTeamCaptain' по-прежнему вызывает запросы N + 1 для инициализации коллекций. Fetch-join в сборке 'Team # players' уменьшает всю эту логику до' 1' одного запроса. Так что оптимизировать только «getTeamCaptain» здесь было бы недостаточно. – Ocramius

+0

Paginator не имеет к этому никакого отношения. – Lighthart