Skip to content

Commit da5094c

Browse files
committed
PoC PHP file resource metadata factory
1 parent 69d39f1 commit da5094c

File tree

12 files changed

+342
-2
lines changed

12 files changed

+342
-2
lines changed

src/Bundle/DependencyInjection/Configuration.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public function getConfigTreeBuilder(): TreeBuilder
3939
->arrayNode('mapping')
4040
->addDefaultsIfNotSet()
4141
->children()
42+
->arrayNode('imports')
43+
->prototype('scalar')->end()
44+
->end()
4245
->arrayNode('paths')
4346
->prototype('scalar')->end()
4447
->end()

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,28 @@
2121
<tag name="cache.pool" />
2222
</service>
2323

24+
<service id="sylius_resource.metadata.extractor.php_file" class="Sylius\Resource\Metadata\Extractor\PhpFileMetadataExtractor">
25+
<argument>%sylius.resource.mapping%</argument>
26+
</service>
27+
28+
<service id="sylius.resource_metadata_collection.factory" alias="sylius.resource_metadata_collection.factory.php_file" />
29+
<service id="Sylius\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface" alias="sylius.resource_metadata_collection.factory.php_file" />
30+
31+
<service id="sylius.resource_metadata_collection.factory.php_file" class="Sylius\Resource\Metadata\Resource\Factory\PhpFileResourceMetadataCollectionFactory">
32+
<argument type="service" id="sylius.resource_registry" />
33+
<argument type="service" id="sylius.routing.factory.operation_route_name_factory" />
34+
<argument type="service" id="sylius_resource.metadata.extractor.php_file" />
35+
</service>
36+
2437
<service id="sylius.resource_metadata_collection.factory.attributes"
2538
class="Sylius\Resource\Metadata\Resource\Factory\AttributesResourceMetadataCollectionFactory"
39+
decorates="sylius.resource_metadata_collection.factory"
40+
decoration-priority="400"
2641
>
2742
<argument type="service" id="sylius.resource_registry" />
2843
<argument type="service" id="sylius.routing.factory.operation_route_name_factory" />
44+
<argument type="service" id=".inner" />
2945
</service>
30-
<service id="sylius.resource_metadata_collection.factory" alias="sylius.resource_metadata_collection.factory.attributes" />
31-
<service id="Sylius\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface" alias="sylius.resource_metadata_collection.factory.attributes" />
3246

3347
<service id="sylius.resource_metadata_collection.factory.state_machine"
3448
class="Sylius\Resource\Metadata\Resource\Factory\StateMachineResourceMetadataCollectionFactory"

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,15 @@
2222
<argument>%sylius.resource.mapping%</argument>
2323
</service>
2424
<service id="Sylius\Resource\Metadata\Resource\Factory\AttributesResourceNameCollectionFactory" alias="sylius_resource.metadata.resource.name_collection.factory.attributes" />
25+
26+
<service id="sylius_resource.metadata.resource.name_collection.factory.php_file"
27+
class="Sylius\Resource\Metadata\Resource\Factory\PhpFileResourceNameCollectionFactory"
28+
decorates="sylius_resource.metadata.resource.name_collection.factory"
29+
decoration-priority="100"
30+
>
31+
<argument type="service" id="sylius_resource.metadata.extractor.php_file" />
32+
<argument type="service" id=".inner" />
33+
</service>
34+
<service id="Sylius\Resource\Metadata\Resource\Factory\PhpFileResourceMetadataCollectionFactory" alias="sylius_resource.metadata.resource.name_collection.factory.php_file" />
2535
</services>
2636
</container>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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\Extractor;
15+
16+
use Sylius\Resource\Metadata\ResourceMetadata;
17+
18+
interface MetadataExtractorInterface
19+
{
20+
/**
21+
* @return ResourceMetadata[]
22+
*/
23+
public function extract(): array;
24+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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\Extractor;
15+
16+
use Sylius\Resource\Metadata\ResourceMetadata;
17+
use Symfony\Component\Finder\Finder;
18+
19+
final class PhpFileMetadataExtractor implements MetadataExtractorInterface
20+
{
21+
public function __construct(
22+
private readonly array $resourceMapping,
23+
) {
24+
}
25+
26+
/**
27+
* @inheritDoc
28+
*/
29+
public function extract(): array
30+
{
31+
$metadata = [];
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 ResourceMetadata) {
41+
continue;
42+
}
43+
44+
$metadata[] = $resource;
45+
}
46+
47+
return $metadata;
48+
}
49+
50+
private function getResourceFilePaths(): iterable
51+
{
52+
foreach ($this->createFinder() as $file) {
53+
yield $file->getPathname();
54+
}
55+
}
56+
57+
private function createFinder(): Finder
58+
{
59+
$finder = (new Finder())->files();
60+
61+
foreach ($this->resourceMapping['imports'] ?? [] as $path) {
62+
$finder->in($path);
63+
}
64+
65+
return $finder->files();
66+
}
67+
68+
/**
69+
* Scope isolated include.
70+
*
71+
* Prevents access to $this/self from included files.
72+
*/
73+
private function getPHPFileClosure(string $filePath): \Closure
74+
{
75+
return \Closure::bind(function () use ($filePath): mixed {
76+
return require $filePath;
77+
}, null, null);
78+
}
79+
}

