Ответ 1
Предпосылка
Вы предложили 5 правильных/достойных решений, но я думаю, что все можно было сократить до двух случаев с некоторыми незначительными вариантами.
Мы знаем, что сортировка всегда O(NlogN)
, поэтому все решения теоретически имеют одинаковую производительность. Но так как это Доктрина, узкие места - это число SQL-запросов и методов гидратации (т.е. Преобразование данных из массива в экземпляр объекта).
Итак, вам нужно выбрать "лучший метод", в зависимости от того, когда вам нужны загружаемые объекты и что вы будете с ними делать.
Это мои "лучшие решения", и в общем случае я предпочитаю свое решение A)
A) DQL в службе загрузчика/хранилища
Аналогично
Ничего из вашего дела (как-то с 5, см. окончательную заметку). Альберто Фернандес указал вам в правильном направлении в комментарии.
Лучшее, когда
DQL (потенциально) самый быстрый метод, поскольку сортировка делегатов в СУБД, которая для этого оптимизирована. DQL также предоставляет полный контроль над тем, какие объекты будут извлекаться в одном запросе и режиме гидратации.
Недостатки
Невозможно (AFAIK) изменить запрос, созданный классами Proctrine Proxy, по конфигурации, поэтому вашему приложению необходимо использовать репозиторий и вызывать соответствующий метод каждый раз, когда вы загружаете объекты (или переопределяете их по умолчанию).
Пример
class MainEntityRepository extends EntityRepository
{
public function findSorted(array $conditions)
{
$qb = $this->createQueryBuilder('e')
->innerJoin('e.association', 'a')
->orderBy('a.value')
;
// if you always/frequently read 'a' entities uncomment this to load EAGER-ly
// $qb->select('e', 'a');
// If you just need data for display (e.g. in Twig only)
// return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
return $qb->getQuery()->getResult();
}
}
B) Желаемая загрузка и сортировка в PHP
Аналогично случаю
Случай 2), 3) и 4) - это то же самое, что и в другом месте. Моя версия - это общий случай, который применяется всякий раз, когда объекты извлекаются. Если вам нужно выбрать один из них, то я думаю, что решение 3) является наиболее удобным, так как не возиться с сущностью и всегда доступно, но используйте загрузку EAGER (читайте дальше).
Лучшее, когда
Если ассоциированные объекты всегда читаются, но невозможно (или удобно) добавлять службу, тогда все объекты должны загружаться EAGER-ly. Сортировка тогда может быть выполнена PHP, всякий раз, когда это имеет смысл для приложения: в прослушивателе событий, в контроллере, в шаблоне ветки... Если сущности всегда должны быть загружены, то наилучшим вариантом является прослушиватель событий.
Недостатки
Менее гибкий, чем DQL, и сортировка в PHP может быть медленной операцией, когда коллекция является большой. Кроме того, объекты должны быть гидратированы как объекты, которые медленны, и переполняют, если коллекция не используется для других целей. Остерегайтесь ленивой загрузки, так как это вызовет один запрос для каждого объекта.
Пример
MainEntity.orm.xml:
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping>
<entity name="MainEntity">
<id name="id" type="integer" />
<one-to-many field="collection" target-entity="LinkedEntity" fetch="EAGER" />
<entity-listeners>
<entity-listener class="MainEntityListener"/>
</entity-listeners>
</entity>
</doctrine-mapping>
MainEntity.php:
class MainEntityListener
{
private $id;
private $collection;
public function __construct()
{
$this->collection = new ArrayCollection();
}
// this works only with Doctrine 2.5+, in previous version association where not loaded on event
public function postLoad(array $conditions)
{
/*
* From your example 1)
* Remember that $this->collection is an ArryCollection when constructor is called,
* but a PersistentCollection when are loaded from DB. Don't recreate the instance!
*/
// Get the values for the ArrayCollection and sort it using the function
$values = $this->collection->getValues();
// sort as you like
asort($values);
// Clear the current collection values and reintroduce in new order.
$collection->clear();
foreach ($values as $key => $item) {
$collection->set($key, $item);
}
}
}
Заключительные замечания
- Я не буду использовать случай 1) как есть, так как это очень сложно и ввести наследование, которое уменьшает инкапсуляцию. Кроме того, я думаю, что он имеет такую же сложность и производительность моего примера.
- Случай 5) не обязательно плохой. Если "служба" - это репозиторий приложений, и она использует DQL для сортировки, то это мой первый лучший случай. Если это настраиваемая услуга только для сортировки коллекции, то я думаю, что это не очень хорошее решение.
- Все коды, которые я написал здесь, не готовы для "copy-paste", поскольку моя цель состояла в том, чтобы показать мою точку зрения. Надеюсь, что это будет хорошей отправной точкой.
Отказ
Это "мои" лучшие решения, как я это делаю в своих работах. Надежда поможет вам и другим.