Skip to content

Commit 7caf6f9

Browse files
committed
Handle custom operations
1 parent 62e635f commit 7caf6f9

File tree

8 files changed

+246
-2
lines changed

8 files changed

+246
-2
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/resource_metadata_collection.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
<argument type="service" id="service_container" />
2323
</service>
2424

25+
<service id="sylius.operation.metadata_updater.custom_php_file" class="Sylius\Resource\Metadata\Operation\CustomPhpFileOperationUpdater">
26+
<argument>%sylius.resource.mapping%</argument>
27+
<argument type="service" id="service_container" />
28+
</service>
29+
2530
<service id="sylius.resource_metadata_collection.factory" alias="sylius.resource_metadata_collection.factory.php_file" />
2631
<service id="Sylius\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface" alias="sylius.resource_metadata_collection.factory.php_file" />
2732

@@ -41,6 +46,15 @@
4146
<argument type="service" id=".inner" />
4247
</service>
4348

49+
<service id="sylius.resource_metadata_collection.factory.custom"
50+
class="Sylius\Resource\Metadata\Resource\Factory\CustomResourceMetadataCollectionFactory"
51+
decorates="sylius.resource_metadata_collection.factory"
52+
decoration-priority="350"
53+
>
54+
<argument type="service" id="sylius.operation.metadata_updater.custom_php_file" />
55+
<argument type="service" id=".inner" />
56+
</service>
57+
4458
<service id="sylius.resource_metadata_collection.factory.state_machine"
4559
class="Sylius\Resource\Metadata\Resource\Factory\StateMachineResourceMetadataCollectionFactory"
4660
decorates="sylius.resource_metadata_collection.factory.attributes"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
use Symfony\Component\Finder\Finder;
18+
19+
final class CustomPhpFileOperationUpdater implements OperationUpdaterInterface
20+
{
21+
public function __construct(
22+
private readonly array $resourceMapping,
23+
) {
24+
}
25+
26+
/**
27+
* @inheritDoc
28+
*/
29+
public function update(Operation $operation): Operation
30+
{
31+
foreach ($this->getResourceFilePaths() as $filePath) {
32+
if (!is_readable($filePath)) {
33+
continue;
34+
}
35+
36+
$resource = $this->getPHPFileClosure($filePath)();
37+
38+
if (!$resource instanceof \Closure) {
39+
continue;
40+
}
41+
42+
$resourceReflection = new \ReflectionFunction($resource);
43+
44+
if (1 !== $resourceReflection->getNumberOfParameters()) {
45+
continue;
46+
}
47+
48+
$firstParameterType = ($resourceReflection->getParameters()[0] ?? null)?->getType();
49+
50+
if (!$firstParameterType instanceof \ReflectionNamedType) {
51+
continue;
52+
}
53+
54+
// Check if the closure parameter is an operation
55+
if (!is_a($firstParameterType->getName(), Operation::class, true)) {
56+
continue;
57+
}
58+
59+
$operation = $resource($operation);
60+
}
61+
62+
return $operation;
63+
}
64+
65+
private function getResourceFilePaths(): iterable
66+
{
67+
foreach ($this->createFinder() as $file) {
68+
yield $file->getPathname();
69+
}
70+
}
71+
72+
private function createFinder(): Finder
73+
{
74+
$finder = (new Finder())->files();
75+
76+
foreach ($this->resourceMapping['imports'] ?? [] as $path) {
77+
$finder->in($path);
78+
}
79+
80+
return $finder->files();
81+
}
82+
83+
/**
84+
* Scope isolated include.
85+
*
86+
* Prevents access to $this/self from included files.
87+
*/
88+
private function getPHPFileClosure(string $filePath): \Closure
89+
{
90+
return \Closure::bind(function () use ($filePath): mixed {
91+
return require $filePath;
92+
}, null, null);
93+
}
94+
}
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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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\Metadata\ResourceMetadata;
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+
/** @var ResourceMetadata $resourceMetadata */
43+
foreach ($resourceMetadataCollection as $resourceMetadata) {
44+
$operations = $resourceMetadata->getOperations() ?? new Operations();
45+
46+
/** @var Operation $operation */
47+
foreach ($operations as $operation) {
48+
$operationName = $operation->getName();
49+
Assert::notNull($operationName);
50+
$operations->add($operationName, $this->metadataUpdater->update($operation));
51+
}
52+
53+
$newMetadataCollection[] = $resourceMetadata->withOperations($operations);
54+
}
55+
56+
return $newMetadataCollection;
57+
}
58+
}

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)