Skip to content

Commit cc34619

Browse files
authored
feat: add api(Resource/Property)Arguments to types and vocab (#378)
1 parent 9ecfdc8 commit cc34619

15 files changed

+103
-63
lines changed

phpstan.neon

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ parameters:
1010
relationTableName: ?string,
1111
cardinality: string,
1212
ormColumn: array<string, string|string[]>,
13-
security: ?string,
13+
apiPropertyArguments: array{},
1414
groups: string[],
1515
mappedBy: ?string,
1616
inversedBy: ?string,
@@ -34,14 +34,14 @@ parameters:
3434
parent: false|string,
3535
guessFrom: string,
3636
operations: array<string, array<string, string[]>>,
37-
security: ?string,
37+
apiResourceArguments: array{},
3838
allProperties: boolean,
3939
properties: array<string, PropertyConfiguration>
4040
}
4141
'''
4242
Configuration: '''
4343
array{
44-
vocabularies: array{uri: string, format: string, allTypes: ?boolean}[],
44+
vocabularies: array{uri: string, format: string, allTypes: ?boolean, apiResourceArguments: array{}}[],
4545
vocabularyNamespace: string,
4646
relations: string[],
4747
debug: boolean,

src/AttributeGenerator/ApiPlatformCoreAttributeGenerator.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public function generateClassAttributes(Class_ $class): array
4646
if ($class->rdfType()) {
4747
$arguments['iri'] = $class->rdfType();
4848
}
49-
if ($class->security) {
50-
$arguments['security'] = $class->security;
49+
if ($class->apiResourceArguments) {
50+
$arguments = array_merge($arguments, $class->apiResourceArguments);
5151
}
5252

5353
if ($class->operations) {
@@ -98,8 +98,8 @@ public function generatePropertyAttributes(Property $property, string $className
9898
$arguments['iri'] = $property->rdfType();
9999
}
100100

101-
if ($property->security) {
102-
$arguments['security'] = $property->security;
101+
if ($property->apiPropertyArguments) {
102+
$arguments = array_merge($arguments, $property->apiPropertyArguments);
103103
}
104104

105105
return $property->isCustom ? [] : [new Attribute('ApiProperty', $arguments)];

src/ClassMutator/ClassMutatorInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717

1818
interface ClassMutatorInterface
1919
{
20-
// @phpstan-ignore-next-line
20+
// @phpstan-ignore-next-line dynamic context
2121
public function __invoke(Class_ $class, array $context): void;
2222
}

src/Model/Class_.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ abstract class Class_
4444
public bool $isAbstract = false;
4545
public bool $hasChild = false;
4646
public bool $isEmbeddable = false;
47-
public ?string $security = null;
47+
// @phpstan-ignore-next-line dynamic array
48+
public array $apiResourceArguments = [];
4849
/** @var array<string, array<string, string[]|null>> */
4950
public array $operations = [];
5051

src/Model/Property.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ abstract class Property
5151
public ?string $adderRemoverTypeHint = null;
5252
/** @var string[] */
5353
public array $groups = [];
54-
public ?string $security = null;
54+
// @phpstan-ignore-next-line dynamic array
55+
public array $apiPropertyArguments = [];
5556
/** @var Attribute[] */
5657
private array $attributes = [];
5758
/** @var string[] */

src/PropertyGenerator/PropertyGeneratorInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface PropertyGeneratorInterface
2121
/**
2222
* @param Configuration $config
2323
*/
24-
// @phpstan-ignore-next-line
24+
// @phpstan-ignore-next-line dynamic context
2525
public function __invoke(
2626
string $name,
2727
array $config,

src/Schema/Generator.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ final class Generator
3434
public function generate(array $configuration, OutputInterface $output, SymfonyStyle $io): void
3535
{
3636
$graphs = [];
37-
foreach ($configuration['vocabularies'] as $vocab) {
38-
$graph = new RdfGraph($vocab['uri']);
39-
if (0 === strpos($vocab['uri'], 'http://') || 0 === strpos($vocab['uri'], 'https://')) {
40-
$graph->load($vocab['uri'], $vocab['format']);
37+
foreach ($configuration['vocabularies'] as $uri => $vocab) {
38+
$graph = new RdfGraph($uri);
39+
if (0 === strpos($uri, 'http://') || 0 === strpos($uri, 'https://')) {
40+
$graph->load($uri, $vocab['format']);
4141
} else {
42-
$graph->parseFile($vocab['uri'], $vocab['format']);
42+
$graph->parseFile($uri, $vocab['format']);
4343
}
4444

4545
$graphs[] = $graph;

src/Schema/PropertyGenerator/PropertyGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public function __invoke(string $name, array $config, Class_ $class, array $cont
163163
$schemaProperty->mappedBy = $propertyConfig['mappedBy'] ?? null;
164164
$schemaProperty->inversedBy = $propertyConfig['inversedBy'] ?? null;
165165
$schemaProperty->groups = $propertyConfig['groups'] ?? [];
166-
$schemaProperty->security = $propertyConfig['security'] ?? null;
166+
$schemaProperty->apiPropertyArguments = $propertyConfig['apiPropertyArguments'] ?? [];
167167

168168
return $schemaProperty;
169169
}

src/SchemaGeneratorConfiguration.php

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ public function getConfigTreeBuilder(): TreeBuilder
4646
{
4747
$namespacePrefix = $this->defaultPrefix ?? 'App\\';
4848

49+
/* @see https://yaml.org/type/omap.html */
50+
$transformOmap = fn (array $nodeConfig) => !empty(array_filter(
51+
$nodeConfig,
52+
fn ($v, $k) => \is_int($k) && \is_array($v) && 1 === \count($v) && \is_string(array_keys($v)[0]),
53+
\ARRAY_FILTER_USE_BOTH
54+
))
55+
? array_reduce(array_values($nodeConfig), fn (array $map, array $v) => $map + $v, [])
56+
: $nodeConfig;
57+
4958
$treeBuilder = new TreeBuilder('config');
5059

5160
$treeBuilder
@@ -59,23 +68,25 @@ public function getConfigTreeBuilder(): TreeBuilder
5968
->end()
6069
->arrayNode('vocabularies')
6170
->info('RDF vocabularies')
62-
->defaultValue([['uri' => self::SCHEMA_ORG_URI, 'format' => 'rdfxml']])
71+
->defaultValue([self::SCHEMA_ORG_URI => ['format' => 'rdfxml']])
6372
->beforeNormalization()
6473
->ifArray()
65-
->then(static function (array $v) {
66-
return array_map(
67-
static function ($rdf) {
68-
return \is_scalar($rdf) ? ['uri' => $rdf, 'format' => null] : $rdf;
69-
},
70-
$v
71-
);
72-
})
74+
->then(fn (array $v) => array_map(fn ($rdf) => \is_scalar($rdf) ? ['uri' => $rdf] : $rdf, $v))
7375
->end()
76+
->useAttributeAsKey('uri')
7477
->arrayPrototype()
7578
->children()
76-
->scalarNode('uri')->defaultValue(self::SCHEMA_ORG_URI)->info('RDF vocabulary to use')->example('https://schema.org/version/latest/schemaorg-current-https.rdf')->end()
79+
->scalarNode('uri')->info('RDF vocabulary to use')->example('https://schema.org/version/latest/schemaorg-current-https.rdf')->end()
7780
->scalarNode('format')->defaultNull()->info('RDF vocabulary format')->example('rdfxml')->end()
7881
->booleanNode('allTypes')->defaultNull()->info('Generate all types for this vocabulary, even if an explicit configuration exists. If allTypes is enabled globally, it can be disabled for this particular vocabulary')->end()
82+
->arrayNode('apiResourceArguments')
83+
->info('Arguments to add to ApiResource for all the classes generated for this vocabulary')
84+
->variablePrototype()->end()
85+
->beforeNormalization()
86+
->ifArray()
87+
->then($transformOmap)
88+
->end()
89+
->end()
7990
->end()
8091
->end()
8192
->end()
@@ -84,7 +95,7 @@ static function ($rdf) {
8495
->info('OWL relation files containing cardinality information in the GoodRelations format')
8596
->example(self::GOOD_RELATIONS_URI)
8697
->defaultValue([self::GOOD_RELATIONS_URI])
87-
->prototype('scalar')->end()
98+
->scalarPrototype()->end()
8899
->end()
89100
->booleanNode('debug')->defaultFalse()->info('Debug mode')->end()
90101
->arrayNode('id')
@@ -119,7 +130,7 @@ static function ($rdf) {
119130
->enumNode('resolveTargetEntityConfigType')->defaultValue('XML')->values(['XML', 'yaml'])->info('The Resolve Target Entity Listener config file type')->end()
120131
->arrayNode('inheritanceAttributes')
121132
->info('Doctrine inheritance attributes (if set, no other attributes are generated)')
122-
->prototype('variable')->end()
133+
->variablePrototype()->end()
123134
->end()
124135
->end()
125136
->end()
@@ -136,7 +147,7 @@ static function ($rdf) {
136147
->booleanNode('fluentMutatorMethods')->defaultFalse()->info('Set this flag to true to generate fluent setter, adder and remover methods')->end()
137148
->arrayNode('rangeMapping')
138149
->useAttributeAsKey('name')
139-
->prototype('scalar')->end()
150+
->scalarPrototype()->end()
140151
->end()
141152
->booleanNode('allTypes')->defaultFalse()->info('Generate all types, even if an explicit configuration exists')->end()
142153
->booleanNode('resolveTypes')->defaultFalse()->info('If a type is present in a vocabulary but not explicitly imported (types) or if the vocabulary is not totally imported (allTypes), it will be generated')->end()
@@ -172,17 +183,24 @@ static function ($rdf) {
172183
->children()
173184
->arrayNode('attributes')
174185
->info('Doctrine attributes (if set, no other attributes are generated)')
175-
->prototype('variable')->end()
186+
->variablePrototype()->end()
176187
->end()
177188
->end()
178189
->end()
179190
->scalarNode('parent')->defaultFalse()->info('The parent class, set to false for a top level class')->end()
180191
->scalarNode('guessFrom')->defaultValue('Thing')->info('If declaring a custom class, this will be the class from which properties type will be guessed')->end()
181192
->arrayNode('operations')
182193
->info('Operations for the class')
183-
->prototype('variable')->end()
194+
->variablePrototype()->end()
195+
->end()
196+
->arrayNode('apiResourceArguments')
197+
->info('Arguments to add to ApiResource (for instance security)')
198+
->variablePrototype()->end()
199+
->beforeNormalization()
200+
->ifArray()
201+
->then($transformOmap)
202+
->end()
184203
->end()
185-
->scalarNode('security')->defaultNull()->info('Security directive for the class')->end()
186204
->booleanNode('allProperties')->defaultFalse()->info('Import all existing properties')->end()
187205
->arrayNode('properties')
188206
->info('Properties of this type to use')
@@ -206,12 +224,19 @@ static function ($rdf) {
206224
->arrayNode('ormColumn')
207225
->info('The doctrine column attribute content')
208226
->example('{type: "decimal", precision: 5, scale: 1, options: {comment: "my comment"}}')
209-
->prototype('variable')->end()
227+
->variablePrototype()->end()
228+
->end()
229+
->arrayNode('apiPropertyArguments')
230+
->info('Arguments to add to ApiProperty (for instance security)')
231+
->variablePrototype()->end()
232+
->beforeNormalization()
233+
->ifArray()
234+
->then($transformOmap)
235+
->end()
210236
->end()
211-
->scalarNode('security')->defaultNull()->info('Security directive for the property')->end()
212237
->arrayNode('groups')
213238
->info('Symfony Serialization Groups')
214-
->prototype('scalar')->end()
239+
->scalarPrototype()->end()
215240
->end()
216241
->scalarNode('mappedBy')->defaultNull()->info('The doctrine mapped by attribute')->example('partOfSeason')->end()
217242
->scalarNode('inversedBy')->defaultNull()->info('The doctrine inversed by attribute')->example('episodes')->end()
@@ -233,7 +258,7 @@ static function ($rdf) {
233258
->defaultValue([
234259
PhpDocAnnotationGenerator::class,
235260
])
236-
->prototype('scalar')->end()
261+
->scalarPrototype()->end()
237262
->end()
238263
->arrayNode('attributeGenerators')
239264
->info('Attribute generators to use')
@@ -243,11 +268,11 @@ static function ($rdf) {
243268
ConstraintAttributeGenerator::class,
244269
SerializerGroupsAttributeGenerator::class,
245270
])
246-
->prototype('scalar')->end()
271+
->scalarPrototype()->end()
247272
->end()
248273
->arrayNode('generatorTemplates')
249274
->info('Directories for custom generator twig templates')
250-
->prototype('scalar')->end()
275+
->scalarPrototype()->end()
251276
->end()
252277
->end();
253278

src/TypesGenerator.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,11 @@ private function buildClass(array $graphs, array $cardinalities, string $typeNam
245245
}
246246

247247
$typeConfig = $config['types'][$typeName] ?? null;
248+
$vocabConfig = $config['vocabularies'][$type->getGraph()->getUri()] ?? null;
248249
$parent = $typeConfig['parent'] ?? null;
249250
$class = new SchemaClass($typeName, $type, $parent);
250251
$class->operations = $typeConfig['operations'] ?? [];
251-
$class->security = $typeConfig['security'] ?? null;
252+
$class->apiResourceArguments = array_merge($vocabConfig['apiResourceArguments'] ?? [], $typeConfig['apiResourceArguments'] ?? []);
252253

253254
if ($class->isEnum()) {
254255
(new SchemaEnumClassMutator(
@@ -430,13 +431,7 @@ private function defineTypesToGenerate(array $graphs, array $config): array
430431
$allTypes = [];
431432

432433
foreach ($graphs as $graph) {
433-
$vocabAllTypes = $config['allTypes'];
434-
foreach ($config['vocabularies'] as $vocab) {
435-
if ($graph->getUri() !== $vocab['uri']) {
436-
continue;
437-
}
438-
$vocabAllTypes = $vocab['allTypes'] ?? $vocabAllTypes;
439-
}
434+
$vocabAllTypes = $config['vocabularies'][$graph->getUri()]['allTypes'] ?? $config['allTypes'];
440435
foreach (self::$classTypes as $classType) {
441436
foreach ($graph->allOfType($classType) as $type) {
442437
if ($type->isBNode()) {

0 commit comments

Comments
 (0)