diff --git a/Controller/ReorderController.php b/Controller/ReorderController.php index 5dcd399..a46a211 100644 --- a/Controller/ReorderController.php +++ b/Controller/ReorderController.php @@ -12,12 +12,18 @@ namespace FSi\Bundle\AdminTreeBundle\Controller; use FSi\Bundle\AdminBundle\Admin\CRUD\DataIndexerElement; -use FSi\Bundle\AdminBundle\Doctrine\Admin\Element; +use FSi\Bundle\AdminBundle\Admin\Element as AdminElement; +use FSi\Bundle\AdminBundle\Doctrine\Admin\Element as AdminDoctrineElement; +use FSi\Bundle\AdminBundle\Event\AdminEvent; +use FSi\Bundle\AdminTreeBundle\Event\MovedDownTreeEvent; +use FSi\Bundle\AdminTreeBundle\Event\MovedUpTreeEvent; use Gedmo\Tree\Entity\Repository\NestedTreeRepository; use InvalidArgumentException; +use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\RouterInterface; use function get_class; @@ -30,27 +36,64 @@ class ReorderController */ private $router; - public function __construct(RouterInterface $router) + /** + * @var EventDispatcherInterface|PsrEventDispatcherInterface + */ + private $eventDispatcher; + + public function __construct(RouterInterface $router, EventDispatcherInterface $eventDispatcher) { $this->router = $router; + $this->eventDispatcher = $eventDispatcher; } + /** + * @param DataIndexerElement&AdminDoctrineElement $element + * @param mixed $id + * @param Request $request + * @return Response + */ public function moveUpAction(DataIndexerElement $element, $id, Request $request): Response { - $this->getRepository($element)->moveUp($this->getEntity($element, $id)); - $this->flush($element); + $entity = $this->getEntity($element, $id); + + $this->getRepository($element)->moveUp($entity); + $element->getObjectManager()->flush(); + + $this->dispatchEvent(new MovedUpTreeEvent($element, $request, $entity)); return $this->getRedirectResponse($element, $request); } + /** + * @param DataIndexerElement&AdminDoctrineElement $element + * @param mixed $id + * @param Request $request + * @return Response + */ public function moveDownAction(DataIndexerElement $element, $id, Request $request): Response { - $this->getRepository($element)->moveDown($this->getEntity($element, $id)); - $this->flush($element); + $entity = $this->getEntity($element, $id); + + $this->getRepository($element)->moveDown($entity); + $element->getObjectManager()->flush(); + + $this->dispatchEvent(new MovedDownTreeEvent($element, $request, $entity)); return $this->getRedirectResponse($element, $request); } + private function dispatchEvent(AdminEvent $event): void + { + if (true === interface_exists(PsrEventDispatcherInterface::class) + && true === $this->eventDispatcher instanceof PsrEventDispatcherInterface + ) { + $this->eventDispatcher->dispatch($event); + } else { + $this->eventDispatcher->dispatch(get_class($event), $event); + } + } + /** * @param DataIndexerElement $element * @param mixed $id @@ -71,7 +114,7 @@ private function getEntity(DataIndexerElement $element, $id) return $entity; } - private function getRepository(Element $element): NestedTreeRepository + private function getRepository(AdminDoctrineElement $element): NestedTreeRepository { $repository = $element->getRepository(); if (false === $repository instanceof NestedTreeRepository) { @@ -85,15 +128,11 @@ private function getRepository(Element $element): NestedTreeRepository return $repository; } - private function flush(Element $element): void - { - $element->getObjectManager()->flush(); - } - - private function getRedirectResponse(DataIndexerElement $element, Request $request): RedirectResponse + private function getRedirectResponse(AdminElement $element, Request $request): RedirectResponse { - if ($request->query->get('redirect_uri')) { - $uri = $request->query->get('redirect_uri'); + $redirectUri = $request->query->get('redirect_uri'); + if (null !== $redirectUri && '' !== $redirectUri) { + $uri = $redirectUri; } else { $uri = $this->router->generate($element->getRoute(), $element->getRouteParameters()); } diff --git a/DependencyInjection/FSiAdminTreeExtension.php b/DependencyInjection/FSiAdminTreeExtension.php index d9b757d..1bf9bd4 100644 --- a/DependencyInjection/FSiAdminTreeExtension.php +++ b/DependencyInjection/FSiAdminTreeExtension.php @@ -18,6 +18,11 @@ final class FSiAdminTreeExtension extends Extension { + /** + * @param array $configs + * @param ContainerBuilder $container + * @return void + */ public function load(array $configs, ContainerBuilder $container) { $loader = new Loader\XmlFileLoader( diff --git a/Event/MovedDownTreeEvent.php b/Event/MovedDownTreeEvent.php new file mode 100644 index 0000000..6c820f9 --- /dev/null +++ b/Event/MovedDownTreeEvent.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace FSi\Bundle\AdminTreeBundle\Event; + +use FSi\Bundle\AdminBundle\Admin\Element; +use FSi\Bundle\AdminBundle\Event\AdminEvent; +use InvalidArgumentException; +use Symfony\Component\HttpFoundation\Request; + +final class MovedDownTreeEvent extends AdminEvent +{ + /** + * @var object + */ + private $entity; + + /** + * @param Element $element + * @param Request $request + * @param mixed $entity + * @throws InvalidArgumentException + */ + public function __construct(Element $element, Request $request, $entity) + { + if (false === is_object($entity)) { + throw new InvalidArgumentException(sprintf( + 'Expected an object, got "%s" instead', + gettype($entity) + )); + } + + parent::__construct($element, $request); + $this->entity = $entity; + } + + /** + * @return object + */ + public function getEntity() + { + return $this->entity; + } +} diff --git a/Event/MovedUpTreeEvent.php b/Event/MovedUpTreeEvent.php new file mode 100644 index 0000000..172c17e --- /dev/null +++ b/Event/MovedUpTreeEvent.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace FSi\Bundle\AdminTreeBundle\Event; + +use FSi\Bundle\AdminBundle\Admin\Element; +use FSi\Bundle\AdminBundle\Event\AdminEvent; +use InvalidArgumentException; +use Symfony\Component\HttpFoundation\Request; +use function gettype; +use function is_object; + +final class MovedUpTreeEvent extends AdminEvent +{ + /** + * @var object + */ + private $entity; + + /** + * @param Element $element + * @param Request $request + * @param mixed $entity + * @throws InvalidArgumentException + */ + public function __construct(Element $element, Request $request, $entity) + { + if (false === is_object($entity)) { + throw new InvalidArgumentException(sprintf( + 'Expected an object, got "%s" instead', + gettype($entity) + )); + } + + parent::__construct($element, $request); + $this->entity = $entity; + } + + /** + * @return object + */ + public function getEntity() + { + return $this->entity; + } +} diff --git a/Resources/config/services.xml b/Resources/config/services.xml index e4f3dde..781b444 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -11,6 +11,7 @@ + diff --git a/Resources/doc/usage.md b/Resources/doc/usage.md index 1f0b615..83b41ef 100644 --- a/Resources/doc/usage.md +++ b/Resources/doc/usage.md @@ -28,3 +28,13 @@ columns: content: url_attr: { class: "btn btn-warning btn-sm" } ``` + +## Events + +Since version 2.2, an event of respective class name is fired after moving an +item up or down the tree: + +- FSi\Bundle\AdminTreeBundle\Event\MovedUpTreeEvent +- FSi\Bundle\AdminTreeBundle\Event\MovedDownTreeEvent + +Both contain the object that was being moved. diff --git a/composer.json b/composer.json index 9d9f286..2df63a9 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.1-dev", + "dev-master": "2.2-dev", + "2.1": "2.1-dev", "2.0": "2.0-dev", "1.0": "1.0-dev" } diff --git a/spec/FSi/Bundle/AdminTreeBundle/Controller/ReorderControllerSpec.php b/spec/FSi/Bundle/AdminTreeBundle/Controller/ReorderControllerSpec.php index 5aa5808..3f6a0a2 100644 --- a/spec/FSi/Bundle/AdminTreeBundle/Controller/ReorderControllerSpec.php +++ b/spec/FSi/Bundle/AdminTreeBundle/Controller/ReorderControllerSpec.php @@ -15,13 +15,17 @@ use Doctrine\ORM\EntityRepository; use FSi\Bundle\AdminBundle\Doctrine\Admin\CRUDElement; use FSi\Bundle\AdminTreeBundle\Controller\ReorderController; +use FSi\Bundle\AdminTreeBundle\Event\MovedDownTreeEvent; +use FSi\Bundle\AdminTreeBundle\Event\MovedUpTreeEvent; use FSi\Component\DataIndexer\DoctrineDataIndexer; use FSi\Component\DataIndexer\Exception\RuntimeException; use Gedmo\Tree\Entity\Repository\NestedTreeRepository; use InvalidArgumentException; use PhpSpec\ObjectBehavior; use Prophecy\Argument; +use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface; use stdClass; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -31,6 +35,7 @@ final class ReorderControllerSpec extends ObjectBehavior { public function let( RouterInterface $router, + EventDispatcherInterface $eventDispatcher, CRUDElement $element, DoctrineDataIndexer $indexer, ObjectManager $om, @@ -46,7 +51,7 @@ public function let( $element->getRoute()->willReturn('fsi_admin_crud_list'); $element->getRouteParameters()->willReturn(['element' => 'category']); - $this->beConstructedWith($router); + $this->beConstructedWith($router, $eventDispatcher); } public function it_is_initializable(): void @@ -61,6 +66,7 @@ public function it_moves_up_item_when_move_up_action_called( ObjectManager $om, RouterInterface $router, DoctrineDataIndexer $indexer, + EventDispatcherInterface $eventDispatcher, Request $request ): void { $indexer->getData(1)->willReturn($category); @@ -69,6 +75,15 @@ public function it_moves_up_item_when_move_up_action_called( $om->flush()->shouldBeCalled(); + if (true === is_subclass_of(EventDispatcherInterface::class, PsrEventDispatcherInterface::class)) { + $eventDispatcher->dispatch(Argument::type(MovedUpTreeEvent::class))->shouldBeCalled(); + } else { + $eventDispatcher->dispatch( + 'FSi\Bundle\AdminTreeBundle\Event\MovedUpTreeEvent', + Argument::type(MovedUpTreeEvent::class) + )->shouldBeCalled(); + } + $router->generate( 'fsi_admin_crud_list', Argument::withEntry('element', 'category') @@ -86,6 +101,7 @@ public function it_moves_down_item_when_move_down_action_called( ObjectManager $om, RouterInterface $router, DoctrineDataIndexer $indexer, + EventDispatcherInterface $eventDispatcher, Request $request ): void { $indexer->getData(1)->willReturn($category); @@ -94,6 +110,15 @@ public function it_moves_down_item_when_move_down_action_called( $om->flush()->shouldBeCalled(); + if (true === is_subclass_of(EventDispatcherInterface::class, PsrEventDispatcherInterface::class)) { + $eventDispatcher->dispatch(Argument::type(MovedDownTreeEvent::class))->shouldBeCalled(); + } else { + $eventDispatcher->dispatch( + 'FSi\Bundle\AdminTreeBundle\Event\MovedDownTreeEvent', + Argument::type(MovedDownTreeEvent::class) + )->shouldBeCalled(); + } + $router->generate( 'fsi_admin_crud_list', Argument::withEntry('element', 'category') @@ -130,6 +155,7 @@ public function it_throws_exception_when_entity_doesnt_have_correct_repository( } public function it_redirects_to_redirect_uri_parameter_after_operation( + EventDispatcherInterface $eventDispatcher, CRUDElement $element, DoctrineDataIndexer $indexer, stdClass $category, @@ -139,10 +165,28 @@ public function it_redirects_to_redirect_uri_parameter_after_operation( $query->get('redirect_uri')->willReturn('some_redirect_uri'); $indexer->getData(1)->willReturn($category); + if (true === is_subclass_of(EventDispatcherInterface::class, PsrEventDispatcherInterface::class)) { + $eventDispatcher->dispatch(Argument::type(MovedUpTreeEvent::class))->shouldBeCalled(); + } else { + $eventDispatcher->dispatch( + 'FSi\Bundle\AdminTreeBundle\Event\MovedUpTreeEvent', + Argument::type(MovedUpTreeEvent::class) + )->shouldBeCalled(); + } + $response = $this->moveUpAction($element, 1, $request); $response->shouldHaveType(RedirectResponse::class); $response->getTargetUrl()->shouldReturn('some_redirect_uri'); + if (true === is_subclass_of(EventDispatcherInterface::class, PsrEventDispatcherInterface::class)) { + $eventDispatcher->dispatch(Argument::type(MovedDownTreeEvent::class))->shouldBeCalled(); + } else { + $eventDispatcher->dispatch( + 'FSi\Bundle\AdminTreeBundle\Event\MovedDownTreeEvent', + Argument::type(MovedDownTreeEvent::class) + )->shouldBeCalled(); + } + $response = $this->moveDownAction($element, 1, $request); $response->shouldHaveType(RedirectResponse::class); $response->getTargetUrl()->shouldReturn('some_redirect_uri');