18
18
use cebe \yii2openapi \lib \openapi \ComponentSchema ;
19
19
use cebe \yii2openapi \lib \openapi \PropertySchema ;
20
20
use Yii ;
21
+ use yii \base \InvalidConfigException ;
21
22
use yii \helpers \Inflector ;
22
23
use yii \helpers \StringHelper ;
23
24
use function explode ;
@@ -29,49 +30,40 @@ class AttributeResolver
29
30
/**
30
31
* @var Attribute[]|array
31
32
*/
32
- private $ attributes = [];
33
+ private array $ attributes = [];
33
34
34
35
/**
35
36
* @var AttributeRelation[]|array
36
37
*/
37
- private $ relations = [];
38
+ public array $ relations = [];
39
+
38
40
/**
39
41
* @var NonDbRelation[]|array
40
42
*/
41
- private $ nonDbRelations = [];
43
+ private array $ nonDbRelations = [];
42
44
/**
43
45
* @var ManyToManyRelation[]|array
44
46
*/
45
- private $ many2many = [];
47
+ private array $ many2many = [];
46
48
47
- /**
48
- * @var string
49
- */
50
- private $ schemaName ;
49
+ private string $ schemaName ;
51
50
52
- /**
53
- * @var string
54
- */
55
- private $ tableName ;
51
+ private string $ tableName ;
56
52
57
- /**
58
- * @var ComponentSchema
59
- */
60
- private $ componentSchema ;
53
+ private ComponentSchema $ componentSchema ;
61
54
62
- /**
63
- * @var \cebe\yii2openapi\lib\items\JunctionSchemas
64
- */
65
- private $ junctions ;
55
+ private JunctionSchemas $ junctions ;
66
56
67
- /** @var bool */
68
- private $ isJunctionSchema ;
57
+ private bool $ isJunctionSchema ;
69
58
70
- /** @var bool */
71
- private $ hasMany2Many ;
59
+ private bool $ hasMany2Many ;
72
60
73
- /** @var Config */
74
- private $ config ;
61
+ private ?Config $ config ;
62
+
63
+ /**
64
+ * @var AttributeRelation[]|array
65
+ */
66
+ public array $ inverseRelations = [];
75
67
76
68
public function __construct (string $ schemaName , ComponentSchema $ schema , JunctionSchemas $ junctions , ?Config $ config = null )
77
69
{
@@ -85,14 +77,14 @@ public function __construct(string $schemaName, ComponentSchema $schema, Junctio
85
77
}
86
78
87
79
/**
88
- * @return \cebe\yii2openapi\lib\items\ DbModel
89
- * @throws \cebe\yii2openapi\lib\exceptions\ InvalidDefinitionException
90
- * @throws \yii\base\ InvalidConfigException
80
+ * @return DbModel
81
+ * @throws InvalidDefinitionException
82
+ * @throws InvalidConfigException
91
83
*/
92
- public function resolve ():DbModel
84
+ public function resolve (): DbModel
93
85
{
94
86
foreach ($ this ->componentSchema ->getProperties () as $ property ) {
95
- /** @var $property \cebe\yii2openapi\lib\openapi\ PropertySchema */
87
+ /** @var $property PropertySchema */
96
88
97
89
$ isRequired = $ this ->componentSchema ->isRequiredProperty ($ property ->getName ());
98
90
$ nullableValue = $ property ->getProperty ()->getSerializableData ()->nullable ?? null ;
@@ -108,6 +100,7 @@ public function resolve():DbModel
108
100
$ this ->resolveProperty ($ property , $ isRequired , $ nullableValue );
109
101
}
110
102
}
103
+
111
104
return Yii::createObject (DbModel::class, [
112
105
[
113
106
/** @see \cebe\openapi\spec\Schema */
@@ -129,24 +122,24 @@ public function resolve():DbModel
129
122
}
130
123
131
124
/**
132
- * @param \cebe\yii2openapi\lib\openapi\ PropertySchema $property
133
- * @param bool $isRequired
134
- * @throws \cebe\yii2openapi\lib\exceptions\ InvalidDefinitionException
135
- * @throws \yii\base\ InvalidConfigException
125
+ * @param PropertySchema $property
126
+ * @param bool $isRequired
127
+ * @throws InvalidDefinitionException
128
+ * @throws InvalidConfigException
136
129
*/
137
- protected function resolveJunctionTableProperty (PropertySchema $ property , bool $ isRequired ):void
130
+ protected function resolveJunctionTableProperty (PropertySchema $ property , bool $ isRequired ): void
138
131
{
139
132
if ($ this ->junctions ->isJunctionProperty ($ this ->schemaName , $ property ->getName ())) {
140
133
$ junkAttribute = $ this ->junctions ->byJunctionSchema ($ this ->schemaName )[$ property ->getName ()];
141
134
$ attribute = Yii::createObject (Attribute::class, [$ property ->getName ()]);
142
135
$ attribute ->setRequired ($ isRequired )
143
- ->setDescription ($ property ->getAttr ('description ' , '' ))
144
- ->setReadOnly ($ property ->isReadonly ())
145
- ->setIsPrimary ($ property ->isPrimaryKey ())
146
- ->asReference ($ junkAttribute ['relatedClassName ' ])
147
- ->setPhpType ($ junkAttribute ['phpType ' ])
148
- ->setDbType ($ junkAttribute ['dbType ' ])
149
- ->setForeignKeyColumnName ($ property ->fkColName );
136
+ ->setDescription ($ property ->getAttr ('description ' , '' ))
137
+ ->setReadOnly ($ property ->isReadonly ())
138
+ ->setIsPrimary ($ property ->isPrimaryKey ())
139
+ ->asReference ($ junkAttribute ['relatedClassName ' ])
140
+ ->setPhpType ($ junkAttribute ['phpType ' ])
141
+ ->setDbType ($ junkAttribute ['dbType ' ])
142
+ ->setForeignKeyColumnName ($ property ->fkColName );
150
143
$ relation = Yii::createObject (AttributeRelation::class, [
151
144
$ property ->getName (),
152
145
$ junkAttribute ['relatedTableName ' ],
@@ -161,12 +154,12 @@ protected function resolveJunctionTableProperty(PropertySchema $property, bool $
161
154
}
162
155
163
156
/**
164
- * @param \cebe\yii2openapi\lib\openapi\ PropertySchema $property
165
- * @param bool $isRequired
166
- * @throws \cebe\yii2openapi\lib\exceptions\ InvalidDefinitionException
167
- * @throws \yii\base\ InvalidConfigException
157
+ * @param PropertySchema $property
158
+ * @param bool $isRequired
159
+ * @throws InvalidDefinitionException
160
+ * @throws InvalidConfigException
168
161
*/
169
- protected function resolveHasMany2ManyTableProperty (PropertySchema $ property , bool $ isRequired ):void
162
+ protected function resolveHasMany2ManyTableProperty (PropertySchema $ property , bool $ isRequired ): void
170
163
{
171
164
if ($ this ->junctions ->isManyToManyProperty ($ this ->schemaName , $ property ->getName ())) {
172
165
return ;
@@ -196,25 +189,25 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo
196
189
197
190
$ this ->relations [Inflector::pluralize ($ junkRef )] =
198
191
Yii::createObject (AttributeRelation::class, [$ junkRef , $ junkAttribute ['junctionTable ' ], $ viaModel ])
199
- ->asHasMany ([$ junkAttribute ['pairProperty ' ] . '_id ' => $ this ->componentSchema ->getPkName ()]);
192
+ ->asHasMany ([$ junkAttribute ['pairProperty ' ] . '_id ' => $ this ->componentSchema ->getPkName ()]);
200
193
return ;
201
194
}
202
195
203
196
$ this ->resolveProperty ($ property , $ isRequired );
204
197
}
205
198
206
199
/**
207
- * @param \cebe\yii2openapi\lib\openapi\ PropertySchema $property
208
- * @param bool $isRequired
209
- * @param bool|null|string $nullableValue if string then its value will be only constant `ARG_ABSENT`. Default `null` is avoided because it can be in passed value in method call
210
- * @throws \cebe\yii2openapi\lib\exceptions\ InvalidDefinitionException
211
- * @throws \yii\base\ InvalidConfigException
200
+ * @param PropertySchema $property
201
+ * @param bool $isRequired
202
+ * @param bool|null|string $nullableValue if string then its value will be only constant `ARG_ABSENT`. Default `null` is avoided because it can be in passed value in method call
203
+ * @throws InvalidDefinitionException
204
+ * @throws InvalidConfigException
212
205
*/
213
206
protected function resolveProperty (
214
207
PropertySchema $ property ,
215
208
bool $ isRequired ,
216
209
$ nullableValue = 'ARG_ABSENT '
217
- ):void {
210
+ ): void {
218
211
if ($ nullableValue === 'ARG_ABSENT ' ) {
219
212
$ nullableValue = $ property ->getProperty ()->getSerializableData ()->nullable ?? null ;
220
213
}
@@ -239,7 +232,7 @@ protected function resolveProperty(
239
232
if ($ property ->isVirtual ()) {
240
233
throw new InvalidDefinitionException ('References not supported for virtual attributes ' );
241
234
}
242
-
235
+
243
236
if ($ property ->isNonDbReference ()) {
244
237
$ attribute ->asNonDbReference ($ property ->getRefClassName ());
245
238
$ relation = Yii::createObject (
@@ -275,21 +268,24 @@ protected function resolveProperty(
275
268
AttributeRelation::class,
276
269
[static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
277
270
)
278
- ->asHasOne ([$ fkProperty ->getName () => $ attribute ->columnName ]);
271
+ ->asHasOne ([$ fkProperty ->getName () => $ attribute ->columnName ]);
279
272
$ relation ->onUpdateFkConstraint = $ property ->onUpdateFkConstraint ;
280
273
$ relation ->onDeleteFkConstraint = $ property ->onDeleteFkConstraint ;
281
274
if ($ property ->isRefPointerToSelf ()) {
282
275
$ relation ->asSelfReference ();
283
276
}
284
277
$ this ->relations [$ property ->getName ()] = $ relation ;
278
+ if (!$ property ->isRefPointerToSelf ()) {
279
+ $ this ->addInverseRelation ($ relatedClassName , $ attribute , $ property , $ fkProperty );
280
+ }
285
281
}
286
282
if (!$ property ->isReference () && !$ property ->hasRefItems ()) {
287
283
[$ min , $ max ] = $ property ->guessMinMax ();
288
284
$ attribute ->setIsVirtual ($ property ->isVirtual ())
289
- ->setPhpType ($ property ->guessPhpType ())
290
- ->setDbType ($ property ->guessDbType ())
291
- ->setSize ($ property ->getMaxLength ())
292
- ->setLimits ($ min , $ max , $ property ->getMinLength ());
285
+ ->setPhpType ($ property ->guessPhpType ())
286
+ ->setDbType ($ property ->guessDbType ())
287
+ ->setSize ($ property ->getMaxLength ())
288
+ ->setLimits ($ min , $ max , $ property ->getMinLength ());
293
289
if ($ property ->hasEnum ()) {
294
290
$ attribute ->setEnumValues ($ property ->getAttr ('enum ' ));
295
291
}
@@ -326,7 +322,7 @@ protected function resolveProperty(
326
322
AttributeRelation::class,
327
323
[static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
328
324
)
329
- ->asHasMany ([$ fkProperty ->getName () => $ fkProperty ->getName ()])->asSelfReference ();
325
+ ->asHasMany ([$ fkProperty ->getName () => $ fkProperty ->getName ()])->asSelfReference ();
330
326
return ;
331
327
}
332
328
$ foreignPk = Inflector::camel2id ($ fkProperty ->getName (), '_ ' ) . '_id ' ;
@@ -335,7 +331,7 @@ protected function resolveProperty(
335
331
AttributeRelation::class,
336
332
[static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
337
333
)
338
- ->asHasMany ([$ foreignPk => $ this ->componentSchema ->getPkName ()]);
334
+ ->asHasMany ([$ foreignPk => $ this ->componentSchema ->getPkName ()]);
339
335
return ;
340
336
}
341
337
$ relatedClassName = $ property ->getRefClassName ();
@@ -354,7 +350,8 @@ protected function resolveProperty(
354
350
AttributeRelation::class,
355
351
[static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
356
352
)
357
- ->asHasMany ([Inflector::camel2id ($ this ->schemaName , '_ ' ) . '_id ' => $ this ->componentSchema ->getPkName ()]);
353
+ ->asHasMany ([Inflector::camel2id ($ this ->schemaName , '_ ' ) . '_id ' => $ this ->componentSchema ->getPkName ()])
354
+ ->setInverse (Inflector::variablize ($ this ->schemaName ));
358
355
return ;
359
356
}
360
357
if ($ this ->componentSchema ->isNonDb () && $ attribute ->isReference ()) {
@@ -374,14 +371,14 @@ protected function resolveProperty(
374
371
* @param string $relatedTableName
375
372
* @param ComponentSchema $refSchema
376
373
* @return bool
377
- * @throws \yii\base\ InvalidConfigException
374
+ * @throws InvalidConfigException|InvalidDefinitionException
378
375
*/
379
376
protected function catchManyToMany (
380
377
string $ propertyName ,
381
378
string $ relatedSchemaName ,
382
379
string $ relatedTableName ,
383
380
ComponentSchema $ refSchema
384
- ):bool {
381
+ ): bool {
385
382
if (strtolower (Inflector::id2camel ($ propertyName , '_ ' ))
386
383
!== strtolower (Inflector::pluralize ($ relatedSchemaName ))) {
387
384
return false ;
@@ -413,9 +410,9 @@ protected function catchManyToMany(
413
410
}
414
411
415
412
/**
416
- * @throws \yii\base\ InvalidConfigException
413
+ * @throws InvalidConfigException
417
414
*/
418
- protected function guessFakerStub (Attribute $ attribute , PropertySchema $ property ):?string
415
+ protected function guessFakerStub (Attribute $ attribute , PropertySchema $ property ): ?string
419
416
{
420
417
$ resolver = Yii::createObject (['class ' => FakerStubResolver::class], [$ attribute , $ property , $ this ->config ]);
421
418
return $ resolver ->resolve ();
@@ -424,9 +421,9 @@ protected function guessFakerStub(Attribute $attribute, PropertySchema $property
424
421
/**
425
422
* @param array $indexes
426
423
* @return array|DbIndex[]
427
- * @throws \cebe\yii2openapi\lib\exceptions\ InvalidDefinitionException
424
+ * @throws InvalidDefinitionException
428
425
*/
429
- protected function prepareIndexes (array $ indexes ):array
426
+ protected function prepareIndexes (array $ indexes ): array
430
427
{
431
428
$ dbIndexes = [];
432
429
foreach ($ indexes as $ index ) {
@@ -481,12 +478,12 @@ protected function prepareIndexes(array $indexes):array
481
478
}
482
479
483
480
/**
484
- * @param \cebe\yii2openapi\lib\openapi\ PropertySchema $property
485
- * @param \cebe\yii2openapi\lib\items\ Attribute $attribute
481
+ * @param PropertySchema $property
482
+ * @param Attribute $attribute
486
483
* @return void
487
- * @throws \yii\base\ InvalidConfigException
484
+ * @throws InvalidConfigException|InvalidDefinitionException
488
485
*/
489
- protected function resolvePropertyRef (PropertySchema $ property , Attribute $ attribute ):void
486
+ protected function resolvePropertyRef (PropertySchema $ property , Attribute $ attribute ): void
490
487
{
491
488
$ fkProperty = new PropertySchema (
492
489
$ property ->getRefSchema ()->getSchema (),
@@ -495,11 +492,11 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri
495
492
);
496
493
[$ min , $ max ] = $ fkProperty ->guessMinMax ();
497
494
$ attribute ->setPhpType ($ fkProperty ->guessPhpType ())
498
- ->setDbType ($ fkProperty ->guessDbType (true ))
499
- ->setSize ($ fkProperty ->getMaxLength ())
500
- ->setDescription ($ fkProperty ->getAttr ('description ' ))
501
- ->setDefault ($ fkProperty ->guessDefault ())
502
- ->setLimits ($ min , $ max , $ fkProperty ->getMinLength ());
495
+ ->setDbType ($ fkProperty ->guessDbType (true ))
496
+ ->setSize ($ fkProperty ->getMaxLength ())
497
+ ->setDescription ($ fkProperty ->getAttr ('description ' ))
498
+ ->setDefault ($ fkProperty ->guessDefault ())
499
+ ->setLimits ($ min , $ max , $ fkProperty ->getMinLength ());
503
500
$ this ->attributes [$ property ->getName ()] =
504
501
$ attribute ->setFakerStub ($ this ->guessFakerStub ($ attribute , $ fkProperty ));
505
502
}
@@ -513,4 +510,22 @@ public static function relationName(string $propertyName, ?string $fkColumnName)
513
510
}
514
511
return $ relationName ;
515
512
}
513
+
514
+ /**
515
+ * @throws InvalidConfigException
516
+ */
517
+ public function addInverseRelation (
518
+ string $ relatedClassName ,
519
+ Attribute $ attribute ,
520
+ PropertySchema $ property ,
521
+ PropertySchema $ fkProperty
522
+ ): void {
523
+ $ inverseRelation = Yii::createObject (
524
+ AttributeRelation::class,
525
+ [$ this ->schemaName , $ this ->tableName , $ this ->schemaName ]
526
+ )
527
+ ->asHasOne ([$ attribute ->columnName => $ fkProperty ->getName ()]);
528
+ $ inverseRelation ->setInverse ($ property ->getName ());
529
+ $ this ->inverseRelations [$ relatedClassName ][] = $ inverseRelation ;
530
+ }
516
531
}
0 commit comments