src/Component/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@ final class AttributesResourceMetadataCollectionFactory implements ResourceMetad
3030
public function __construct(
3131
private RegistryInterface $resourceRegistry,
3232
private OperationRouteNameFactory $operationRouteNameFactory,
33+
private ?ResourceMetadataCollectionFactoryInterface $decorated,
3334
) {
3435
}
3536

3637
public function create(string $resourceClass): ResourceMetadataCollection
3738
{
3839
$resourceMetadataCollection = new ResourceMetadataCollection();
40+
if ($this->decorated) {
41+
return $this->decorated->create($resourceClass);
42+
}
3943

4044
$attributes = ClassReflection::getClassAttributes($resourceClass);
4145

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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\Extractor\MetadataExtractorInterface;
17+
use Sylius\Resource\Metadata\Operation;
18+
use Sylius\Resource\Metadata\Operations;
19+
use Sylius\Resource\Metadata\RegistryInterface;
20+
use Sylius\Resource\Metadata\Resource\ResourceMetadataCollection;
21+
use Sylius\Resource\Symfony\Routing\Factory\RouteName\OperationRouteNameFactory;
22+
23+
final class PhpFileResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface
24+
{
25+
use OperationDefaultsTrait;
26+
27+
public function __construct(
28+
private readonly RegistryInterface $resourceRegistry,
29+
private readonly OperationRouteNameFactory $operationRouteNameFactory,
30+
private readonly MetadataExtractorInterface $metadataExtractor,
31+
private readonly ?ResourceMetadataCollectionFactoryInterface $decorated = null,
32+
) {
33+
}
34+
35+
public function create(string $resourceClass): ResourceMetadataCollection
36+
{
37+
$resourceMetadataCollection = new ResourceMetadataCollection();
38+
39+
if ($this->decorated) {
40+
return $this->decorated->create($resourceClass);
41+
}
42+
43+
foreach ($this->metadataExtractor->extract() as $resource) {
44+
if ($resourceClass !== $resource->getClass()) {
45+
continue;
46+
}
47+
48+
$resourceAlias = $resource->getAlias();
49+
50+
if (null !== $resourceAlias) {
51+
$resourceConfiguration = $this->resourceRegistry->get($resource->getAlias() ?? '');
52+
} else {
53+
$resourceConfiguration = $this->resourceRegistry->getByClass($resourceClass);
54+
}
55+
56+
$resource = $this->getResourceWithDefaults($resourceClass, $resource, $resourceConfiguration);
57+
58+
$operations = [];
59+
/** @var Operation $operation */
60+
foreach ($resource->getOperations() ?? new Operations() as $operation) {
61+
[$key, $operation] = $this->getOperationWithDefaults($this->operationRouteNameFactory, $this->resourceRegistry, $resource, $operation);
62+
$operations[$key] = $operation;
63+
}
64+
65+
if ($operations) {
66+
$resource = $resource->withOperations(new Operations($operations));
67+
}
68+
69+
$resourceMetadataCollection[] = $resource;
70+
}
71+
72+
return $resourceMetadataCollection;
73+
}
74+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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\Extractor\MetadataExtractorInterface;
17+
use Sylius\Resource\Metadata\Resource\ResourceNameCollection;
18+
19+
final class PhpFileResourceNameCollectionFactory implements ResourceNameCollectionFactoryInterface
20+
{
21+
use OperationDefaultsTrait;
22+
23+
public function __construct(
24+
private readonly MetadataExtractorInterface $metadataExtractor,
25+
private readonly ?ResourceNameCollectionFactoryInterface $decorated = null,
26+
) {
27+
}
28+
29+
public function create(): ResourceNameCollection
30+
{
31+
$classes = [];
32+
33+
if ($this->decorated) {
34+
foreach ($this->decorated->create() as $resourceClass) {
35+
$classes[$resourceClass] = true;
36+
}
37+
}
38+
39+
foreach ($this->metadataExtractor->extract() as $resource) {
40+
$resourceClass = $resource->getClass();
41+
42+
if (null === $resourceClass) {
43+
continue;
44+
}
45+
46+
$classes[$resourceClass] = true;
47+
}
48+
49+
return new ResourceNameCollection(array_keys($classes));
50+
}
51+
}

tests/Application/config/packages/doctrine.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ doctrine:
1919
type: attribute
2020
dir: '%kernel.project_dir%/src/BoardGameBlog/Domain'
2121
prefix: 'App\BoardGameBlog\Domain'
22+
Conference:
23+
is_bundle: false
24+
type: attribute
25+
dir: '%kernel.project_dir%/src/Conference/Entity'
26+
prefix: 'App\Conference\Entity'
2227
Subscription:
2328
is_bundle: false
2429
type: attribute

tests/Application/config/sylius/resources.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
sylius_resource:
22
mapping:
3+
imports:
4+
- '%kernel.project_dir%/config/sylius/resources'
35
paths:
46
- '%kernel.project_dir%/src/BoardGameBlog/Infrastructure/Sylius/Resource'
57
- '%kernel.project_dir%/src/Subscription/Entity'
@@ -61,3 +63,7 @@ sylius_resource:
6163
classes:
6264
model: App\Entity\Zone\ZoneMember
6365
interface: App\Entity\Zone\ZoneMemberInterface
66+
67+
app.speaker:
68+
classes:
69+
model: App\Conference\Entity\Speaker

0 commit comments

Comments
 (0)