diff --git a/composer.json b/composer.json index 94a9ff54..528addb3 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,7 @@ "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", "sylius-labs/coding-standard": "^4.0", - "sylius/resource-bundle": "^1.10", + "sylius/resource-bundle": "dev-poc-new-resource-metadata", "symfony/console": "^5.4 || ^6.0", "symfony/dotenv": "^5.4 || ^6.0", "symfony/maker-bundle": "^1.36", @@ -79,7 +79,9 @@ "twig/extensions": "^1.5", "twig/twig": "^2.12 || ^3.0", "vimeo/psalm": "^4.26", - "rector/rector": "^0.13" + "rector/rector": "^0.13", + "symfony/uid": "^5.4 || ^6.0", + "symfony/messenger": "^5.4 || ^6.0" }, "suggest": { "sylius/currency-bundle": "^1.7" diff --git a/ecs.php b/ecs.php index 21ea33d6..deb1f587 100644 --- a/ecs.php +++ b/ecs.php @@ -2,6 +2,7 @@ use PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer; use PhpCsFixer\Fixer\Comment\HeaderCommentFixer; +use PhpCsFixer\Fixer\FunctionNotation\MethodArgumentSpaceFixer; use PhpCsFixer\Fixer\Phpdoc\NoSuperfluousPhpdocTagsFixer; use SlevomatCodingStandard\Sniffs\Commenting\InlineDocCommentDeclarationSniff; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -26,9 +27,10 @@ ->set(NoSuperfluousPhpdocTagsFixer::class)->call('configure', [['allow_mixed' => true]]) ; - $containerConfigurator->parameters()->set(Option::SKIP, [ + $containerConfigurator->parameters()->set(Option::SKIP, value: [ InlineDocCommentDeclarationSniff::class . '.MissingVariable', VisibilityRequiredFixer::class => ['*Spec.php'], + MethodArgumentSpaceFixer::class => ['*/BoardGameBlog/*'], '**/var/*', ]); }; diff --git a/src/Bundle/Builder/GridBuilder.php b/src/Bundle/Builder/GridBuilder.php index 93336720..44c460f9 100644 --- a/src/Bundle/Builder/GridBuilder.php +++ b/src/Bundle/Builder/GridBuilder.php @@ -29,6 +29,9 @@ final class GridBuilder implements GridBuilderInterface private array $driverConfiguration = []; + /** @var string|callable|null */ + private $provider; + /** @var FieldInterface[] */ private array $fields = []; @@ -72,6 +75,18 @@ public function setDriver(string $driver): GridBuilderInterface return $this; } + public function getProvider(): callable|string|null + { + return $this->provider; + } + + public function setProvider(callable|string|null $provider): GridBuilderInterface + { + $this->provider = $provider; + + return $this; + } + /** * @param mixed $value */ @@ -225,6 +240,8 @@ public function toArray(): array $output['extends'] = $this->extends; } + $output['provider'] = $this->provider ?? 'sylius.grid.data_provider.default'; + return $output; } } diff --git a/src/Bundle/Builder/GridBuilderInterface.php b/src/Bundle/Builder/GridBuilderInterface.php index 75ec643b..38b0ba12 100644 --- a/src/Bundle/Builder/GridBuilderInterface.php +++ b/src/Bundle/Builder/GridBuilderInterface.php @@ -18,6 +18,13 @@ use Sylius\Bundle\GridBundle\Builder\Field\FieldInterface; use Sylius\Bundle\GridBundle\Builder\Filter\FilterInterface; +/** + * @method string|callable|null getProvider + * @method GridBuilderInterface setProvider(string|callable|null $provider) + * + * @psalm-method string|callable|null getProvider + * @psalm-method GridBuilderInterface setProvider(string|callable|null $provider) + */ interface GridBuilderInterface { public static function create(string $name, ?string $resourceClass = null): self; diff --git a/src/Bundle/DependencyInjection/SyliusGridExtension.php b/src/Bundle/DependencyInjection/SyliusGridExtension.php index ca77b907..a9844736 100644 --- a/src/Bundle/DependencyInjection/SyliusGridExtension.php +++ b/src/Bundle/DependencyInjection/SyliusGridExtension.php @@ -16,6 +16,7 @@ use Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle; use Sylius\Bundle\GridBundle\Grid\GridInterface; use Sylius\Bundle\GridBundle\SyliusGridBundle; +use Sylius\Component\Grid\Data\DataProviderInterface; use Sylius\Component\Grid\Filtering\FilterInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -62,6 +63,10 @@ public function load(array $configs, ContainerBuilder $container): void $container->registerForAutoconfiguration(FilterInterface::class) ->addTag('sylius.grid_filter') ; + + $container->registerForAutoconfiguration(DataProviderInterface::class) + ->addTag('sylius.grid_data_provider') + ; } public function getConfiguration(array $config, ContainerBuilder $container): Configuration diff --git a/src/Bundle/Resources/config/services.xml b/src/Bundle/Resources/config/services.xml index 8a2077cc..3879df82 100644 --- a/src/Bundle/Resources/config/services.xml +++ b/src/Bundle/Resources/config/services.xml @@ -72,12 +72,17 @@ - + + + + + + + - diff --git a/src/Bundle/Tests/DataFixtures/ORM/fixtures.yml b/src/Bundle/Tests/DataFixtures/ORM/fixtures.yml index ed91aefb..a53e31cc 100644 --- a/src/Bundle/Tests/DataFixtures/ORM/fixtures.yml +++ b/src/Bundle/Tests/DataFixtures/ORM/fixtures.yml @@ -46,3 +46,15 @@ App\Entity\Price: price_2: amount: 1000 currencyCode: "GBP" + +App\BoardGameBlog\Domain\ValueObject\BoardGameName: + stone_age_name: + __construct: ['Stone Age'] + ticket_to_ride_name: + __construct: ['Ticket to Ride'] + +App\BoardGameBlog\Domain\Model\BoardGame: + stone_age: + __construct: ['@stone_age_name'] + ticket_to_ride: + __construct: ['@ticket_to_ride_name'] diff --git a/src/Bundle/Tests/Functional/BoardGameUiTest.php b/src/Bundle/Tests/Functional/BoardGameUiTest.php new file mode 100644 index 00000000..90c2f910 --- /dev/null +++ b/src/Bundle/Tests/Functional/BoardGameUiTest.php @@ -0,0 +1,49 @@ +loadFixturesFromFile('fixtures.yml'); + + $this->client->request('GET', '/admin/board-games'); + $response = $this->client->getResponse(); + + $this->assertResponseCode($response, Response::HTTP_OK); + $content = $response->getContent(); + + $this->assertStringContainsString('Stone Age', $content); + $this->assertStringContainsString(sprintf('Show', $boardGames['stone_age']->id()), $content); + $this->assertStringContainsString(sprintf('Edit', $boardGames['stone_age']->id()), $content); + $this->assertStringContainsString(sprintf('
', $boardGames['stone_age']->id()), $content); + + $this->assertStringContainsString('Ticket to Ride', $content); + $this->assertStringContainsString(sprintf('Show', $boardGames['ticket_to_ride']->id()), $content); + $this->assertStringContainsString(sprintf('Edit', $boardGames['ticket_to_ride']->id()), $content); + $this->assertStringContainsString(sprintf('', $boardGames['ticket_to_ride']->id()), $content); + } + + protected function buildMatcher(): Matcher + { + return $this->matcherFactory->createMatcher(new VoidBacktrace()); + } +} diff --git a/src/Bundle/test/config/db.sql b/src/Bundle/test/config/db.sql index d322ec3a..b74a4009 100644 Binary files a/src/Bundle/test/config/db.sql and b/src/Bundle/test/config/db.sql differ diff --git a/src/Bundle/test/config/packages/doctrine.yaml b/src/Bundle/test/config/packages/doctrine.yaml index 7843b01d..0cb15ea4 100644 --- a/src/Bundle/test/config/packages/doctrine.yaml +++ b/src/Bundle/test/config/packages/doctrine.yaml @@ -12,3 +12,8 @@ doctrine: dir: '%kernel.project_dir%/config/doctrine' prefix: 'App\Entity' alias: App + BoardGameBlog: + is_bundle: false + type: attribute + dir: '%kernel.project_dir%/src/BoardGameBlog/Domain' + prefix: 'App\BoardGameBlog\Domain' diff --git a/src/Bundle/test/config/packages/fos_rest.yaml b/src/Bundle/test/config/packages/fos_rest.yaml index 009df60e..fd03639a 100644 --- a/src/Bundle/test/config/packages/fos_rest.yaml +++ b/src/Bundle/test/config/packages/fos_rest.yaml @@ -5,4 +5,5 @@ fos_rest: empty_content: 204 format_listener: rules: + - { path: '^/admin/*', priorities: ['html'], fallback_format: html } - { path: '^/', priorities: ['json'], fallback_format: json, prefer_extension: true } diff --git a/src/Bundle/test/config/packages/messenger.yaml b/src/Bundle/test/config/packages/messenger.yaml new file mode 100644 index 00000000..3314c6f2 --- /dev/null +++ b/src/Bundle/test/config/packages/messenger.yaml @@ -0,0 +1,11 @@ +framework: + messenger: + default_bus: command.bus + buses: + command.bus: ~ + query.bus: ~ + transports: + sync: 'sync://' + routing: + 'App\Shared\Application\Query\QueryInterface': 'sync' + 'App\Shared\Application\Command\CommandInterface': 'sync' diff --git a/src/Bundle/test/config/packages/sylius_grid.yaml b/src/Bundle/test/config/packages/sylius_grid.yaml new file mode 100644 index 00000000..d6e974ca --- /dev/null +++ b/src/Bundle/test/config/packages/sylius_grid.yaml @@ -0,0 +1,6 @@ +sylius_grid: + templates: + action: + delete: 'grid/action/delete.html.twig' + show: 'grid/action/show.html.twig' + update: 'grid/action/update.html.twig' diff --git a/src/Bundle/test/config/routes/sylius_resource.yaml b/src/Bundle/test/config/routes/sylius_resource.yaml new file mode 100644 index 00000000..0c8ade2c --- /dev/null +++ b/src/Bundle/test/config/routes/sylius_resource.yaml @@ -0,0 +1,7 @@ +sylius_crud_routes: + resource: 'sylius.routing.loader.crud_routes_attributes' + type: service + +sylius_routes: + resource: 'sylius.routing.loader.routes_attributes' + type: service diff --git a/src/Bundle/test/config/services.yaml b/src/Bundle/test/config/services.yaml index c6ecb992..ed587c67 100644 --- a/src/Bundle/test/config/services.yaml +++ b/src/Bundle/test/config/services.yaml @@ -24,3 +24,9 @@ services: public: true App\Filter\NationalityFilter: null + + App\Shared\: + resource: '../src/Shared' + + App\BoardGameBlog\: + resource: '../src/BoardGameBlog' diff --git a/src/Bundle/test/config/sylius/resources.yaml b/src/Bundle/test/config/sylius/resources.yaml index 0b889b09..95ff5f66 100644 --- a/src/Bundle/test/config/sylius/resources.yaml +++ b/src/Bundle/test/config/sylius/resources.yaml @@ -1,5 +1,14 @@ sylius_resource: + mapping: + paths: + - '%kernel.project_dir%/src/BoardGameBlog/Infrastructure/Sylius/Resource' + resources: + app.board_game: + driver: false + classes: + model: App\BoardGameBlog\Infrastructure\Sylius\Resource\BoardGameResource + app.book: classes: model: App\Entity\Book diff --git a/src/Bundle/test/public/index.php b/src/Bundle/test/public/index.php new file mode 100644 index 00000000..bb146611 --- /dev/null +++ b/src/Bundle/test/public/index.php @@ -0,0 +1,49 @@ +getParameterOption(['--env', '-e'], null, true)) { + putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); +} + +if ($input->hasParameterOption('--no-debug', true)) { + putenv('APP_DEBUG=' . $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); +} + +require dirname(__DIR__) . '/config/bootstrap.php'; + +if ($_SERVER['APP_DEBUG']) { + umask(0000); + + if (class_exists(Debug::class)) { + Debug::enable(); + } +} + +$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); +$request = Request::createFromGlobals(); +$response = $kernel->handle($request); +$response->send(); +$kernel->terminate($request, $response); diff --git a/src/Bundle/test/src/BoardGameBlog/Application/Command/CreateBoardGameCommand.php b/src/Bundle/test/src/BoardGameBlog/Application/Command/CreateBoardGameCommand.php new file mode 100644 index 00000000..bae31b48 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Application/Command/CreateBoardGameCommand.php @@ -0,0 +1,25 @@ +name, + ); + + $this->boardGameRepository->save($boardGame); + + return $boardGame; + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Application/Command/DeleteBoardGameCommand.php b/src/Bundle/test/src/BoardGameBlog/Application/Command/DeleteBoardGameCommand.php new file mode 100644 index 00000000..b9e6b20a --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Application/Command/DeleteBoardGameCommand.php @@ -0,0 +1,25 @@ +boardGameRepository->ofId($command->id)) { + return; + } + + $this->boardGameRepository->remove($boardGame); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Application/Command/UpdateBoardGameCommand.php b/src/Bundle/test/src/BoardGameBlog/Application/Command/UpdateBoardGameCommand.php new file mode 100644 index 00000000..870375cd --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Application/Command/UpdateBoardGameCommand.php @@ -0,0 +1,27 @@ +boardGameRepository->ofId($command->id); + if (null === $boardGame) { + throw new MissingBoardGameException($command->id); + } + + $boardGame->update( + name: $command->name, + ); + + $this->boardGameRepository->save($boardGame); + + return $boardGame; + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Application/Query/FindBoardGameQuery.php b/src/Bundle/test/src/BoardGameBlog/Application/Query/FindBoardGameQuery.php new file mode 100644 index 00000000..9e6cdb46 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Application/Query/FindBoardGameQuery.php @@ -0,0 +1,25 @@ +repository->ofId($query->id); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Domain/Exception/MissingBoardGameException.php b/src/Bundle/test/src/BoardGameBlog/Domain/Exception/MissingBoardGameException.php new file mode 100644 index 00000000..ddbe3026 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Domain/Exception/MissingBoardGameException.php @@ -0,0 +1,24 @@ +id = new BoardGameId(); + } + + public function update( + ?BoardGameName $name = null, + ): void { + $this->name = $name ?? $this->name; + } + + public function id(): BoardGameId + { + return $this->id; + } + + public function name(): BoardGameName + { + return $this->name; + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Domain/Repository/BoardGameRepositoryInterface.php b/src/Bundle/test/src/BoardGameBlog/Domain/Repository/BoardGameRepositoryInterface.php new file mode 100644 index 00000000..5b20636e --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Domain/Repository/BoardGameRepositoryInterface.php @@ -0,0 +1,30 @@ + + */ +interface BoardGameRepositoryInterface extends RepositoryInterface +{ + public function save(BoardGame $boardGame): void; + + public function remove(BoardGame $boardGame): void; + + public function ofId(BoardGameId $id): ?BoardGame; +} diff --git a/src/Bundle/test/src/BoardGameBlog/Domain/ValueObject/BoardGameId.php b/src/Bundle/test/src/BoardGameBlog/Domain/ValueObject/BoardGameId.php new file mode 100644 index 00000000..d72650fe --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Domain/ValueObject/BoardGameId.php @@ -0,0 +1,23 @@ +value = $value; + } + + public function __toString(): string + { + return $this->value; + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Doctrine/DoctrineBoardGameRepository.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Doctrine/DoctrineBoardGameRepository.php new file mode 100644 index 00000000..abfa6575 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Doctrine/DoctrineBoardGameRepository.php @@ -0,0 +1,46 @@ +getEntityManager()->persist($boardGame); + $this->getEntityManager()->flush(); + $this->getEntityManager()->refresh($boardGame); + } + + public function remove(BoardGame $boardGame): void + { + $this->getEntityManager()->remove($boardGame); + $this->getEntityManager()->flush(); + } + + public function ofId(BoardGameId $id): ?BoardGame + { + return $this->find($id->value); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Grid/BoardGameGrid.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Grid/BoardGameGrid.php new file mode 100644 index 00000000..0ce24571 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Grid/BoardGameGrid.php @@ -0,0 +1,66 @@ +setProvider(BoardGameGridProvider::class) + ->orderBy('name', 'asc') + ->addField( + StringField::create('name') + ->setLabel('Name') + ->setSortable(true), + ) + ->addActionGroup( + MainActionGroup::create( + CreateAction::create(), + ), + ) + ->addActionGroup( + ItemActionGroup::create( + ShowAction::create(), + UpdateAction::create(), + DeleteAction::create(), + ), + ) + ; + } + + public function getResourceClass(): string + { + return BoardGameResource::class; + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Grid/Provider/BoardGameGridProvider.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Grid/Provider/BoardGameGridProvider.php new file mode 100644 index 00000000..bb74db05 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Grid/Provider/BoardGameGridProvider.php @@ -0,0 +1,52 @@ +boardGameRepository->createQueryBuilder('o'); + $page = (int) $parameters->get('page', 1); + + $paginator = new Pagerfanta( + new QueryAdapter($queryBuilder), + ); + $paginator->setNormalizeOutOfRangePages(true); + $paginator->setCurrentPage($page > 0 ? $page : 1); + $paginator->setMaxPerPage($itemsPerPage); + + $resources = []; + foreach ($paginator as $model) { + $resources[] = BoardGameResource::fromModel($model); + } + + return new Pagerfanta(new ArrayAdapter($resources)); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Resource/BoardGameResource.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Resource/BoardGameResource.php new file mode 100644 index 00000000..fe7a0f25 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/Resource/BoardGameResource.php @@ -0,0 +1,82 @@ +id()->value, + $boardGame->name()->value, + ); + } + + public function getId(): ?string + { + return $this->id?->__toString(); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/CreateBoardGameProcessor.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/CreateBoardGameProcessor.php new file mode 100644 index 00000000..c2bb4179 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/CreateBoardGameProcessor.php @@ -0,0 +1,51 @@ +name); + + $command = new CreateBoardGameCommand( + new BoardGameName($data->name), + ); + + /** @var BoardGame $model */ + $model = $this->commandBus->dispatch($command); + + return BoardGameResource::fromModel($model); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/DeleteBoardGameProcessor.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/DeleteBoardGameProcessor.php new file mode 100644 index 00000000..4f6555ac --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/DeleteBoardGameProcessor.php @@ -0,0 +1,40 @@ +commandBus->dispatch(new DeleteBoardGameCommand(new BoardGameId($data->id))); + + return null; + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/UpdateBoardGameProcessor.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/UpdateBoardGameProcessor.php new file mode 100644 index 00000000..6e98c729 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Processor/UpdateBoardGameProcessor.php @@ -0,0 +1,49 @@ +id), + null !== $data->name ? new BoardGameName($data->name) : null, + ); + + $model = $this->commandBus->dispatch($command); + + return BoardGameResource::fromModel($model); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameCollectionProvider.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameCollectionProvider.php new file mode 100644 index 00000000..a3b93681 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameCollectionProvider.php @@ -0,0 +1,31 @@ +requestGridProvider->provide($operation, $context); + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameItemProvider.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameItemProvider.php new file mode 100644 index 00000000..98af8ab9 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Sylius/State/Http/Provider/BoardGameItemProvider.php @@ -0,0 +1,45 @@ +get(RequestOption::class)?->request(); + Assert::notNull($request); + + $id = (string) $request->attributes->get('id'); + + $model = $this->queryBus->ask(new FindBoardGameQuery(new BoardGameId(Uuid::fromString($id)))); + + return null !== $model ? BoardGameResource::fromModel($model) : null; + } +} diff --git a/src/Bundle/test/src/BoardGameBlog/Infrastructure/Symfony/Form/Type/BoardGameType.php b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Symfony/Form/Type/BoardGameType.php new file mode 100644 index 00000000..654e2318 --- /dev/null +++ b/src/Bundle/test/src/BoardGameBlog/Infrastructure/Symfony/Form/Type/BoardGameType.php @@ -0,0 +1,36 @@ +add('name') + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => BoardGameResource::class, + ]); + } +} diff --git a/src/Bundle/test/src/Entity/Author.php b/src/Bundle/test/src/Entity/Author.php index 3e8f6fa7..3e81efe7 100644 --- a/src/Bundle/test/src/Entity/Author.php +++ b/src/Bundle/test/src/Entity/Author.php @@ -25,12 +25,14 @@ class Author implements ResourceInterface { /** * @Serializer\Expose + * * @Serializer\Type("integer") */ private ?int $id = null; /** * @Serializer\Expose + * * @Serializer\Type("string") */ private ?string $name = null; diff --git a/src/Bundle/test/src/Entity/Book.php b/src/Bundle/test/src/Entity/Book.php index c1384eb3..09cb6df6 100644 --- a/src/Bundle/test/src/Entity/Book.php +++ b/src/Bundle/test/src/Entity/Book.php @@ -32,12 +32,14 @@ class Book implements ResourceInterface /** * @Serializer\Expose + * * @Serializer\Type("integer") */ private ?int $id = null; /** * @Serializer\Expose + * * @Serializer\Type("string") */ private ?string $title = null; diff --git a/src/Bundle/test/src/Entity/Nationality.php b/src/Bundle/test/src/Entity/Nationality.php index 0a65ce70..da6a2492 100644 --- a/src/Bundle/test/src/Entity/Nationality.php +++ b/src/Bundle/test/src/Entity/Nationality.php @@ -23,12 +23,14 @@ class Nationality implements ResourceInterface { /** * @Serializer\Expose + * * @Serializer\Type("integer") */ private ?int $id = null; /** * @Serializer\Expose + * * @Serializer\Type("string") */ private ?string $name = null; diff --git a/src/Bundle/test/src/Entity/Price.php b/src/Bundle/test/src/Entity/Price.php index 4a7ace52..e7a47717 100644 --- a/src/Bundle/test/src/Entity/Price.php +++ b/src/Bundle/test/src/Entity/Price.php @@ -22,12 +22,14 @@ final class Price { /** * @Serializer\Expose + * * @Serializer\Type("integer") */ private ?int $amount = null; /** * @Serializer\Expose + * * @Serializer\Type("string") */ private ?string $currencyCode = null; diff --git a/src/Bundle/test/src/Kernel.php b/src/Bundle/test/src/Kernel.php index b268e2d9..5ea914b1 100644 --- a/src/Bundle/test/src/Kernel.php +++ b/src/Bundle/test/src/Kernel.php @@ -13,10 +13,24 @@ namespace App; +use App\Shared\Application\Command\CommandHandlerInterface; +use App\Shared\Application\Query\QueryHandlerInterface; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel as BaseKernel; class Kernel extends BaseKernel { use MicroKernelTrait; + + protected function build(ContainerBuilder $container): void + { + $container->registerForAutoconfiguration(QueryHandlerInterface::class) + ->addTag('messenger.message_handler', ['bus' => 'query.bus']) + ; + + $container->registerForAutoconfiguration(CommandHandlerInterface::class) + ->addTag('messenger.message_handler', ['bus' => 'command.bus']) + ; + } } diff --git a/src/Bundle/test/src/Shared/Application/Command/CommandBusInterface.php b/src/Bundle/test/src/Shared/Application/Command/CommandBusInterface.php new file mode 100644 index 00000000..716cad3b --- /dev/null +++ b/src/Bundle/test/src/Shared/Application/Command/CommandBusInterface.php @@ -0,0 +1,19 @@ + + */ +interface PaginatorInterface extends \IteratorAggregate, \Countable +{ + public function getCurrentPage(): int; + + public function getItemsPerPage(): int; + + public function getLastPage(): int; + + public function getTotalItems(): int; +} diff --git a/src/Bundle/test/src/Shared/Domain/Repository/RepositoryInterface.php b/src/Bundle/test/src/Shared/Domain/Repository/RepositoryInterface.php new file mode 100644 index 00000000..008ea8f2 --- /dev/null +++ b/src/Bundle/test/src/Shared/Domain/Repository/RepositoryInterface.php @@ -0,0 +1,18 @@ +value = $value ?? Uuid::v4(); + } + + public function __toString(): string + { + return (string) $this->value; + } +} diff --git a/src/Bundle/test/src/Shared/Infrastructure/Symfony/Messenger/MessengerCommandBus.php b/src/Bundle/test/src/Shared/Infrastructure/Symfony/Messenger/MessengerCommandBus.php new file mode 100644 index 00000000..ad2a5559 --- /dev/null +++ b/src/Bundle/test/src/Shared/Infrastructure/Symfony/Messenger/MessengerCommandBus.php @@ -0,0 +1,42 @@ +messageBus = $commandBus; + } + + public function dispatch(CommandInterface $command): mixed + { + try { + return $this->handle($command); + } catch (HandlerFailedException $e) { + /** @var array{0: \Throwable} $exceptions */ + $exceptions = $e->getNestedExceptions(); + + throw $exceptions[0]; + } + } +} diff --git a/src/Bundle/test/src/Shared/Infrastructure/Symfony/Messenger/MessengerQueryBus.php b/src/Bundle/test/src/Shared/Infrastructure/Symfony/Messenger/MessengerQueryBus.php new file mode 100644 index 00000000..8413bc14 --- /dev/null +++ b/src/Bundle/test/src/Shared/Infrastructure/Symfony/Messenger/MessengerQueryBus.php @@ -0,0 +1,42 @@ +messageBus = $queryBus; + } + + public function ask(QueryInterface $query): mixed + { + try { + return $this->handle($query); + } catch (HandlerFailedException $e) { + /** @var array{0: \Throwable} $exceptions */ + $exceptions = $e->getNestedExceptions(); + + throw $exceptions[0]; + } + } +} diff --git a/src/Bundle/test/templates/board_game/show.html.twig b/src/Bundle/test/templates/board_game/show.html.twig new file mode 100644 index 00000000..8e2b5fa8 --- /dev/null +++ b/src/Bundle/test/templates/board_game/show.html.twig @@ -0,0 +1,4 @@ +

