Skip to content

Commit

Permalink
Move routing into component
Browse files Browse the repository at this point in the history
  • Loading branch information
loic425 committed Oct 17, 2023
1 parent 3449806 commit 3bec94a
Show file tree
Hide file tree
Showing 20 changed files with 729 additions and 485 deletions.
56 changes: 3 additions & 53 deletions src/Bundle/Routing/AttributesOperationRouteFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,60 +13,10 @@

namespace Sylius\Bundle\ResourceBundle\Routing;

use Sylius\Component\Resource\Metadata\HttpOperation;
use Sylius\Component\Resource\Metadata\MetadataInterface;
use Sylius\Component\Resource\Metadata\Operations;
use Sylius\Component\Resource\Metadata\RegistryInterface;
use Sylius\Component\Resource\Metadata\Resource as ResourceMetadata;
use Sylius\Component\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use Sylius\Component\Resource\Symfony\Routing\Factory\OperationRouteFactoryInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Webmozart\Assert\Assert;
class_exists(\Sylius\Component\Resource\Symfony\Routing\Factory\AttributesOperationRouteFactory::class);

final class AttributesOperationRouteFactory implements AttributesOperationRouteFactoryInterface
{
public function __construct(
private RegistryInterface $resourceRegistry,
private OperationRouteFactoryInterface $operationRouteFactory,
private ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory,
) {
}

public function createRouteForClass(RouteCollection $routeCollection, string $className): void
{
$resourceMetadata = $this->resourceMetadataFactory->create($className);

/** @var ResourceMetadata $resource */
foreach ($resourceMetadata->getIterator() as $resource) {
$this->createRoutesForResource($routeCollection, $resource);
}
}

private function createRoutesForResource(RouteCollection $routeCollection, ResourceMetadata $resource): void
{
foreach ($resource->getOperations() ?? new Operations() as $operation) {
if (!$operation instanceof HttpOperation) {
continue;
}

$this->addRouteForOperation($routeCollection, $resource, $operation);
}
}

private function addRouteForOperation(RouteCollection $routeCollection, ResourceMetadata $resource, HttpOperation $operation): void
{
$metadata = $this->resourceRegistry->get($resource->getAlias() ?? '');
$routeName = $operation->getRouteName();

Assert::notNull($routeName, sprintf('Operation %s has no route name. Please define one.', $operation::class));

$route = $this->createRoute($metadata, $resource, $operation);
$routeCollection->add($routeName, $route);
}

private function createRoute(MetadataInterface $metadata, ResourceMetadata $resource, HttpOperation $operation): Route
if (false) {
final class AttributesOperationRouteFactory extends \Sylius\Component\Resource\Symfony\Routing\Factory\AttributesOperationRouteFactory
{
return $this->operationRouteFactory->create($metadata, $resource, $operation);
}
}
10 changes: 5 additions & 5 deletions src/Bundle/Routing/AttributesOperationRouteFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

namespace Sylius\Bundle\ResourceBundle\Routing;

use Symfony\Component\Routing\RouteCollection;
interface_exists(\Sylius\Component\Resource\Symfony\Routing\Factory\AttributesOperationRouteFactoryInterface::class);

interface AttributesOperationRouteFactoryInterface
{
/** @psalm-param class-string $className */
public function createRouteForClass(RouteCollection $routeCollection, string $className): void;
if (false) {
interface AttributesOperationRouteFactoryInterface extends \Sylius\Component\Resource\Symfony\Routing\Factory\AttributesOperationRouteFactoryInterface
{
}
}
42 changes: 3 additions & 39 deletions src/Bundle/Routing/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,10 @@

namespace Sylius\Bundle\ResourceBundle\Routing;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class_exists(\Sylius\Component\Resource\Symfony\Bundle\Routing\Configuration::class);

final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
if (false) {
final class Configuration extends \Sylius\Component\Resource\Symfony\Bundle\Routing\Configuration
{
$treeBuilder = new TreeBuilder('routing');
/** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode();

$rootNode
->children()
->scalarNode('alias')->isRequired()->cannotBeEmpty()->end()
->scalarNode('path')->defaultValue(null)->end()
->scalarNode('identifier')->defaultValue('id')->end()
->arrayNode('criteria')
->useAttributeAsKey('identifier')
->scalarPrototype()
->end()
->end()
->booleanNode('filterable')->end()
->variableNode('form')->cannotBeEmpty()->end()
->scalarNode('serialization_version')->cannotBeEmpty()->end()
->scalarNode('section')->cannotBeEmpty()->end()
->scalarNode('redirect')->cannotBeEmpty()->end()
->scalarNode('templates')->cannotBeEmpty()->end()
->scalarNode('grid')->cannotBeEmpty()->end()
->booleanNode('permission')->defaultValue(false)->end()
->arrayNode('except')
->scalarPrototype()->end()
->end()
->arrayNode('only')
->scalarPrototype()->end()
->end()
->variableNode('vars')->cannotBeEmpty()->end()
->end()
;

return $treeBuilder;
}
}
43 changes: 3 additions & 40 deletions src/Bundle/Routing/CrudRoutesAttributesLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,10 @@

namespace Sylius\Bundle\ResourceBundle\Routing;

use Sylius\Component\Resource\Annotation\SyliusCrudRoutes;
use Sylius\Component\Resource\Reflection\ClassReflection;
use Symfony\Bundle\FrameworkBundle\Routing\RouteLoaderInterface;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Yaml\Yaml;
class_exists(\Sylius\Component\Resource\Symfony\Bundle\Routing\Loader\CrudRoutesAttributesLoader::class);

final class CrudRoutesAttributesLoader implements RouteLoaderInterface
{
private array $mapping;

private ResourceLoader $resourceLoader;

public function __construct(
array $mapping,
ResourceLoader $resourceLoader,
) {
$this->mapping = $mapping;
$this->resourceLoader = $resourceLoader;
}

public function __invoke(): RouteCollection
{
$routeCollection = new RouteCollection();
$paths = $this->mapping['paths'] ?? [];

/** @var string $className */
foreach (ClassReflection::getResourcesByPaths($paths) as $className) {
$this->addRoutesForSyliusCrudRoutesAttributes($routeCollection, $className);
}

return $routeCollection;
}

private function addRoutesForSyliusCrudRoutesAttributes(RouteCollection $routeCollection, string $className): void
if (false) {
final class CrudRoutesAttributesLoader extends \Sylius\Component\Resource\Symfony\Bundle\Routing\Loader\CrudRoutesAttributesLoader
{
$attributes = ClassReflection::getClassAttributes($className, SyliusCrudRoutes::class);

foreach ($attributes as $reflectionAttribute) {
$resource = Yaml::dump($reflectionAttribute->getArguments());
$resourceRouteCollection = $this->resourceLoader->load($resource);
$routeCollection->addCollection($resourceRouteCollection);
}
}
}
183 changes: 3 additions & 180 deletions src/Bundle/Routing/ResourceLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,187 +13,10 @@

namespace Sylius\Bundle\ResourceBundle\Routing;

use Gedmo\Sluggable\Util\Urlizer;
use Sylius\Component\Resource\Metadata\MetadataInterface;
use Sylius\Component\Resource\Metadata\RegistryInterface;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Yaml\Yaml;
class_exists(\Sylius\Component\Resource\Symfony\Routing\Loader\ResourceLoader::class);

final class ResourceLoader implements LoaderInterface
{
private RegistryInterface $resourceRegistry;

private RouteFactoryInterface $routeFactory;

public function __construct(RegistryInterface $resourceRegistry, RouteFactoryInterface $routeFactory)
{
$this->resourceRegistry = $resourceRegistry;
$this->routeFactory = $routeFactory;
}

public function load($resource, $type = null): RouteCollection
{
$processor = new Processor();
$configurationDefinition = new Configuration();

$configuration = Yaml::parse($resource);
$configuration = $processor->processConfiguration($configurationDefinition, ['routing' => $configuration]);

if (!empty($configuration['only']) && !empty($configuration['except'])) {
throw new \InvalidArgumentException('You can configure only one of "except" & "only" options.');
}

$routesToGenerate = ['show', 'index', 'create', 'update', 'delete', 'bulkDelete'];

if (!empty($configuration['only'])) {
$routesToGenerate = $configuration['only'];
}
if (!empty($configuration['except'])) {
$routesToGenerate = array_diff($routesToGenerate, $configuration['except']);
}

$isApi = $type === 'sylius.resource_api';

/** @var MetadataInterface $metadata */
$metadata = $this->resourceRegistry->get($configuration['alias']);
$routes = $this->routeFactory->createRouteCollection();

$rootPath = sprintf('/%s/', $configuration['path'] ?? Urlizer::urlize($metadata->getPluralName()));
$identifier = sprintf('{%s}', $configuration['identifier']);

if (in_array('index', $routesToGenerate, true)) {
$indexRoute = $this->createRoute($metadata, $configuration, $rootPath, 'index', ['GET'], $isApi);
$routes->add($this->getRouteName($metadata, $configuration, 'index'), $indexRoute);
}

if (in_array('create', $routesToGenerate, true)) {
$createRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath : $rootPath . 'new', 'create', $isApi ? ['POST'] : ['GET', 'POST'], $isApi);
$routes->add($this->getRouteName($metadata, $configuration, 'create'), $createRoute);
}

if (in_array('update', $routesToGenerate, true)) {
$updateRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . $identifier : $rootPath . $identifier . '/edit', 'update', $isApi ? ['PUT', 'PATCH'] : ['GET', 'PUT', 'PATCH'], $isApi);
$routes->add($this->getRouteName($metadata, $configuration, 'update'), $updateRoute);
}

if (in_array('show', $routesToGenerate, true)) {
$showRoute = $this->createRoute($metadata, $configuration, $rootPath . $identifier, 'show', ['GET'], $isApi);
$routes->add($this->getRouteName($metadata, $configuration, 'show'), $showRoute);
}

if (!$isApi && in_array('bulkDelete', $routesToGenerate, true)) {
$bulkDeleteRoute = $this->createRoute($metadata, $configuration, $rootPath . 'bulk-delete', 'bulkDelete', ['DELETE'], $isApi);
$routes->add($this->getRouteName($metadata, $configuration, 'bulk_delete'), $bulkDeleteRoute);
}

if (in_array('delete', $routesToGenerate, true)) {
$deleteRoute = $this->createRoute($metadata, $configuration, $rootPath . $identifier, 'delete', ['DELETE'], $isApi);
$routes->add($this->getRouteName($metadata, $configuration, 'delete'), $deleteRoute);
}

return $routes;
}

public function supports($resource, $type = null): bool
{
return 'sylius.resource' === $type || 'sylius.resource_api' === $type;
}

/**
* @psalm-suppress InvalidReturnType Symfony docblocks are messing with us
*
* @return LoaderResolverInterface
*/
public function getResolver()
if (false) {
final class ResourceLoader extends \Sylius\Component\Resource\Symfony\Routing\Loader\ResourceLoader
{
// Intentionally left blank.
}

public function setResolver(LoaderResolverInterface $resolver): void
{
// Intentionally left blank.
}

private function createRoute(
MetadataInterface $metadata,
array $configuration,
string $path,
string $actionName,
array $methods,
bool $isApi = false,
): Route {
$defaults = [
'_controller' => $metadata->getServiceId('controller') . sprintf('::%sAction', $actionName),
];

if ($isApi && 'index' === $actionName) {
$defaults['_sylius']['serialization_groups'] = ['Default'];
}
if ($isApi && in_array($actionName, ['show', 'create', 'update'], true)) {
$defaults['_sylius']['serialization_groups'] = ['Default', 'Detailed'];
}
if ($isApi && 'delete' === $actionName) {
$defaults['_sylius']['csrf_protection'] = false;
}
if (isset($configuration['grid']) && 'index' === $actionName) {
$defaults['_sylius']['grid'] = $configuration['grid'];
}
if (isset($configuration['form']) && in_array($actionName, ['create', 'update'], true)) {
$defaults['_sylius']['form'] = $configuration['form'];
}
if (isset($configuration['serialization_version'])) {
$defaults['_sylius']['serialization_version'] = $configuration['serialization_version'];
}
if (isset($configuration['section'])) {
$defaults['_sylius']['section'] = $configuration['section'];
}
if (!empty($configuration['criteria'])) {
$defaults['_sylius']['criteria'] = $configuration['criteria'];
}
if (array_key_exists('filterable', $configuration)) {
$defaults['_sylius']['filterable'] = $configuration['filterable'];
}
if (isset($configuration['templates']) && in_array($actionName, ['show', 'index', 'create', 'update'], true)) {
$defaults['_sylius']['template'] = sprintf(
false === strpos($configuration['templates'], ':') ? '%s/%s.html.twig' : '%s:%s.html.twig',
$configuration['templates'],
$actionName,
);
}
if (isset($configuration['redirect']) && in_array($actionName, ['create', 'update'], true)) {
$defaults['_sylius']['redirect'] = $this->getRouteName($metadata, $configuration, $configuration['redirect']);
}
if (isset($configuration['permission'])) {
$defaults['_sylius']['permission'] = $configuration['permission'];
}
if (isset($configuration['vars']['all'])) {
$defaults['_sylius']['vars'] = $configuration['vars']['all'];
}

if (isset($configuration['vars'][$actionName])) {
$vars = $configuration['vars']['all'] ?? [];
$defaults['_sylius']['vars'] = array_merge($vars, $configuration['vars'][$actionName]);
}

if ($actionName === 'bulkDelete') {
$defaults['_sylius']['paginate'] = false;
$defaults['_sylius']['repository'] = [
'method' => 'findById',
'arguments' => ['$ids'],
];
}

return $this->routeFactory->createRoute($path, $defaults, [], [], '', [], $methods);
}

private function getRouteName(MetadataInterface $metadata, array $configuration, string $actionName): string
{
$sectionPrefix = isset($configuration['section']) ? $configuration['section'] . '_' : '';

return sprintf('%s_%s%s_%s', $metadata->getApplicationName(), $sectionPrefix, $metadata->getName(), $actionName);
}
}
Loading

0 comments on commit 3bec94a

Please sign in to comment.