Skip to content

Commit 50e7c58

Browse files
author
jodou
committed
first commit
0 parents  commit 50e7c58

23 files changed

+1232
-0
lines changed

Config/Menu/TreeMenuItem.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace Umanit\EasyAdminTreeBundle\Config\Menu;
4+
5+
use EasyCorp\Bundle\EasyAdminBundle\Config\Menu\MenuItemTrait;
6+
use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA;
7+
use EasyCorp\Bundle\EasyAdminBundle\Config\Option\SortOrder;
8+
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Menu\MenuItemInterface;
9+
use EasyCorp\Bundle\EasyAdminBundle\Dto\MenuItemDto;
10+
use Symfony\Component\Uid\AbstractUid;
11+
use Symfony\Contracts\Translation\TranslatableInterface;
12+
13+
final class TreeMenuItem implements MenuItemInterface
14+
{
15+
private const MENU_TYPE_TREE = 'tree';
16+
17+
use MenuItemTrait;
18+
19+
public function __construct(TranslatableInterface|string $label, ?string $icon, string $entityFqcn)
20+
{
21+
$this->dto = new MenuItemDto();
22+
23+
$this->dto->setType(MenuItemDto::TYPE_URL);
24+
$this->dto->setLabel($label);
25+
$this->dto->setIcon($icon);
26+
$this->dto->setRouteParameters([
27+
EA::CRUD_ACTION => 'index',
28+
EA::CRUD_CONTROLLER_FQCN => null,
29+
EA::ENTITY_FQCN => $entityFqcn,
30+
EA::ENTITY_ID => null,
31+
]);
32+
$this->dto->setLinkUrl('http://google.fr');
33+
}
34+
35+
public function setController(string $controllerFqcn): self
36+
{
37+
$this->dto->setRouteParameters(array_merge(
38+
$this->dto->getRouteParameters(),
39+
[EA::CRUD_CONTROLLER_FQCN => $controllerFqcn]
40+
));
41+
42+
return $this;
43+
}
44+
45+
public function setAction(string $actionName): self
46+
{
47+
$this->dto->setRouteParameters(array_merge(
48+
$this->dto->getRouteParameters(),
49+
[EA::CRUD_ACTION => $actionName]
50+
));
51+
52+
return $this;
53+
}
54+
55+
public function setEntityId(/* AbstractUid|int|string */ $entityId): self
56+
{
57+
if (!\is_int($entityId) && !\is_string($entityId) && !$entityId instanceof AbstractUid) {
58+
trigger_deprecation(
59+
'easycorp/easyadmin-bundle',
60+
'4.0.5',
61+
'Argument "%s" for "%s" must be one of these types: %s. Passing type "%s" will cause an error in 5.0.0.',
62+
'$entityId',
63+
__METHOD__,
64+
sprintf('"int", "string" or "%s"', AbstractUid::class),
65+
\gettype($entityId)
66+
);
67+
}
68+
69+
$this->dto->setRouteParameters(array_merge(
70+
$this->dto->getRouteParameters(),
71+
[EA::ENTITY_ID => $entityId]
72+
));
73+
74+
return $this;
75+
}
76+
77+
/**
78+
* @param $sortFieldsAndOrder ['fieldName' => 'ASC|DESC', ...]
79+
*/
80+
public function setDefaultSort(array $sortFieldsAndOrder): self
81+
{
82+
$sortFieldsAndOrder = array_map('strtoupper', $sortFieldsAndOrder);
83+
foreach ($sortFieldsAndOrder as $sortField => $sortOrder) {
84+
if (!\in_array($sortOrder, [SortOrder::ASC, SortOrder::DESC], true)) {
85+
throw new \InvalidArgumentException(sprintf('The sort order can be only "ASC" or "DESC", "%s" given.', $sortOrder));
86+
}
87+
88+
if (!\is_string($sortField)) {
89+
throw new \InvalidArgumentException(sprintf('The keys of the array that defines the default sort must be strings with the field names, but the given "%s" value is a "%s".', $sortField, \gettype($sortField)));
90+
}
91+
}
92+
93+
$this->dto->setRouteParameters(array_merge(
94+
$this->dto->getRouteParameters(),
95+
[EA::SORT => $sortFieldsAndOrder]
96+
));
97+
98+
return $this;
99+
}
100+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace Umanit\EasyAdminTreeBundle\Controller;
4+
5+
use Doctrine\ORM\Query;
6+
use Doctrine\ORM\QueryBuilder;
7+
use Doctrine\Persistence\ManagerRegistry;
8+
use Doctrine\Persistence\ObjectRepository;
9+
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
10+
use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
11+
use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
12+
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
13+
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
14+
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
15+
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
16+
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
17+
18+
abstract class AbstractCategorizedCrudController extends AbstractCrudController
19+
{
20+
public const CATEGORY_URL_PARAM_NAME = 'umanit_category';
21+
22+
protected ManagerRegistry $registry;
23+
24+
public function __construct(
25+
ManagerRegistry $registry
26+
) {
27+
$this->registry = $registry;
28+
}
29+
30+
abstract public static function getCategoryFqcn(): string;
31+
32+
abstract protected static function getCategoryPropertyName(): string;
33+
34+
abstract protected function getDefaultCategoryId();
35+
36+
protected function getCategoryRepository(): ObjectRepository
37+
{
38+
return $this->registry->getRepository($this->getCategoryFqcn());
39+
}
40+
41+
public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
42+
{
43+
$qb = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
44+
45+
// On récupère une liste d'identifiant de la catégorie en cours + ceux des catégories enfants
46+
$currentCategoryId = $this->getCurrentCategoryId();
47+
$currentCategory = $this->getCategoryRepository()->find($currentCategoryId);
48+
$categories = $this
49+
->getCategoryRepository()
50+
->childrenQueryBuilder($currentCategory, false, null, 'ASC', true)
51+
->select('node.id')
52+
->getQuery()
53+
->getResult(Query::HYDRATE_SCALAR_COLUMN)
54+
;
55+
56+
// On applique la catégorie seulement s'il n'y a pas de recherche ou de filtre en cours
57+
if ('' === $searchDto->getQuery() && empty($filters->all())) {
58+
$qb
59+
->andWhere('entity.'.$this->getCategoryPropertyName().' in (:categories)')
60+
->setParameter('categories', $categories)
61+
;
62+
}
63+
64+
return $qb;
65+
}
66+
67+
public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
68+
{
69+
return KeyValueStore::new(array_merge($responseParameters->all(), [
70+
'context' => $this->getContext(),
71+
'current_category' => $this->getCurrentCategoryId(),
72+
]));
73+
}
74+
75+
protected function getCurrentCategoryId(): int
76+
{
77+
return $this->getContext()->getRequest()->get(self::CATEGORY_URL_PARAM_NAME) ?? $this->getDefaultCategoryId();
78+
}
79+
80+
public function configureCrud(Crud $crud): Crud
81+
{
82+
$crud
83+
->renderContentMaximized()
84+
->overrideTemplate('crud/index', '@UmanitEasyAdminTreeBundle/categorized-crud/index.html.twig')
85+
;
86+
87+
return $crud;
88+
}
89+
90+
public function configureAssets(Assets $assets): Assets
91+
{
92+
return $assets
93+
->addCssFile('bundles/umaniteasyadmintree/css/categorized-tree.css')
94+
;
95+
}
96+
}

Controller/TreeCrudController.php

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
namespace Umanit\EasyAdminTreeBundle\Controller;
4+
5+
use Doctrine\Persistence\ManagerRegistry;
6+
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
7+
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
8+
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
9+
use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
10+
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
11+
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
12+
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
13+
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
14+
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
15+
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
16+
use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
17+
use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
18+
use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
19+
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
20+
use EasyCorp\Bundle\EasyAdminBundle\Security\Permission;
21+
22+
abstract class TreeCrudController extends AbstractCrudController
23+
{
24+
25+
protected ManagerRegistry $doctrine;
26+
27+
public function __construct(ManagerRegistry $doctrine)
28+
{
29+
$this->doctrine = $doctrine;
30+
}
31+
32+
public function index(AdminContext $context)
33+
{
34+
$event = new BeforeCrudActionEvent($context);
35+
$this->container->get('event_dispatcher')->dispatch($event);
36+
37+
if ($event->isPropagationStopped()) {
38+
return $event->getResponse();
39+
}
40+
41+
if (!$this->isGranted(Permission::EA_EXECUTE_ACTION, ['action' => Action::INDEX, 'entity' => null])) {
42+
throw new ForbiddenActionException($context);
43+
}
44+
45+
$fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
46+
$context->getCrud()->setFieldAssets($this->getFieldAssets($fields));
47+
$filters = $this->container->get(FilterFactory::class)->create($context->getCrud()->getFiltersConfig(), $fields, $context->getEntity());
48+
49+
$repository = $this->doctrine->getRepository($context->getEntity()->getFqcn());
50+
51+
$queryBuilder = $repository
52+
->createQueryBuilder('entity')
53+
->orderBy('entity.root, entity.lft', 'ASC')
54+
;
55+
56+
$this->doctrine->getManager()->getConfiguration()->addCustomHydrationMode('tree', 'Gedmo\Tree\Hydrator\ORM\TreeObjectHydrator');
57+
$entities = $queryBuilder->getQuery()->getResult();
58+
$entities = $this->container->get(EntityFactory::class)->createCollection($context->getEntity(), $entities);
59+
60+
$this->container->get(EntityFactory::class)->processFieldsForAll($entities, $fields);
61+
$actions = $this->container->get(EntityFactory::class)->processActionsForAll($entities, $context->getCrud()->getActionsConfig());
62+
63+
$responseParameters = $this->configureResponseParameters(KeyValueStore::new([
64+
'pageName' => Crud::PAGE_INDEX,
65+
'templateName' => 'crud/index',
66+
'entities' => $entities,
67+
'global_actions' => $actions->getGlobalActions(),
68+
'batch_actions' => null,
69+
'filters' => $filters,
70+
]));
71+
72+
$event = new AfterCrudActionEvent($context, $responseParameters);
73+
$this->container->get('event_dispatcher')->dispatch($event);
74+
75+
if ($event->isPropagationStopped()) {
76+
return $event->getResponse();
77+
}
78+
79+
return $responseParameters;
80+
}
81+
82+
public function configureFields(string $pageName): iterable
83+
{
84+
if ($pageName === Crud::PAGE_INDEX) {
85+
return [TextField::new($this->getEntityLabelProperty())];
86+
}
87+
88+
return [];
89+
}
90+
91+
public function configureCrud(Crud $crud): Crud
92+
{
93+
return $crud
94+
->overrideTemplate('crud/index', '@UmanitEasyAdminTreeBundle/index.html.twig')
95+
->setPaginatorPageSize(9999999)
96+
->showEntityActionsInlined()
97+
->setSearchFields(null)
98+
;
99+
}
100+
101+
public static function getEntityFqcn(): string
102+
{
103+
throw new \LogicException('Override this method in child class');
104+
}
105+
106+
public function configureAssets(Assets $assets): Assets
107+
{
108+
return $assets
109+
->addCssFile('bundles/umaniteasyadmintree/css/tree.css')
110+
;
111+
}
112+
113+
public function configureActions(Actions $actions): Actions
114+
{
115+
return $actions
116+
->update(Crud::PAGE_INDEX, Action::EDIT,
117+
function (Action $action) {
118+
return $action
119+
->addCssClass('umanit_easyadmintree_tree-item-action')
120+
;
121+
}
122+
)
123+
->update(Crud::PAGE_INDEX, Action::DELETE,
124+
function (Action $action) {
125+
return $action
126+
->addCssClass('action-delete umanit_easyadmintree_tree-item-action')
127+
;
128+
}
129+
)
130+
;
131+
}
132+
133+
abstract protected function getEntityLabelProperty(): string;
134+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Umanit\EasyAdminTreeBundle\DependencyInjection;
4+
5+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6+
use Symfony\Component\Config\Definition\ConfigurationInterface;
7+
8+
/**
9+
* This is the class that validates and merges configuration from your app/config files
10+
*
11+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
12+
*/
13+
class Configuration implements ConfigurationInterface
14+
{
15+
/**
16+
* {@inheritDoc}
17+
*/
18+
public function getConfigTreeBuilder()
19+
{
20+
$treeBuilder = new TreeBuilder('umanit_easy_admin_tree');
21+
$rootNode = $treeBuilder->getRootNode();
22+
23+
// Here you should define the parameters that are allowed to
24+
// configure your bundle. See the documentation linked above for
25+
// more information on that topic.
26+
27+
return $treeBuilder;
28+
}
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Umanit\EasyAdminTreeBundle\DependencyInjection;
4+
5+
use Symfony\Component\DependencyInjection\ContainerBuilder;
6+
use Symfony\Component\Config\FileLocator;
7+
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8+
use Symfony\Component\DependencyInjection\Loader;
9+
10+
/**
11+
* This is the class that loads and manages your bundle configuration
12+
*
13+
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
14+
*/
15+
class UmanitEasyAdminTreeExtension extends Extension
16+
{
17+
/**
18+
* {@inheritDoc}
19+
*/
20+
public function load(array $configs, ContainerBuilder $container)
21+
{
22+
$configuration = new Configuration();
23+
$this->processConfiguration($configuration, $configs);
24+
25+
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
26+
$loader->load('services.yaml');
27+
}
28+
}

0 commit comments

Comments
 (0)