Science book

+ +

ID: {{ resource.id }}

+Name: {{ resource.name }} diff --git a/src/Bundle/test/templates/crud/create.html.twig b/src/Bundle/test/templates/crud/create.html.twig new file mode 100644 index 00000000..b772b2ac --- /dev/null +++ b/src/Bundle/test/templates/crud/create.html.twig @@ -0,0 +1,7 @@ +

{{ (operation.resource.applicationName ~ '.ui.new_' ~ operation.resource.name)|trans }}

+ +{{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }} +{{ form_widget(form) }} +
+ +{{ form_end(form) }} diff --git a/src/Bundle/test/templates/crud/index.html.twig b/src/Bundle/test/templates/crud/index.html.twig new file mode 100644 index 00000000..00bcf1f0 --- /dev/null +++ b/src/Bundle/test/templates/crud/index.html.twig @@ -0,0 +1,34 @@ +

{{ (operation.resource.applicationName ~ '.ui.' ~ operation.resource.pluralName)|trans }}

+ +{% set grid = resources %} +{% set definition = grid.definition %} + + + + + {% for field in definition.fields %} + {% if field.enabled %} + + {% endif %} + {% if definition.actionGroups.item is defined and definition.getEnabledActions('item')|length > 0 %} + + {% endif %} + {% endfor %} + + + + {% for resource in resources.data %} + + {% for field in definition.enabledFields %} + + + {% endfor %} + + {% endfor %} + + +
{{ field.label|trans }}Actions
{{ sylius_grid_render_field(grid, field, resource) }} + {% for action in definition.getEnabledActions('item') %} + {{ sylius_grid_render_action(grid, action, resource) }} + {% endfor %} +
diff --git a/src/Bundle/test/templates/crud/update.html.twig b/src/Bundle/test/templates/crud/update.html.twig new file mode 100644 index 00000000..f8a06e56 --- /dev/null +++ b/src/Bundle/test/templates/crud/update.html.twig @@ -0,0 +1,8 @@ +

