Skip to content

Commit

Permalink
Add admin filters
Browse files Browse the repository at this point in the history
  • Loading branch information
core23 committed Jun 19, 2020
1 parent d24b74e commit 084d273
Show file tree
Hide file tree
Showing 6 changed files with 457 additions and 0 deletions.
125 changes: 125 additions & 0 deletions src/Admin/Filter/CategoryFilter.php
@@ -0,0 +1,125 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\ClassificationBundle\Admin\Filter;

use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Form\Type\Filter\DefaultType;
use Sonata\ClassificationBundle\Model\CategoryInterface;
use Sonata\ClassificationBundle\Model\CategoryManagerInterface;
use Sonata\DoctrineORMAdminBundle\Filter\Filter;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

final class CategoryFilter extends Filter
{
/**
* @var CategoryManagerInterface
*/
private $categoryManager;

public function __construct(CategoryManagerInterface $categoryManager)
{
$this->categoryManager = $categoryManager;
}

public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data): void
{
if (null === $data || !\is_array($data) || !\array_key_exists('value', $data)) {
return;
}

if (null !== $data['value']) {
$queryBuilder
->andWhere(sprintf('%s.%s = :category', $alias, $field))
->setParameter('category', $data['value'])
;
}

$this->active = null !== $data['value'];
}

public function getDefaultOptions(): array
{
return [
'context' => null,
];
}

public function getFieldType(): string
{
return $this->getOption('field_type', ChoiceType::class);
}

public function getFieldOptions(): array
{
return $this->getOption('choices', [
'choices' => $this->getChoices(),
'choice_translation_domain' => false,
]);
}

public function getRenderSettings(): array
{
return [DefaultType::class, [
'field_type' => $this->getFieldType(),
'field_options' => $this->getFieldOptions(),
'label' => $this->getLabel(),
]];
}

protected function association(ProxyQueryInterface $queryBuilder, $data): array
{
$alias = $queryBuilder->entityJoin($this->getParentAssociationMappings());
$part = strrchr('.'.$this->getFieldName(), '.');
$fieldName = substr(false === $part ? $this->getFieldType() : $part, 1);

return [$alias, $fieldName];
}

/**
* @return array<string, int>
*/
private function getChoices(): array
{
$context = $this->getOption('context');

if (null === $context) {
$categories = $this->categoryManager->getAllRootCategories();
} else {
$categories = $this->categoryManager->getRootCategoriesForContext($context);
}

$choices = [];

foreach ($categories as $category) {
$choices[sprintf('%s (%s)', $category->getName(), $category->getContext()->getId())] = $category->getId();

$this->visitChild($category, $choices);
}

return $choices;
}

private function visitChild(CategoryInterface $category, array &$choices, int $level = 2): void
{
if (0 === \count($category->getChildren())) {
return;
}

foreach ($category->getChildren() as $child) {
$choices[sprintf('%s %s', str_repeat('-', 1 * $level), (string) $child)] = $child->getId();

$this->visitChild($child, $choices, $level + 1);
}
}
}
109 changes: 109 additions & 0 deletions src/Admin/Filter/CollectionFilter.php
@@ -0,0 +1,109 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\ClassificationBundle\Admin\Filter;

use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Form\Type\Filter\DefaultType;
use Sonata\ClassificationBundle\Model\CollectionManagerInterface;
use Sonata\DoctrineORMAdminBundle\Filter\Filter;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

final class CollectionFilter extends Filter
{
/**
* @var CollectionManagerInterface
*/
private $collectionManager;

public function __construct(CollectionManagerInterface $collectionManager)
{
$this->collectionManager = $collectionManager;
}

public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data): void
{
if (null === $data || !\is_array($data) || !\array_key_exists('value', $data)) {
return;
}

if ($data['value']) {
$queryBuilder
->andWhere(sprintf('%s.%s = :collection', $alias, $field))
->setParameter('collection', $data['value'])
;
}

$this->active = null !== $data['value'];
}

