2023-04-15 00:43:55 +02:00

233 lines
7.1 KiB
PHP

<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\CRUD\Routing;
use RuntimeException;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use function array_filter;
use function array_keys;
use function array_search;
use function count;
use function in_array;
use const ARRAY_FILTER_USE_BOTH;
class CRUDRoutesLoader extends Loader
{
private const ALL_INDEX_METHODS = [Request::METHOD_GET, Request::METHOD_HEAD];
private const ALL_SINGLE_METHODS = [
Request::METHOD_GET,
Request::METHOD_POST,
Request::METHOD_PUT,
Request::METHOD_DELETE,
];
protected array $apiCrudConfig = [];
protected array $crudConfig = [];
private bool $isLoaded = false;
public function __construct(array $crudConfig, array $apiCrudConfig)
{
$this->crudConfig = $crudConfig;
$this->apiCrudConfig = $apiCrudConfig;
parent::__construct();
}
/**
* Load routes for CRUD and CRUD Api.
*
* @param mixed $resource
* @param mixed|null $type
*/
public function load($resource, $type = null): RouteCollection
{
if (true === $this->isLoaded) {
throw new RuntimeException('Do not add the "CRUD" loader twice');
}
$collection = new RouteCollection();
foreach ($this->crudConfig as $crudConfig) {
$collection->addCollection($this->loadCrudConfig($crudConfig));
}
foreach ($this->apiCrudConfig as $crudConfig) {
$collection->addCollection($this->loadApi($crudConfig));
}
return $collection;
}
/**
* @param mixed $resource
* @param null $type
*
* @return bool
*/
public function supports($resource, $type = null)
{
return 'CRUD' === $type;
}
/**
* Load routes for api single.
*
* @param $crudConfig
*/
protected function loadApi(array $crudConfig): RouteCollection
{
$collection = new RouteCollection();
$controller = 'csapi_' . $crudConfig['name'] . '_controller';
foreach ($crudConfig['actions'] as $name => $action) {
// filter only on single actions
$singleCollection = $action['single_collection'] ?? '_entity' === $name ? 'single' : null;
if ('collection' === $singleCollection) {
// continue;
}
// compute default action
switch ($name) {
case '_entity':
$controllerAction = 'entityApi';
break;
case '_index':
$controllerAction = 'indexApi';
break;
default:
$controllerAction = $name . 'Api';
break;
}
$defaults = [
'_controller' => $controller . ':' . ($action['controller_action'] ?? $controllerAction),
];
// path are rewritten
// if name === 'default', we rewrite it to nothing :-)
$localName = in_array($name, ['_entity', '_index'], true) ? '' : '/' . $name;
if ('collection' === $action['single_collection'] || '_index' === $name) {
$localPath = $action['path'] ?? $localName . '.{_format}';
} else {
$localPath = $action['path'] ?? '/{id}' . $localName . '.{_format}';
}
$path = $crudConfig['base_path'] . $localPath;
$requirements = $action['requirements'] ?? ['{id}' => '\d+'];
$methods = array_keys(array_filter(
$action['methods'],
static fn ($value, $key) => $value,
ARRAY_FILTER_USE_BOTH
));
if (count($methods) === 0) {
throw new RuntimeException("The api configuration named \"{$crudConfig['name']}\", action \"{$name}\", " .
'does not have any allowed methods. You should remove this action from the config ' .
'or allow, at least, one method');
}
if ('_entity' === $name && in_array(Request::METHOD_POST, $methods, true)) {
unset($methods[array_search(Request::METHOD_POST, $methods, true)]);
$entityPostRoute = $this->createEntityPostRoute(
$name,
$crudConfig,
$action,
$controller
);
$collection->add(
"chill_api_single_{$crudConfig['name']}_{$name}_create",
$entityPostRoute
);
}
if (count($methods) === 0) {
// the only method was POST,
// continue to next
continue;
}
$route = new Route($path, $defaults, $requirements);
$route->setMethods($methods);
$collection->add('chill_api_single_' . $crudConfig['name'] . '_' . $name, $route);
}
return $collection;
}
/**
* Load routes for CRUD (without api).
*
* @param $crudConfig
*/
protected function loadCrudConfig($crudConfig): RouteCollection
{
$collection = new RouteCollection();
$controller = 'cscrud_' . $crudConfig['name'] . '_controller';
foreach ($crudConfig['actions'] as $name => $action) {
// defaults (controller name)
$defaults = [
'_controller' => $controller . ':' . ($action['controller_action'] ?? $name),
];
if ('index' === $name) {
$path = '{_locale}' . $crudConfig['base_path'];
$route = new Route($path, $defaults);
} elseif ('new' === $name) {
$path = '{_locale}' . $crudConfig['base_path'] . '/' . $name;
$route = new Route($path, $defaults);
} else {
$path = '{_locale}' . $crudConfig['base_path'] . ($action['path'] ?? '/{id}/' . $name);
$requirements = $action['requirements'] ?? [
'{id}' => '\d+',
];
$route = new Route($path, $defaults, $requirements);
}
$collection->add('chill_crud_' . $crudConfig['name'] . '_' . $name, $route);
}
return $collection;
}
private function createEntityPostRoute(string $name, $crudConfig, array $action, $controller): Route
{
$localPath = $action['path'] . '.{_format}';
$defaults = [
'_controller' => $controller . ':' . ($action['controller_action'] ?? 'entityPost'),
];
$path = $crudConfig['base_path'] . $localPath;
$requirements = $action['requirements'] ?? [];
$route = new Route($path, $defaults, $requirements);
$route->setMethods([Request::METHOD_POST]);
return $route;
}
}