Symfony2 - Доступ к функциям репозитория в Entity - PullRequest
15 голосов
/ 08 февраля 2012

Допустим, у меня есть две таблицы в моей базе данных: кролики и морковь.У кроликов может быть 0 или несколько морковок, а морковь принадлежит одному кролику.Это отношение 1, n между этими двумя таблицами.

У меня есть две сущности: кролик и морковь.

У меня есть массив кроликов, переданных в моем шаблоне, и я хотел бы получить конкретныеморковь от каждого кролика и отображение их: допустим, я хочу получить 10 более дорогих морковок (цены на морковь будут храниться в таблице морковей) от каждого кролика в массиве.

Что-то вроде:

{% for rabbit in rabbits %}
    {% for carrot in rabbit.getMoreExpensiveCarrots %}

        {{ carrot.price }}

    {% endfor %}
{% endfor %}

Я использую класс репозитория, но если я создам функцию getMoreExорогоCarrots ($ rabbit) в классе репозитория кролика, я не смог бы получить доступ к этой функции из класса сущностей, как это яwant:

$ rabbit-> getMoreExaciousCarrots ()

Я думал, что способ сделать это - создать getMoreExoyCarrots () в объекте rabbit:

// Entity rabbit
class Rabbit
{
    public function getMoreExpensiveCarrots()
    {
        // Access repository functions like getMoreExpensiveCarrots( $rabbit )
        // But how can I do such thing ? Isn't that bad practise ?
        return $carrots;
    }         
}

Я думал, что смогу сделать это тоже:

    // Entity rabbit
    class Rabbit
    {
        public function getMoreExpensiveCarrots()
        {
            $this->getCarrots();

            // Then try here to sort the carrots by their price, using php            

            return $carrots;
        }         
    }

Вот мой контроллер:

    public function indexAction()
    {
        $em = $this->getDoctrine()->getEntityManager();

        $rabbits = $em->getRepository('AppNameBundle:Rabbit')->getSomeRabbits();

        return $this->render('AppNameBundle:Home:index.html.twig', 
                array(
                    "rabbits"=>$rabbits
        ));
    }

Как лучше всего вызывать функцию getMoreExoyCarrots из eacч кролик в шаблоне?

спасибо!

Ответы [ 3 ]

8 голосов
/ 12 февраля 2012

Вернуться к основам.Забудьте о репозитории против сервиса и просто сосредоточьтесь на кроликах и моркови.

class Rabbit
{
/** @ORM\OneToMany(targetEntity="Carrot", mappedBy="rabbit" */
protected $carrots;

public function getCarrots() { return $this->carrots; }

public function getMoreExpensiveCarrots()
{
    // Get all carrots
    $carrots = $this->getCarrots()->toArray();

    // Sort by price
    // http://php.net/manual/en/function.usort.php
    usort(&$carrots,array($this,'compareTwoCarrotsByPrice'));

    // Now slice off the top 10 carrots (slice - carrots, hah, kindo of funny
    $carrots = array_slice($carrots, 0, 10);

    // And return the sorted carrots
    return $carrots;
}
public function compareTwoCarrotsByPrice($carrot1,$carrot2)
{
    if ($carrot1->getPrice() > $carrot2->getPrice()) return -1;
    if ($carrot1->getPrice() < $carrot2->getPrice()) return  1;
    return 0;
}
}

Удалите всю морковь из вашего запроса и просто получите список кроликов.
Ваш оригинальный шаблон теперь будет работать точно так, как ожидалось:

{% for rabbit in rabbits %}
    {% for carrot in rabbit.getMoreExpensiveCarrots %}

        {{ carrot.price }}

    {% endfor %}
{% endfor %}

Единственным недостатком здесь является то, что для каждого кролика Doctrine автоматически генерирует отдельный запрос для всех морковок.В какой-то момент производительность будет затруднена.Когда вы дойдете до этой точки, вы сможете вернуться к исходному запросу и посмотреть, как более эффективно вводить морковь.Но сначала приведите в действие вышеприведенный класс, так как я думаю, что это может быть вашей точкой блокировки.

8 голосов
/ 08 февраля 2012

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

Возможное решение здесь - использовать объект службы (RabbitService), который содержитgetMoreExpensiveCarrots метод.Службе разрешено знать о менеджере сущностей и репозиториях, поэтому именно здесь вы выполняете любые сложные операции.

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

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

7 голосов
/ 12 февраля 2012

Позвольте мне попытаться объяснить это. Доктрина 2 ORM требует небольшого переосмысления. В настоящее время у вас есть мышление, что кролик должен иметь возможность запрашивать морковь всякий раз, когда он нуждается в них. Вам нужно изменить это мышление.

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

В этом случае вам нужна 10 самых дорогих морковок, поэтому ваш процесс должен это знать. Итак, теперь вы вернетесь к ответу @Arms и создадите сервис RabbitManager с методом loadRabbitsWithExoyCarrots. Возвращаемым значением будет массив кроликов с уже заполненной морковью.

Обновлено это для учета комментариев.

  1. Хранилище Doctrine 2 против службы Symfony 2

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

Служба Symfony 2 скрывает все детали того, как организованы ваши кролики и морковь. Это более общая концепция, и она может иметь дело с более сложными запросами, затрагивающими несколько объектов. Это основной строительный блок S2, и вам действительно нужно освоиться с ним.

Для вашего текущего требования подойдет любой подход.

  1. Вопрос о параметре моркови

Все еще не уверен, что именно ты имеешь в виду. Возможно, вы можете опубликовать пример запроса? Имейте в виду, что вы, безусловно, можете добавить метод getExoyCarrots () к вашему объекту rabbit. Метод начал бы вызывать getCarrots (). Возвращенная морковь была бы уже загружена первоначальным запросом. Ваш метод будет фильтровать и сортировать.

И имейте в виду, что мы пытаемся разобраться со случаем, когда к кролику могут быть присоединены сотни или тысячи морковок. Поэтому мы стараемся не загружать всю морковь только из соображений производительности. Это может быть легче начать, не загружая морковь вообще в начальном запросе. Вместо этого при первом вызове вашего rabbit-> getCarrots () Doctrine 2 автоматически выдаст запрос и загрузит всю морковь для этого кролика. И затем снова ваш метод getExорогоCarrots будет фильтровать и сортировать по мере необходимости.

Надеюсь, это поможет. Возможно, просто запутал вас еще больше.

...