public function getDefaultOptions(): array
{
return [
'context' => null,
];
}

public function getFieldType(): string
{
return $this->getOption('field_type', ChoiceType::class);
}

public function getFieldOptions(): array
{
return $this->getOption('choices', [
'choices' => $this->getChoices(),
'choice_translation_domain' => false,
]);
}

public function getRenderSettings(): array
{
return [DefaultType::class, [
'field_type' => $this->getFieldType(),
'field_options' => $this->getFieldOptions(),
'label' => $this->getLabel(),
]];
}

protected function association(ProxyQueryInterface $queryBuilder, $data): array
{
$alias = $queryBuilder->entityJoin($this->getParentAssociationMappings());
$part = strrchr('.'.$this->getFieldName(), '.');
$fieldName = substr(false === $part ? $this->getFieldType() : $part, 1);

return [$alias, $fieldName];
}

/**
* @return array<string, int>
*/
private function getChoices(): array
{
$context = $this->getOption('context');

if (null === $context) {
$collections = $this->collectionManager->findAll();
} else {
$collections = $this->collectionManager->getByContext($context);
}

$choices = [];

foreach ($collections as $collection) {
$choices[(string) $collection] = $collection->getId();
}

return $choices;
}
}
8 changes: 8 additions & 0 deletions src/Resources/config/admin.xml
Expand Up @@ -50,5 +50,13 @@
<argument>%sonata.classification.admin.context.translation_domain%</argument>
</call>
</service>
<service id="Sonata\ClassificationBundle\Admin\Filter\CategoryFilter">
<tag name="sonata.admin.filter.type"/>
<argument type="service" id="sonata.classification.manager.category"/>
</service>
<service id="Sonata\ClassificationBundle\Admin\Filter\CollectionFilter">
<tag name="sonata.admin.filter.type"/>
<argument type="service" id="sonata.classification.manager.collection"/>
</service>
</services>
</container>
46 changes: 46 additions & 0 deletions tests/Admin/Filter/CategoryFilterTest.php
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\ClassificationBundle\Tests\Admin\Filter;

use PHPUnit\Framework\TestCase;
use Sonata\ClassificationBundle\Admin\Filter\CategoryFilter;
use Sonata\ClassificationBundle\Model\CategoryManagerInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

class CategoryFilterTest extends TestCase
{
/**
* @var MockObject&CategoryManagerInterface
*/
private $categoryManager;

protected function setUp(): void
{
$this->categoryManager = $this->createStub(CategoryManagerInterface::class);
}

public function testRenderSettings(): void
{
$this->categoryManager->method('getAllRootCategories')->willReturn([]);

$filter = new CategoryFilter($this->categoryManager);
$filter->initialize('field_name', [
'field_options' => ['class' => 'FooBar'],
]);
$options = $filter->getRenderSettings()[1];

$this->assertSame(ChoiceType::class, $options['field_type']);
$this->assertSame([], $options['field_options']['choices']);
}
}
47 changes: 47 additions & 0 deletions tests/Admin/Filter/CollectionFilterTest.php
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\ClassificationBundle\Tests\Admin\Filter;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sonata\ClassificationBundle\Admin\Filter\CollectionFilter;
use Sonata\ClassificationBundle\Model\CollectionManagerInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

class CollectionFilterTest extends TestCase
{
/**
* @var MockObject&CollectionManagerInterface
*/
private $collectionManager;

protected function setUp(): void
{
$this->collectionManager = $this->createStub(CollectionManagerInterface::class);
}

public function testRenderSettings(): void
{
$this->collectionManager->method('findAll')->willReturn([]);

$filter = new CollectionFilter($this->collectionManager);
$filter->initialize('field_name', [
'field_options' => ['class' => 'FooBar'],
]);
$options = $filter->getRenderSettings()[1];

$this->assertSame(ChoiceType::class, $options['field_type']);
$this->assertSame([], $options['field_options']['choices']);
}
}

0 comments on commit 084d273

Please sign in to comment.