Skip to content

Commit 19d2b8f

Browse files
committed
Handle custom operations
1 parent 4530671 commit 19d2b8f

File tree

9 files changed

+249
-4
lines changed

9 files changed

+249
-4
lines changed

psalm.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
<InvalidNullableReturnType>
121121
<errorLevel type="suppress">
122122
<file name="src/Component/src/Metadata/Extractor/PhpFileMetadataExtractor.php" />
123+
<file name="src/Component/src/Metadata/Operation/CustomPhpFileOperationUpdater.php" />
123124
</errorLevel>
124125
</InvalidNullableReturnType>
125126

@@ -188,6 +189,7 @@
188189
<NullableReturnStatement>
189190
<errorLevel type="suppress">
190191
<file name="src/Component/src/Metadata/Extractor/PhpFileMetadataExtractor.php" />
192+
<file name="src/Component/src/Metadata/Operation/CustomPhpFileOperationUpdater.php" />
191193
</errorLevel>
192194
</NullableReturnStatement>
193195

@@ -336,6 +338,7 @@
336338
<UnresolvableInclude>
337339
<errorLevel type="suppress">
338340
<file name="src/Component/src/Metadata/Extractor/PhpFileMetadataExtractor.php" />
341+
<file name="src/Component/src/Metadata/Operation/CustomPhpFileOperationUpdater.php" />
339342
</errorLevel>
340343
</UnresolvableInclude>
341344

src/Bundle/Resources/config/services/metadata.xml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
<argument type="service" id="service_container" />
2727
</service>
2828

29+
<service id="sylius.operation.metadata_updater.custom_php_file" class="Sylius\Resource\Metadata\Operation\CustomPhpFileOperationUpdater">
30+
<argument>%sylius.resource.mapping%</argument>
31+
<argument type="service" id="service_container" />
32+
</service>
33+
2934
<service id="sylius.resource_metadata_collection.factory" alias="sylius.resource_metadata_collection.factory.php_file" />
3035
<service id="Sylius\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface" alias="sylius.resource_metadata_collection.factory.php_file" />
3136

@@ -45,6 +50,15 @@
4550
<argument type="service" id=".inner" />
4651
</service>
4752

53+
<service id="sylius.resource_metadata_collection.factory.custom"
54+
class="Sylius\Resource\Metadata\Resource\Factory\CustomResourceMetadataCollectionFactory"
55+
decorates="sylius.resource_metadata_collection.factory"
56+
decoration-priority="350"
57+
>
58+
<argument type="service" id="sylius.operation.metadata_updater.custom_php_file" />
59+
<argument type="service" id=".inner" />
60+
</service>
61+
4862
<service id="sylius.resource_metadata_collection.factory.state_machine"
4963
class="Sylius\Resource\Metadata\Resource\Factory\StateMachineResourceMetadataCollectionFactory"
5064
decorates="sylius.resource_metadata_collection.factory"
@@ -104,7 +118,7 @@
104118
<service id="sylius.resource_metadata_collection.factory.cached"
105119
class="Sylius\Resource\Metadata\Resource\Factory\CachedResourceMetadataCollectionFactory"
106120
decorates="sylius.resource_metadata_collection.factory"
107-
decoration-priority="-10"
121+
decoration-priority="-20"
108122
>
109123
<argument type="service" id="sylius.cache.metadata.resource_collection" />
110124
<argument type="service" id=".inner" />

