SonataAdminBundle: фильтры
Лично для меня - фильтры один из самых краеугольных камней, ибо это любимое требование заказчика, и очень часто у этого заказчика очень нестандартные запросы к фильтрам.
Итак, соната предоставляет несколько возможностей для внедрения фильтрации данных.
- Неявные фильтры. Могут понадобится в случае, когда данные нужно фильтровать для пользователя определенным образом, но сам пользователь об этом знать не должен. Как пример: пользователь должен видеть только свои посты.
Стандартное поведение админ классов можно легко модифицировать с помощью AdminExtension’ов. AdminExtension - это сервис, который админ класс запускает после того как сделает свою основную работу по конфигурации. AdminExtension’ов может быть несколько. Они включают в себя основные методы админ класса, такие как configureFormFields, configureDatagridFilters и др. Мы рассмотрим метод configureQuery, с помощью которого можно изменить стандартный запрос, который выполняется, когда мы просматриваем список сущностей. В моем случае я буду выводить только те сущности, в которых поле parent является пустым, об иных сущностях пользователю знать не стоит
<?php
namespace Mtools\RatecardBundle\Admin;
use Sonata\AdminBundle\Admin\AdminExtension;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
class RatecardAdminExtension extends AdminExtension
{
public function configureQuery(AdminInterface $admin, ProxyQueryInterface $query, $context = 'list')
{
$query->getQueryBuilder()->where('o.parent IS NULL');
}
}
Конечно, наш класс унаследован от AdminExtension
Определение сервиса следующее:
mtools.ratecard.admin.extension:
id: mtools.ratecard.admin.extension
class: Mtools\RatecardBundle\Admin\RatecardAdminExtension
tags:
- { name: sonata.admin.extension, target: mtools.ratecard.admin }
target в секции tag - это id сервиса админ класса, к которому будет присоединен AdminExtension
С помощью AdminExtension'ов очень удобно джойнить нужные сущности, чтобы потом фильтровать по этим полям внутри админ класса (это второй способ, рассмотрен ниже)
- Часто возникает необходимость реализовать нестандартную логику фильтрации по определнному полю, а времени, чтобы оформлять данный фильтр как отдельный сервис нет, да и такой фильтр может встреаться только в одном месте. Для таких случаев SonataAdminBundle предоставляет тип фильтра doctrine_orm_callback. Здесь все просто как 2 копейки:
<?php
...
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('codes', 'doctrine_orm_callback', array(
'label' => 'Код',
'callback' => array($this, 'getCodesFilter'),
'field_type' => 'genemu_jquerychosen',
'field_options' => array(
'class' => 'Mtools\ClientBundle\Entity\Client',
'widget' => 'entity',
'multiple' => false,
)
);
}
public function getCodesFilter($queryBuilder, $alias, $field, $value)
{
if (null === $value['value']) {
return;
}
$queryBuilder->leftJoin(sprintf('%s.codes', $alias), 'c');
$queryBuilder->andWhere('c.code = :code');
$queryBuilder->setParameter('code', $value['value']);
}
...
Сам фильтр мы так же, как и обычные, описываем в методе configureDatagridFilters, с той разницей, что тип фильтра указываем doctrine_orm_callback, а в опциях фильтра обязательно указываем callback - метод, который будет вызываться для формирования QueryBuilder’a соответственно нашему фильтру. Иначе говоря, в этот метод передается сам QueryBuilder, $alias - основной алиас сущности, $field - какое то мистическая переменная, я не нашел ей применения, и $value - собственно значение из фильтра, которое ввел пользователь.
Внутри функции callback’a в данном случае добавляется джоин (что, как я и говорил раньше, можно вынести в AdminExtension, и даже нужно, если этих джоинов множество), ну, и само условие для фильтрации.
Еще любопытные опции, на поиск которых я потратил некоторое время:
- field_type - тип поля, которое будет отображаться для ввода пользователем значения
- field_options - опции этого поля, которые обычно идут третьим аргументом в FormMapper'e метода configureFormFields
Значение фильтров по-умолчанию
Однажды столкнулся с задачей, что необходимо было явно указать в фильтрах значение по-умолчанию, как если бы пользователь сам его выбрал.
<?php
...
public function getFilterParameters()
{
$parameters = parent::getFilterParameters();
$filterUsers = ...;
if (!isset($parameters['managers']['value']) || !sizeof($parameters['managers']['value'])) {
$parameters['managers']['value'] = $filterUsers;
$this->datagridValues['managers']['value'] = $filterUsers;
}
return $parameters;
}
Ключ ко всему - метод getFilterParameters, который соната вызывает, чтобы составить список сущностей для listAction. Данный метод заполняет свойство datagridValues соответствующими значениями. В данном случае предполагается, что $filterUsers - это массив с id’шками пользователей.