{{ (operation.resource.applicationName ~ '.ui.edit_' ~ operation.resource.name)|trans }}

+ +{{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }} + +{{ form_widget(form) }} +
+ +{{ form_end(form) }} diff --git a/src/Bundle/test/templates/grid/action/delete.html.twig b/src/Bundle/test/templates/grid/action/delete.html.twig new file mode 100644 index 00000000..e40ec82a --- /dev/null +++ b/src/Bundle/test/templates/grid/action/delete.html.twig @@ -0,0 +1,9 @@ +{% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('delete')), options.link.parameters|default({'id': data.id}))) %} + + + + + +
diff --git a/src/Bundle/test/templates/grid/action/show.html.twig b/src/Bundle/test/templates/grid/action/show.html.twig new file mode 100644 index 00000000..b3961e64 --- /dev/null +++ b/src/Bundle/test/templates/grid/action/show.html.twig @@ -0,0 +1,3 @@ +{% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('show')), options.link.parameters|default({'id': data.id}))) %} + +Show diff --git a/src/Bundle/test/templates/grid/action/update.html.twig b/src/Bundle/test/templates/grid/action/update.html.twig new file mode 100644 index 00000000..e82376f0 --- /dev/null +++ b/src/Bundle/test/templates/grid/action/update.html.twig @@ -0,0 +1,3 @@ +{% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('update')), options.link.parameters|default({'id': data.id}))) %} + +Edit diff --git a/src/Bundle/test/translations/messages.en.yaml b/src/Bundle/test/translations/messages.en.yaml new file mode 100644 index 00000000..5756b2da --- /dev/null +++ b/src/Bundle/test/translations/messages.en.yaml @@ -0,0 +1,5 @@ +app: + ui: + board_games: Board games + edit_board_game: Edit board game + new_board_game: New board game diff --git a/src/Component/Data/Provider.php b/src/Component/Data/Provider.php new file mode 100644 index 00000000..d2efdcf9 --- /dev/null +++ b/src/Component/Data/Provider.php @@ -0,0 +1,49 @@ +getProvider(); + + if (null === $provider) { + return null; + } + + if (\is_callable($provider)) { + return $provider($grid, $parameters); + } + + if (!$this->locator->has($provider)) { + throw new \RuntimeException(sprintf('Provider "%s" not found on grid "%s"', $provider, $grid->getCode())); + } + + $providerInstance = $this->locator->get($provider); + Assert::isInstanceOf($providerInstance, DataProviderInterface::class); + + return $providerInstance->getData($grid, $parameters); + } +} diff --git a/src/Component/Definition/ArrayToDefinitionConverter.php b/src/Component/Definition/ArrayToDefinitionConverter.php index 89b9604f..0c439bf6 100644 --- a/src/Component/Definition/ArrayToDefinitionConverter.php +++ b/src/Component/Definition/ArrayToDefinitionConverter.php @@ -35,6 +35,10 @@ public function convert(string $code, array $configuration): Grid $configuration['driver']['options'], ); + if (array_key_exists('provider', $configuration)) { + $grid->setProvider($configuration['provider']); + } + if (array_key_exists('sorting', $configuration)) { $grid->setSorting($configuration['sorting']); } diff --git a/src/Component/Definition/Grid.php b/src/Component/Definition/Grid.php index f819996d..b5a1012f 100644 --- a/src/Component/Definition/Grid.php +++ b/src/Component/Definition/Grid.php @@ -24,6 +24,9 @@ class Grid /** @var array */ private $driverConfiguration; + /** @var string|callable|null */ + private $provider; + /** @var array */ private $sorting = []; @@ -71,6 +74,16 @@ public function setDriverConfiguration(array $driverConfiguration): void $this->driverConfiguration = $driverConfiguration; } + public function getProvider(): string|callable|null + { + return $this->provider; + } + + public function setProvider(string|callable|null $provider): void + { + $this->provider = $provider; + } + public function getSorting(): array { return $this->sorting;