src/Bundle/Resources/config/services/routing/resource.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<services>
1616
<service id="sylius_resource.routing.resource.route_collection_factory" class="Sylius\Resource\Symfony\Routing\Factory\Resource\ResourceRouteCollectionFactory">
1717
<argument type="service" id="sylius.routing.factory.operation_route" />
18-
<argument type="service" id="sylius.resource_metadata_collection.factory.attributes" />
18+
<argument type="service" id="sylius.resource_metadata_collection.factory" />
1919
<argument type="service" id="sylius.resource_registry" />
2020
</service>
2121
<service id="Sylius\Resource\Symfony\Routing\Factory\Resource\ResourceRouteCollectionFactoryInterface" alias="sylius_resource.routing.resource.route_collection_factory" />
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Sylius\Resource\Metadata\Operation;
15+
16+
use Psr\Container\ContainerInterface;
17+
use Sylius\Resource\Metadata\Operation;
18+
use Symfony\Component\Finder\Finder;
19+
20+
final class CustomPhpFileOperationUpdater implements OperationUpdaterInterface
21+
{
22+
public function __construct(
23+
private readonly array $resourceMapping,
24+
private readonly ?ContainerInterface $container = null,
25+
) {
26+
}
27+
28+
/**
29+
* @inheritDoc
30+
*/
31+
public function update(Operation $operation): Operation
32+
{
33+
foreach ($this->getResourceFilePaths() as $filePath) {
34+
if (!is_readable($filePath)) {
35+
continue;
36+
}
37+
38+
$resource = $this->getPHPFileClosure($filePath)();
39+
40+
if (!$resource instanceof \Closure) {
41+
continue;
42+
}
43+
44+
$resourceReflection = new \ReflectionFunction($resource);
45+
46+
if (1 !== $resourceReflection->getNumberOfParameters()) {
47+
continue;
48+
}
49+
50+
$firstParameterType = ($resourceReflection->getParameters()[0] ?? null)?->getType();
51+
52+
if (!$firstParameterType instanceof \ReflectionNamedType) {
53+
continue;
54+
}
55+
56+
// Check if the closure parameter is an operation
57+
if (!is_a($firstParameterType->getName(), Operation::class, true)) {
58+
continue;
59+
}
60+
61+
$operation = $resource($operation);
62+
}
63+
64+
return $operation;
65+
}
66+
67+
private function getResourceFilePaths(): iterable
68+
{
69+
foreach ($this->createFinder() as $file) {
70+
yield $file->getPathname();
71+
}
72+
}
73+
74+
private function createFinder(): Finder
75+
{
76+
$finder = (new Finder())->files();
77+
78+
foreach ($this->resourceMapping['imports'] ?? [] as $path) {
79+
$finder->in($path);
80+
}
81+
82+
return $finder->files();
83+
}
84+
85+
/**
86+
* Scope isolated include.
87+
*
88+
* Prevents access to $this/self from included files.
89+
*/
90+
private function getPHPFileClosure(string $filePath): \Closure
91+
{
92+
return \Closure::bind(function () use ($filePath): mixed {
93+
return require $filePath;
94+
}, null, null);
95+
}
96+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Sylius\Resource\Metadata\Operation;
15+
16+
use Sylius\Resource\Metadata\Operation;
17+
18+
interface OperationUpdaterInterface
19+
{
20+
public function update(Operation $operation): Operation;
21+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Sylius\Resource\Metadata\Resource\Factory;
15+
16+
use Sylius\Resource\Metadata\Operation;
17+
use Sylius\Resource\Metadata\Operation\OperationUpdaterInterface;
18+
use Sylius\Resource\Metadata\Operations;
19+
use Sylius\Resource\Metadata\Resource\ResourceMetadataCollection;
20+
use Sylius\Resource\Symfony\Routing\Factory\RouteName\OperationRouteNameFactory;
21+
use Webmozart\Assert\Assert;
22+
23+
final class CustomResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface
24+
{
25+
use OperationDefaultsTrait;
26+
27+
public function __construct(
28+
private readonly OperationUpdaterInterface $metadataUpdater,
29+
private readonly ?ResourceMetadataCollectionFactoryInterface $decorated = null,
30+
) {
31+
}
32+
33+
public function create(string $resourceClass): ResourceMetadataCollection
34+
{
35+
$resourceMetadataCollection = new ResourceMetadataCollection();
36+
if ($this->decorated) {
37+
$resourceMetadataCollection = $this->decorated->create($resourceClass);
38+
}
39+
40+
$newMetadataCollection = new ResourceMetadataCollection();
41+
42+
foreach ($resourceMetadataCollection as $resourceMetadata) {
43+
$operations = $resourceMetadata->getOperations() ?? new Operations();
44+
45+
/** @var Operation $operation */
46+
foreach ($operations as $operation) {
47+
$operationName = $operation->getName();
48+
Assert::notNull($operationName);
49+
$operations->add($operationName, $this->metadataUpdater->update($operation));
50+
}
51+
52+
$newMetadataCollection[] = $resourceMetadata->withOperations($operations);
53+
}
54+
55+
return $newMetadataCollection;
56+
}
57+
}

src/Component/src/Symfony/Routing/Factory/Resource/ResourceRouteCollectionFactory.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ public function __construct(
3939
public function createRouteCollectionForClass(string $className): RouteCollection
4040
{
4141
$routeCollection = new RouteCollection();
42-
$resourceMetadata = $this->resourceMetadataFactory->create($className);
42+
43+
$resourceMetadataCollection = $this->resourceMetadataFactory->create($className);
4344

4445
/** @var ResourceMetadata $resource */
45-
foreach ($resourceMetadata->getIterator() as $resource) {
46+
foreach ($resourceMetadataCollection->getIterator() as $resource) {
4647
$this->createRoutesForResource($routeCollection, $resource);
4748
}
4849

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
use Sylius\Resource\Metadata\Create;
15+
use Sylius\Resource\Metadata\Operation;
16+
17+
return static function (Operation $operation): Operation {
18+
if (
19+
!$operation instanceof Create ||
20+
'app_admin_speaker_create' !== $operation->getName()
21+
) {
22+
return $operation;
23+
}
24+
25+
return $operation->withPath('speakers/register');
26+
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
use App\Conference\Entity\Speaker;
15+
use Sylius\Resource\Metadata\Operations;
16+
use Sylius\Resource\Metadata\ResourceMetadata;
17+
use Sylius\Resource\Metadata\Update;
18+
19+
return (new ResourceMetadata())
20+
->withRoutePrefix('/admin')
21+
->withClass(Speaker::class)
22+
->withSection('admin')
23+
->withTemplatesDir('crud')
24+
->withOperations(new Operations([
25+
new Update(),
26+
]))
27+
;

0 commit comments

Comments
 (0)