From b7bc003ccb893a1c2d46a06f0107cfab6bd052c0 Mon Sep 17 00:00:00 2001 From: Matthieu Crinquand Date: Tue, 29 Mar 2016 11:47:06 +0200 Subject: [PATCH 1/4] allow to switch content language on list and filter by language --- .../Gedmo/TranslatableAdminExtension.php | 60 ++++++++++++++++++- EventListener/LocaleSwitcherListener.php | 3 + Resources/config/listener.xml | 1 + Resources/public/css/sonata-translation.css | 43 +++++++++---- Resources/public/less/sonata-translation.less | 2 +- .../Block/block_locale_switcher.html.twig | 11 +++- 6 files changed, 105 insertions(+), 15 deletions(-) diff --git a/Admin/Extension/Gedmo/TranslatableAdminExtension.php b/Admin/Extension/Gedmo/TranslatableAdminExtension.php index bef41fdb..8859fe2f 100644 --- a/Admin/Extension/Gedmo/TranslatableAdminExtension.php +++ b/Admin/Extension/Gedmo/TranslatableAdminExtension.php @@ -13,6 +13,8 @@ use Gedmo\Translatable\TranslatableListener; use Sonata\AdminBundle\Admin\AdminInterface; +use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; +use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\TranslationBundle\Admin\Extension\AbstractTranslatableAdminExtension; use Sonata\TranslationBundle\Checker\TranslatableChecker; @@ -40,13 +42,69 @@ public function alterObject(AdminInterface $admin, $object) if ($this->getTranslatableChecker()->isTranslatable($object)) { $translatableListener = $this->getTranslatableListener($admin); $translatableListener->setTranslatableLocale($this->getTranslatableLocale($admin)); - $translatableListener->setTranslationFallback(''); + $translatableListener->setTranslationFallback(false); $this->getContainer($admin)->get('doctrine')->getManager()->refresh($object); $object->setLocale($this->getTranslatableLocale($admin)); } } + /** + * {@inheritdoc} + */ + public function configureQuery(AdminInterface $admin, ProxyQueryInterface $query, $context = 'list') + { + $this->getTranslatableListener($admin)->setTranslatableLocale($this->getTranslatableLocale($admin)); + $this->getTranslatableListener($admin)->setTranslationFallback(false); + } + + /** + * Search on normal field and on translated field, use with a + * doctrine_orm_callback filter type. + * + * @param ProxyQuery $queryBuilder + * @param string $alias + * @param string $field + * @param string $value + * + * @return bool|null + */ + public static function translationFieldFilter(ProxyQuery $queryBuilder, $alias, $field, $value) + { + if (!$value['value']) { + return; + } + + // verify if the join is not already done + $aliasAlreadyExists = false; + foreach ($queryBuilder->getDQLParts()['join'] as $joins) { + foreach ($joins as $join) { + if ($join->getAlias() === 't') { + $aliasAlreadyExists = true; + break 2; + } + } + } + + if (!$aliasAlreadyExists) { + $queryBuilder->leftJoin($alias.'.translations', 't'); + } + + // search on translation OR on normal field + $queryBuilder->andWhere($queryBuilder->expr()->orX( + $queryBuilder->expr()->andX( + $queryBuilder->expr()->eq('t.field', $queryBuilder->expr()->literal($field)), + $queryBuilder->expr()->like('t.content', $queryBuilder->expr()->literal('%'.$value['value'].'%')) + ), + $queryBuilder->expr()->like( + sprintf('%s.%s', $alias, $field), + $queryBuilder->expr()->literal('%'.$value['value'].'%') + ) + )); + + return true; + } + /** * @param AdminInterface $admin Deprecated, set TranslatableListener in the constructor instead * diff --git a/EventListener/LocaleSwitcherListener.php b/EventListener/LocaleSwitcherListener.php index 9ce503d7..57e24391 100644 --- a/EventListener/LocaleSwitcherListener.php +++ b/EventListener/LocaleSwitcherListener.php @@ -28,6 +28,9 @@ public function onBlock(BlockEvent $event, $eventName) if ($eventName == 'sonata.block.event.sonata.admin.show.top') { $settings['locale_switcher_route'] = 'show'; } + if ($eventName == 'sonata.block.event.sonata.admin.list.table.top') { + $settings['locale_switcher_route'] = 'list'; + } $block = new Block(); $block->setSettings($settings); diff --git a/Resources/config/listener.xml b/Resources/config/listener.xml index 05b239e3..c614064b 100644 --- a/Resources/config/listener.xml +++ b/Resources/config/listener.xml @@ -7,6 +7,7 @@ + diff --git a/Resources/public/css/sonata-translation.css b/Resources/public/css/sonata-translation.css index 4a100b4d..68de41ab 100644 --- a/Resources/public/css/sonata-translation.css +++ b/Resources/public/css/sonata-translation.css @@ -2,7 +2,8 @@ * SonataTranslation: adds translations to your forms */ .sonata-bc .sonata-ba-form .locale_switcher, -.sonata-bc .sonata-ba-show .locale_switcher { +.sonata-bc .sonata-ba-show .locale_switcher, +.sonata-bc .box-primary .locale_switcher { padding: 1px 0 0 0; text-align: right; margin-right: 15px; @@ -10,67 +11,87 @@ float: right; } .sonata-bc .sonata-ba-form .locale_switcher a, -.sonata-bc .sonata-ba-show .locale_switcher a { +.sonata-bc .sonata-ba-show .locale_switcher a, +.sonata-bc .box-primary .locale_switcher a { margin-right: 0; padding: 2px; opacity: 0.5; } .sonata-bc .sonata-ba-form .locale_switcher a.active, -.sonata-bc .sonata-ba-show .locale_switcher a.active { +.sonata-bc .sonata-ba-show .locale_switcher a.active, +.sonata-bc .box-primary .locale_switcher a.active { opacity: 1; } .sonata-bc .sonata-ba-form .locale_switcher a:hover, -.sonata-bc .sonata-ba-show .locale_switcher a:hover { +.sonata-bc .sonata-ba-show .locale_switcher a:hover, +.sonata-bc .box-primary .locale_switcher a:hover { opacity: 1; } .sonata-bc .sonata-ba-form label.wysiwyg.locale, .sonata-bc .sonata-ba-show label.wysiwyg.locale, +.sonata-bc .box-primary label.wysiwyg.locale, .sonata-bc .sonata-ba-form label.checkbox.locale, .sonata-bc .sonata-ba-show label.checkbox.locale, +.sonata-bc .box-primary label.checkbox.locale, .sonata-bc .sonata-ba-form input.locale, .sonata-bc .sonata-ba-show input.locale, +.sonata-bc .box-primary input.locale, .sonata-bc .sonata-ba-form textarea.locale, .sonata-bc .sonata-ba-show textarea.locale, +.sonata-bc .box-primary textarea.locale, .sonata-bc .sonata-ba-form select.locale, .sonata-bc .sonata-ba-show select.locale, +.sonata-bc .box-primary select.locale, .sonata-bc .sonata-ba-form li.locale a, -.sonata-bc .sonata-ba-show li.locale a { +.sonata-bc .sonata-ba-show li.locale a, +.sonata-bc .box-primary li.locale a { background-repeat: no-repeat; } .sonata-bc .sonata-ba-form input[type="text"].locale, .sonata-bc .sonata-ba-show input[type="text"].locale, +.sonata-bc .box-primary input[type="text"].locale, .sonata-bc .sonata-ba-form select.locale, -.sonata-bc .sonata-ba-show select.locale { +.sonata-bc .sonata-ba-show select.locale, +.sonata-bc .box-primary select.locale { padding-left: 35px !important; background-position: 5px center; } .sonata-bc .sonata-ba-form textarea.locale, -.sonata-bc .sonata-ba-show textarea.locale { +.sonata-bc .sonata-ba-show textarea.locale, +.sonata-bc .box-primary textarea.locale { padding-left: 35px; background-position: 5px 5px; } .sonata-bc .sonata-ba-form label.wysiwyg.locale, .sonata-bc .sonata-ba-show label.wysiwyg.locale, +.sonata-bc .box-primary label.wysiwyg.locale, .sonata-bc .sonata-ba-form label.checkbox.locale, -.sonata-bc .sonata-ba-show label.checkbox.locale { +.sonata-bc .sonata-ba-show label.checkbox.locale, +.sonata-bc .box-primary label.checkbox.locale { padding-right: 35px; background-position: right bottom; } .sonata-bc .sonata-ba-form .form-horizontal .controls input[type="checkbox"], -.sonata-bc .sonata-ba-show .form-horizontal .controls input[type="checkbox"] { +.sonata-bc .sonata-ba-show .form-horizontal .controls input[type="checkbox"], +.sonata-bc .box-primary .form-horizontal .controls input[type="checkbox"] { /*top: 5px;*/ position: relative; width: 15px; } .sonata-bc .sonata-ba-form .form-horizontal .controls input[type="checkbox"].locale, -.sonata-bc .sonata-ba-show .form-horizontal .controls input[type="checkbox"].locale { +.sonata-bc .sonata-ba-show .form-horizontal .controls input[type="checkbox"].locale, +.sonata-bc .box-primary .form-horizontal .controls input[type="checkbox"].locale { width: 65px; background-position: 0 center; } .sonata-bc .sonata-ba-form .form-horizontal .controls input[type="checkbox"]:before, -.sonata-bc .sonata-ba-show .form-horizontal .controls input[type="checkbox"]:before { +.sonata-bc .sonata-ba-show .form-horizontal .controls input[type="checkbox"]:before, +.sonata-bc .box-primary .form-horizontal .controls input[type="checkbox"]:before { margin-right: 35px; } +.sonata-bc .sonata-ba-show .locale_switcher { + margin-right: 15px; +} .sonata-bc.locale_nl label.wysiwyg.locale, .sonata-bc.locale_nl label.checkbox.locale, .sonata-bc.locale_nl input.locale, diff --git a/Resources/public/less/sonata-translation.less b/Resources/public/less/sonata-translation.less index 34581786..38a1f225 100644 --- a/Resources/public/less/sonata-translation.less +++ b/Resources/public/less/sonata-translation.less @@ -3,7 +3,7 @@ */ .sonata-bc { - .sonata-ba-form, .sonata-ba-show { + .sonata-ba-form, .sonata-ba-show, .box-primary { .locale_switcher { padding: 1px 0 0 0; text-align: right; diff --git a/Resources/views/Block/block_locale_switcher.html.twig b/Resources/views/Block/block_locale_switcher.html.twig index 4d4488ec..f58c143e 100644 --- a/Resources/views/Block/block_locale_switcher.html.twig +++ b/Resources/views/Block/block_locale_switcher.html.twig @@ -2,8 +2,15 @@ {% set admin = block_context.settings.admin %} {% set locale_switcher_route = block_context.settings.locale_switcher_route %} {% set locale_switcher_route_parameters = block_context.settings.locale_switcher_route_parameters %} +{% set currentLocale = object.locale %} + +{% if (admin.class is translatable) %} + {% for extension in admin.extensions %} + {% if (extension.translatableLocale is defined) %} + {% set currentLocale = extension.translatableLocale(admin) %} + {% endif %} + {% endfor %} -{% if (object is translatable) %}
{% spaceless %} {% for locale in sonata_translation_locales %} @@ -19,7 +26,7 @@ {'id': admin.id(object), 'tl': locale}|merge(locale_switcher_route_parameters) ) }}" accesskey="" - class=" {% if (object.locale == locale) %} active {% endif %}" + class=" {% if (currentLocale == locale) %} active {% endif %}" title="{{ 'admin.locale_switcher.tooltip' |trans([], 'SonataTranslationBundle') }}"> From 84d748e1aab1600ec090523baf35b09af9955cc7 Mon Sep 17 00:00:00 2001 From: Dariusz Markowicz Date: Sun, 11 Dec 2016 19:33:18 +0100 Subject: [PATCH 2/4] fix null variable in template; clean code object is null in list context, currentLocale is set later by extension --- .../views/Block/block_locale_switcher.html.twig | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Resources/views/Block/block_locale_switcher.html.twig b/Resources/views/Block/block_locale_switcher.html.twig index f58c143e..46a58ca7 100644 --- a/Resources/views/Block/block_locale_switcher.html.twig +++ b/Resources/views/Block/block_locale_switcher.html.twig @@ -2,11 +2,12 @@ {% set admin = block_context.settings.admin %} {% set locale_switcher_route = block_context.settings.locale_switcher_route %} {% set locale_switcher_route_parameters = block_context.settings.locale_switcher_route_parameters %} -{% set currentLocale = object.locale %} -{% if (admin.class is translatable) %} +{% if admin.class is translatable %} + {% set currentLocale = object.locale|default(null) %} + {% for extension in admin.extensions %} - {% if (extension.translatableLocale is defined) %} + {% if extension.translatableLocale is defined %} {% set currentLocale = extension.translatableLocale(admin) %} {% endif %} {% endfor %} @@ -14,8 +15,8 @@
{% spaceless %} {% for locale in sonata_translation_locales %} - {% if (locale_switcher_route is empty) %} - {% if (object.id) %} + {% if locale_switcher_route is empty %} + {% if object.id %} {% set locale_switcher_route = 'edit' %} {% else %} {% set locale_switcher_route = 'create' %} @@ -26,7 +27,7 @@ {'id': admin.id(object), 'tl': locale}|merge(locale_switcher_route_parameters) ) }}" accesskey="" - class=" {% if (currentLocale == locale) %} active {% endif %}" + class=" {% if currentLocale == locale %} active {% endif %}" title="{{ 'admin.locale_switcher.tooltip' |trans([], 'SonataTranslationBundle') }}"> From a468e566ce71f03ca79395cbc9fd0f3885754784 Mon Sep 17 00:00:00 2001 From: Dariusz Markowicz Date: Sun, 11 Dec 2016 23:05:59 +0100 Subject: [PATCH 3/4] Add documentation and tests for translationFieldFilter --- Resources/doc/reference/orm.rst | 38 +++++++++++++++++ Tests/Traits/GedmoOrmTest.php | 74 +++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/Resources/doc/reference/orm.rst b/Resources/doc/reference/orm.rst index 643e20e5..7b9b61ce 100644 --- a/Resources/doc/reference/orm.rst +++ b/Resources/doc/reference/orm.rst @@ -173,6 +173,44 @@ you have to make a translation class to handle it. protected $object; } +4. Configure search filter +-------------------------- + +**This step is optional**, but you can use the ``translationFieldFilter`` callback method on ``doctrine_orm_callback`` +filter to search on fields and on their translations. + +4.1 Example for configure search filter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: php + + add('title', 'doctrine_orm_callback', array( + 'callback' => array( + 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', + 'translationFieldFilter', + ), + )); + } + B. Using KnpLabs Doctrine Behaviours ------------------------------------ diff --git a/Tests/Traits/GedmoOrmTest.php b/Tests/Traits/GedmoOrmTest.php index ac0a0b57..f4e29d62 100644 --- a/Tests/Traits/GedmoOrmTest.php +++ b/Tests/Traits/GedmoOrmTest.php @@ -13,6 +13,8 @@ use Doctrine\Common\EventManager; use Gedmo\Translatable\TranslatableListener; +use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; +use Sonata\DoctrineORMAdminBundle\Filter\CallbackFilter; use Sonata\TranslationBundle\Test\DoctrineOrmTestCase; use Sonata\TranslationBundle\Tests\Fixtures\Traits\ORM\ArticlePersonalTranslatable; use Sonata\TranslationBundle\Tests\Fixtures\Traits\ORM\ArticlePersonalTranslation; @@ -77,6 +79,78 @@ public function testPersonalTranslatableEntity() $this->assertCount(3, $translations); } + public function testTranslationFieldFilter() + { + $qb = $this->em->createQueryBuilder() + ->select('o') + ->from(self::ARTICLE, 'o'); + $builder = new ProxyQuery($qb); + + $filter = new CallbackFilter(); + $filter->initialize('title', array( + 'callback' => array( + 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', + 'translationFieldFilter', + ), + )); + + $filter->filter($builder, 'o', 'title', array('type' => null, 'value' => 'foo')); + $this->assertEquals( + 'SELECT o FROM '.self::ARTICLE.' o LEFT JOIN o.translations t' + ." WHERE (t.field = 'title' AND t.content LIKE '%foo%') OR o.title LIKE '%foo%'", + $builder->getDQL() + ); + $this->assertTrue($filter->isActive()); + } + + public function testTranslationFieldFilterWithoutValue() + { + $qb = $this->em->createQueryBuilder() + ->select('o') + ->from(self::ARTICLE, 'o'); + $builder = new ProxyQuery($qb); + + $filter = new CallbackFilter(); + $filter->initialize('title', array( + 'callback' => array( + 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', + 'translationFieldFilter', + ), + )); + + $filter->filter($builder, 'o', 'title', array('type' => null, 'value' => null)); + $this->assertEquals( + 'SELECT o FROM '.self::ARTICLE.' o', + $builder->getDQL() + ); + $this->assertNull($filter->isActive()); + } + + public function testTranslationFieldFilterIfAlreadyJoined() + { + $qb = $this->em->createQueryBuilder() + ->select('o') + ->from(self::ARTICLE, 'o') + ->leftJoin('o.translations', 't'); + $builder = new ProxyQuery($qb); + + $filter = new CallbackFilter(); + $filter->initialize('title', array( + 'callback' => array( + 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', + 'translationFieldFilter', + ), + )); + + $filter->filter($builder, 'o', 'title', array('type' => null, 'value' => 'foo')); + $this->assertEquals( + 'SELECT o FROM '.self::ARTICLE.' o LEFT JOIN o.translations t' + ." WHERE (t.field = 'title' AND t.content LIKE '%foo%') OR o.title LIKE '%foo%'", + $builder->getDQL() + ); + $this->assertTrue($filter->isActive()); + } + protected function getUsedEntityFixtures() { return array( From f9d2b0df1aa8541f56f54d4479611dfc995a125e Mon Sep 17 00:00:00 2001 From: Dariusz Markowicz Date: Sun, 18 Dec 2016 16:38:22 +0100 Subject: [PATCH 4/4] Refactor translationFieldFilter callback to TranslationFieldFilter class --- .../Gedmo/TranslatableAdminExtension.php | 48 -------- Filter/TranslationFieldFilter.php | 113 ++++++++++++++++++ Resources/config/service.xml | 3 + Resources/doc/reference/orm.rst | 10 +- Tests/Traits/GedmoOrmTest.php | 41 ++----- 5 files changed, 132 insertions(+), 83 deletions(-) create mode 100644 Filter/TranslationFieldFilter.php diff --git a/Admin/Extension/Gedmo/TranslatableAdminExtension.php b/Admin/Extension/Gedmo/TranslatableAdminExtension.php index 8859fe2f..3d384e1d 100644 --- a/Admin/Extension/Gedmo/TranslatableAdminExtension.php +++ b/Admin/Extension/Gedmo/TranslatableAdminExtension.php @@ -14,7 +14,6 @@ use Gedmo\Translatable\TranslatableListener; use Sonata\AdminBundle\Admin\AdminInterface; use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; -use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\TranslationBundle\Admin\Extension\AbstractTranslatableAdminExtension; use Sonata\TranslationBundle\Checker\TranslatableChecker; @@ -58,53 +57,6 @@ public function configureQuery(AdminInterface $admin, ProxyQueryInterface $query $this->getTranslatableListener($admin)->setTranslationFallback(false); } - /** - * Search on normal field and on translated field, use with a - * doctrine_orm_callback filter type. - * - * @param ProxyQuery $queryBuilder - * @param string $alias - * @param string $field - * @param string $value - * - * @return bool|null - */ - public static function translationFieldFilter(ProxyQuery $queryBuilder, $alias, $field, $value) - { - if (!$value['value']) { - return; - } - - // verify if the join is not already done - $aliasAlreadyExists = false; - foreach ($queryBuilder->getDQLParts()['join'] as $joins) { - foreach ($joins as $join) { - if ($join->getAlias() === 't') { - $aliasAlreadyExists = true; - break 2; - } - } - } - - if (!$aliasAlreadyExists) { - $queryBuilder->leftJoin($alias.'.translations', 't'); - } - - // search on translation OR on normal field - $queryBuilder->andWhere($queryBuilder->expr()->orX( - $queryBuilder->expr()->andX( - $queryBuilder->expr()->eq('t.field', $queryBuilder->expr()->literal($field)), - $queryBuilder->expr()->like('t.content', $queryBuilder->expr()->literal('%'.$value['value'].'%')) - ), - $queryBuilder->expr()->like( - sprintf('%s.%s', $alias, $field), - $queryBuilder->expr()->literal('%'.$value['value'].'%') - ) - )); - - return true; - } - /** * @param AdminInterface $admin Deprecated, set TranslatableListener in the constructor instead * diff --git a/Filter/TranslationFieldFilter.php b/Filter/TranslationFieldFilter.php new file mode 100644 index 00000000..d781d414 --- /dev/null +++ b/Filter/TranslationFieldFilter.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\TranslationBundle\Filter; + +use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; +use Sonata\DoctrineORMAdminBundle\Filter\Filter; + +final class TranslationFieldFilter extends Filter +{ + /** + * {@inheritdoc} + */ + public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data) + { + if (!$data || !is_array($data) || !array_key_exists('value', $data)) { + return; + } + + $data['value'] = trim($data['value']); + + if (strlen($data['value']) == 0) { + return; + } + + $joinAlias = 'tff'; + + // verify if the join is not already done + $aliasAlreadyExists = false; + foreach ($queryBuilder->getDQLParts()['join'] as $joins) { + foreach ($joins as $join) { + if ($join->getAlias() === $joinAlias) { + $aliasAlreadyExists = true; + break 2; + } + } + } + + if (!$aliasAlreadyExists) { + $queryBuilder->leftJoin($alias.'.translations', $joinAlias); + } + + // search on translation OR on normal field + $queryBuilder->andWhere($queryBuilder->expr()->orX( + $queryBuilder->expr()->andX( + $queryBuilder->expr()->eq($joinAlias.'.field', $queryBuilder->expr()->literal($field)), + $queryBuilder->expr()->like( + $joinAlias.'.content', + $queryBuilder->expr()->literal('%'.$data['value'].'%') + ) + ), + $queryBuilder->expr()->like( + sprintf('%s.%s', $alias, $field), + $queryBuilder->expr()->literal('%'.$data['value'].'%') + ) + )); + + $this->active = true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultOptions() + { + return array( + 'field_type' => method_exists('Symfony\Component\Form\AbstractType', 'getBlockPrefix') + ? 'Symfony\Component\Form\Extension\Core\Type\TextType' + : 'text', // NEXT_MAJOR: Remove ternary (when requirement of Symfony is >= 2.8) + 'operator_type' => method_exists('Symfony\Component\Form\AbstractType', 'getBlockPrefix') + ? 'Symfony\Component\Form\Extension\Core\Type\HiddenType' + : 'hidden', // NEXT_MAJOR: Remove ternary (when requirement of Symfony is >= 2.8) + 'operator_options' => array(), + ); + } + + /** + * {@inheritdoc} + */ + public function getRenderSettings() + { + // NEXT_MAJOR: Remove this line when drop Symfony <2.8 support + $type = method_exists('Symfony\Component\Form\AbstractType', 'getBlockPrefix') + ? 'Sonata\AdminBundle\Form\Type\Filter\DefaultType' + : 'sonata_type_filter_default'; + + return array($type, array( + 'field_type' => $this->getFieldType(), + 'field_options' => $this->getFieldOptions(), + 'operator_type' => $this->getOption('operator_type'), + 'operator_options' => $this->getOption('operator_options'), + 'label' => $this->getLabel(), + )); + } + + /** + * {@inheritdoc} + */ + protected function association(ProxyQueryInterface $queryBuilder, $data) + { + $alias = $queryBuilder->entityJoin($this->getParentAssociationMappings()); + + return array($this->getOption('alias', $alias), $this->getFieldName()); + } +} diff --git a/Resources/config/service.xml b/Resources/config/service.xml index 62fd78c7..871247cb 100644 --- a/Resources/config/service.xml +++ b/Resources/config/service.xml @@ -5,5 +5,8 @@ + + + diff --git a/Resources/doc/reference/orm.rst b/Resources/doc/reference/orm.rst index 7b9b61ce..026a823a 100644 --- a/Resources/doc/reference/orm.rst +++ b/Resources/doc/reference/orm.rst @@ -176,7 +176,7 @@ you have to make a translation class to handle it. 4. Configure search filter -------------------------- -**This step is optional**, but you can use the ``translationFieldFilter`` callback method on ``doctrine_orm_callback`` +**This step is optional**, but you can use the ``doctrine_orm_translation_field`` filter to search on fields and on their translations. 4.1 Example for configure search filter @@ -193,6 +193,7 @@ filter to search on fields and on their translations. use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Show\ShowMapper; + use Sonata\TranslationBundle\Filter\TranslationFieldFilter; class FAQCategoryAdmin extends AbstractAdmin { @@ -203,12 +204,7 @@ filter to search on fields and on their translations. { $datagridMapper // ... - ->add('title', 'doctrine_orm_callback', array( - 'callback' => array( - 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', - 'translationFieldFilter', - ), - )); + ->add('title', TranslationFieldFilter::class); // or 'doctrine_orm_translation_field' } B. Using KnpLabs Doctrine Behaviours diff --git a/Tests/Traits/GedmoOrmTest.php b/Tests/Traits/GedmoOrmTest.php index f4e29d62..17caaa01 100644 --- a/Tests/Traits/GedmoOrmTest.php +++ b/Tests/Traits/GedmoOrmTest.php @@ -14,7 +14,7 @@ use Doctrine\Common\EventManager; use Gedmo\Translatable\TranslatableListener; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; -use Sonata\DoctrineORMAdminBundle\Filter\CallbackFilter; +use Sonata\TranslationBundle\Filter\TranslationFieldFilter; use Sonata\TranslationBundle\Test\DoctrineOrmTestCase; use Sonata\TranslationBundle\Tests\Fixtures\Traits\ORM\ArticlePersonalTranslatable; use Sonata\TranslationBundle\Tests\Fixtures\Traits\ORM\ArticlePersonalTranslation; @@ -86,18 +86,13 @@ public function testTranslationFieldFilter() ->from(self::ARTICLE, 'o'); $builder = new ProxyQuery($qb); - $filter = new CallbackFilter(); - $filter->initialize('title', array( - 'callback' => array( - 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', - 'translationFieldFilter', - ), - )); + $filter = new TranslationFieldFilter(); + $filter->initialize('title'); $filter->filter($builder, 'o', 'title', array('type' => null, 'value' => 'foo')); $this->assertEquals( - 'SELECT o FROM '.self::ARTICLE.' o LEFT JOIN o.translations t' - ." WHERE (t.field = 'title' AND t.content LIKE '%foo%') OR o.title LIKE '%foo%'", + 'SELECT o FROM '.self::ARTICLE.' o LEFT JOIN o.translations tff' + ." WHERE (tff.field = 'title' AND tff.content LIKE '%foo%') OR o.title LIKE '%foo%'", $builder->getDQL() ); $this->assertTrue($filter->isActive()); @@ -110,20 +105,15 @@ public function testTranslationFieldFilterWithoutValue() ->from(self::ARTICLE, 'o'); $builder = new ProxyQuery($qb); - $filter = new CallbackFilter(); - $filter->initialize('title', array( - 'callback' => array( - 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', - 'translationFieldFilter', - ), - )); + $filter = new TranslationFieldFilter(); + $filter->initialize('title'); $filter->filter($builder, 'o', 'title', array('type' => null, 'value' => null)); $this->assertEquals( 'SELECT o FROM '.self::ARTICLE.' o', $builder->getDQL() ); - $this->assertNull($filter->isActive()); + $this->assertFalse($filter->isActive()); } public function testTranslationFieldFilterIfAlreadyJoined() @@ -131,21 +121,16 @@ public function testTranslationFieldFilterIfAlreadyJoined() $qb = $this->em->createQueryBuilder() ->select('o') ->from(self::ARTICLE, 'o') - ->leftJoin('o.translations', 't'); + ->leftJoin('o.translations', 'tff'); $builder = new ProxyQuery($qb); - $filter = new CallbackFilter(); - $filter->initialize('title', array( - 'callback' => array( - 'Sonata\TranslationBundle\Admin\Extension\Gedmo\TranslatableAdminExtension', - 'translationFieldFilter', - ), - )); + $filter = new TranslationFieldFilter(); + $filter->initialize('title'); $filter->filter($builder, 'o', 'title', array('type' => null, 'value' => 'foo')); $this->assertEquals( - 'SELECT o FROM '.self::ARTICLE.' o LEFT JOIN o.translations t' - ." WHERE (t.field = 'title' AND t.content LIKE '%foo%') OR o.title LIKE '%foo%'", + 'SELECT o FROM '.self::ARTICLE.' o LEFT JOIN o.translations tff' + ." WHERE (tff.field = 'title' AND tff.content LIKE '%foo%') OR o.title LIKE '%foo%'", $builder->getDQL() ); $this->assertTrue($filter->isActive());