Как правильно получить детей от сущности в Symfony? - PullRequest
3 голосов
/ 18 марта 2019

Я использую Symfony4. У меня есть структура базы данных с тремя таблицами, с двумя связующими отношениями «один ко многим». Например:

Дом -> Человек -> Собака

Каждый дом может содержать много людей, каждый человек может иметь много собак. (Моя база данных не совсем о домах и собаках, реальные имена таблиц были изменены, чтобы защитить невинных.) Используя сущности Doctrine, у меня, конечно, есть такие функции, как $house->getPersons() и $person->getDogs(). Но как правильно реализовать такую ​​функцию, как $house->getDogs()? Я могу предложить несколько решений, но ни одно из них не кажется мне «хорошим» решением:

  • Я мог бы просто добавить отношение один-ко-многим, связывающее Хауса с Собакой. Однако это противоречит одному из фундаментальных правил нормальной структуры базы данных: нет избыточных данных. Отношение house-> person-> dog уже выражено в базе данных, поэтому добавление другого отношения непосредственно из house-> dog не только избыточно, но потенциально может привести к ситуации, когда в одном доме живет человек собаки, но собака живет в другом (чего я не хочу).

  • Я мог бы просто сделать что-то вроде

    foreach($house->getPersons() as $person){
        $person->getDogs();
        // Do something with each dog
        ...
    }
    

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

  • Я мог бы использовать HouseRepository или построитель запросов в сущности House для выполнения пользовательского запроса. Насколько я понимаю, это не одобряется в Symfony. Как правило, мы не хотим никакого DQL или использования репозиториев в классах сущностей, правильно?

  • Я мог бы использовать HouseRepository из служб / контроллеров, чтобы выполнить пользовательский запрос. Это был бы простой способ, но кажется немного неэлегичным. Тем не менее, я чувствую, что это может быть «правильный» путь.

Подводя итог: у меня есть ощущение, что я должен быть в состоянии каким-то образом поместить это довольно простое двойное соединение в мою сущность, и мне не нужно переходить на уровень хранилища, чтобы получить эти данные. Чтобы было ясно, я не спрашиваю, как написать соединение DQL, я спрашиваю, где находится правильное место для его размещения, или есть какой-нибудь умный способ Symfony сделать это с помощью Criteria или чего-то подобного.

Ответы [ 2 ]

1 голос
/ 19 марта 2019

Если вы хотите вызвать $house->getDogs(), вы можете добавить метод в сущность вашего Дома.

public function getDogs()
{
    /** @var Doctrine\Common\Collections\ArrayCollection */
    $dogs = new ArrayCollection();

    if ($persons = $this->getPersons()) {
        foreach($persons as $person) {
            if ($d = $person->getDogs()) {
                $dogs->add($d);
            }
        }
    }

    return $dogs;
}
0 голосов
/ 20 марта 2019

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

Если предположить, что одна-ко-многим отношения собака-> человек, а вторая ко многим отношениям-человек-> собака, то в вашем HouseRepository используйте метод, который извлекает все соответствующие данные в одном запросе, включая всех связанных лиц и собак и собак (с).

class HouseRepository extends ServiceEntityRepository
{
    $qb = $this->createQueryBuilder('house')
        ->leftJoin('house.persons', 'persons')
        ->addSelect('persons')
        ->leftJoin('persons.dogs', 'dogs')
        ->addSelect('dogs')
        ->getQuery()->getResult();
}

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

...