1818use cebe \yii2openapi \lib \openapi \ComponentSchema ;
1919use cebe \yii2openapi \lib \openapi \PropertySchema ;
2020use Yii ;
21+ use yii \base \InvalidConfigException ;
2122use yii \helpers \Inflector ;
2223use yii \helpers \StringHelper ;
2324use function explode ;
@@ -29,49 +30,40 @@ class AttributeResolver
2930 /**
3031 * @var Attribute[]|array
3132 */
32- private $ attributes = [];
33+ private array $ attributes = [];
3334
3435 /**
3536 * @var AttributeRelation[]|array
3637 */
37- private $ relations = [];
38+ public array $ relations = [];
39+
3840 /**
3941 * @var NonDbRelation[]|array
4042 */
41- private $ nonDbRelations = [];
43+ private array $ nonDbRelations = [];
4244 /**
4345 * @var ManyToManyRelation[]|array
4446 */
45- private $ many2many = [];
47+ private array $ many2many = [];
4648
47- /**
48- * @var string
49- */
50- private $ schemaName ;
49+ private string $ schemaName ;
5150
52- /**
53- * @var string
54- */
55- private $ tableName ;
51+ private string $ tableName ;
5652
57- /**
58- * @var ComponentSchema
59- */
60- private $ componentSchema ;
53+ private ComponentSchema $ componentSchema ;
6154
62- /**
63- * @var \cebe\yii2openapi\lib\items\JunctionSchemas
64- */
65- private $ junctions ;
55+ private JunctionSchemas $ junctions ;
6656
67- /** @var bool */
68- private $ isJunctionSchema ;
57+ private bool $ isJunctionSchema ;
6958
70- /** @var bool */
71- private $ hasMany2Many ;
59+ private bool $ hasMany2Many ;
7260
73- /** @var Config */
74- private $ config ;
61+ private ?Config $ config ;
62+
63+ /**
64+ * @var AttributeRelation[]|array
65+ */
66+ public array $ inverseRelations = [];
7567
7668 public function __construct (string $ schemaName , ComponentSchema $ schema , JunctionSchemas $ junctions , ?Config $ config = null )
7769 {
@@ -85,14 +77,14 @@ public function __construct(string $schemaName, ComponentSchema $schema, Junctio
8577 }
8678
8779 /**
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
9183 */
92- public function resolve ():DbModel
84+ public function resolve (): DbModel
9385 {
9486 foreach ($ this ->componentSchema ->getProperties () as $ property ) {
95- /** @var $property \cebe\yii2openapi\lib\openapi\ PropertySchema */
87+ /** @var $property PropertySchema */
9688
9789 $ isRequired = $ this ->componentSchema ->isRequiredProperty ($ property ->getName ());
9890 $ nullableValue = $ property ->getProperty ()->getSerializableData ()->nullable ?? null ;
@@ -108,6 +100,7 @@ public function resolve():DbModel
108100 $ this ->resolveProperty ($ property , $ isRequired , $ nullableValue );
109101 }
110102 }
103+
111104 return Yii::createObject (DbModel::class, [
112105 [
113106 /** @see \cebe\openapi\spec\Schema */
@@ -129,24 +122,24 @@ public function resolve():DbModel
129122 }
130123
131124 /**
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
136129 */
137- protected function resolveJunctionTableProperty (PropertySchema $ property , bool $ isRequired ):void
130+ protected function resolveJunctionTableProperty (PropertySchema $ property , bool $ isRequired ): void
138131 {
139132 if ($ this ->junctions ->isJunctionProperty ($ this ->schemaName , $ property ->getName ())) {
140133 $ junkAttribute = $ this ->junctions ->byJunctionSchema ($ this ->schemaName )[$ property ->getName ()];
141134 $ attribute = Yii::createObject (Attribute::class, [$ property ->getName ()]);
142135 $ 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 );
150143 $ relation = Yii::createObject (AttributeRelation::class, [
151144 $ property ->getName (),
152145 $ junkAttribute ['relatedTableName ' ],
@@ -161,12 +154,12 @@ protected function resolveJunctionTableProperty(PropertySchema $property, bool $
161154 }
162155
163156 /**
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
168161 */
169- protected function resolveHasMany2ManyTableProperty (PropertySchema $ property , bool $ isRequired ):void
162+ protected function resolveHasMany2ManyTableProperty (PropertySchema $ property , bool $ isRequired ): void
170163 {
171164 if ($ this ->junctions ->isManyToManyProperty ($ this ->schemaName , $ property ->getName ())) {
172165 return ;
@@ -196,25 +189,25 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo
196189
197190 $ this ->relations [Inflector::pluralize ($ junkRef )] =
198191 Yii::createObject (AttributeRelation::class, [$ junkRef , $ junkAttribute ['junctionTable ' ], $ viaModel ])
199- ->asHasMany ([$ junkAttribute ['pairProperty ' ] . '_id ' => $ this ->componentSchema ->getPkName ()]);
192+ ->asHasMany ([$ junkAttribute ['pairProperty ' ] . '_id ' => $ this ->componentSchema ->getPkName ()]);
200193 return ;
201194 }
202195
203196 $ this ->resolveProperty ($ property , $ isRequired );
204197 }
205198
206199 /**
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
212205 */
213206 protected function resolveProperty (
214207 PropertySchema $ property ,
215208 bool $ isRequired ,
216209 $ nullableValue = 'ARG_ABSENT '
217- ):void {
210+ ): void {
218211 if ($ nullableValue === 'ARG_ABSENT ' ) {
219212 $ nullableValue = $ property ->getProperty ()->getSerializableData ()->nullable ?? null ;
220213 }
@@ -239,7 +232,7 @@ protected function resolveProperty(
239232 if ($ property ->isVirtual ()) {
240233 throw new InvalidDefinitionException ('References not supported for virtual attributes ' );
241234 }
242-
235+
243236 if ($ property ->isNonDbReference ()) {
244237 $ attribute ->asNonDbReference ($ property ->getRefClassName ());
245238 $ relation = Yii::createObject (
@@ -275,21 +268,24 @@ protected function resolveProperty(
275268 AttributeRelation::class,
276269 [static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
277270 )
278- ->asHasOne ([$ fkProperty ->getName () => $ attribute ->columnName ]);
271+ ->asHasOne ([$ fkProperty ->getName () => $ attribute ->columnName ]);
279272 $ relation ->onUpdateFkConstraint = $ property ->onUpdateFkConstraint ;
280273 $ relation ->onDeleteFkConstraint = $ property ->onDeleteFkConstraint ;
281274 if ($ property ->isRefPointerToSelf ()) {
282275 $ relation ->asSelfReference ();
283276 }
284277 $ this ->relations [$ property ->getName ()] = $ relation ;
278+ if (!$ property ->isRefPointerToSelf ()) {
279+ $ this ->addInverseRelation ($ relatedClassName , $ attribute , $ property , $ fkProperty );
280+ }
285281 }
286282 if (!$ property ->isReference () && !$ property ->hasRefItems ()) {
287283 [$ min , $ max ] = $ property ->guessMinMax ();
288284 $ 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 ());
293289 if ($ property ->hasEnum ()) {
294290 $ attribute ->setEnumValues ($ property ->getAttr ('enum ' ));
295291 }
@@ -326,7 +322,7 @@ protected function resolveProperty(
326322 AttributeRelation::class,
327323 [static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
328324 )
329- ->asHasMany ([$ fkProperty ->getName () => $ fkProperty ->getName ()])->asSelfReference ();
325+ ->asHasMany ([$ fkProperty ->getName () => $ fkProperty ->getName ()])->asSelfReference ();
330326 return ;
331327 }
332328 $ foreignPk = Inflector::camel2id ($ fkProperty ->getName (), '_ ' ) . '_id ' ;
@@ -335,7 +331,7 @@ protected function resolveProperty(
335331 AttributeRelation::class,
336332 [static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
337333 )
338- ->asHasMany ([$ foreignPk => $ this ->componentSchema ->getPkName ()]);
334+ ->asHasMany ([$ foreignPk => $ this ->componentSchema ->getPkName ()]);
339335 return ;
340336 }
341337 $ relatedClassName = $ property ->getRefClassName ();
@@ -354,7 +350,8 @@ protected function resolveProperty(
354350 AttributeRelation::class,
355351 [static ::relationName ($ property ->getName (), $ property ->fkColName ), $ relatedTableName , $ relatedClassName ]
356352 )
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 ));
358355 return ;
359356 }
360357 if ($ this ->componentSchema ->isNonDb () && $ attribute ->isReference ()) {
@@ -374,14 +371,14 @@ protected function resolveProperty(
374371 * @param string $relatedTableName
375372 * @param ComponentSchema $refSchema
376373 * @return bool
377- * @throws \yii\base\ InvalidConfigException
374+ * @throws InvalidConfigException|InvalidDefinitionException
378375 */
379376 protected function catchManyToMany (
380377 string $ propertyName ,
381378 string $ relatedSchemaName ,
382379 string $ relatedTableName ,
383380 ComponentSchema $ refSchema
384- ):bool {
381+ ): bool {
385382 if (strtolower (Inflector::id2camel ($ propertyName , '_ ' ))
386383 !== strtolower (Inflector::pluralize ($ relatedSchemaName ))) {
387384 return false ;
@@ -413,9 +410,9 @@ protected function catchManyToMany(
413410 }
414411
415412 /**
416- * @throws \yii\base\ InvalidConfigException
413+ * @throws InvalidConfigException
417414 */
418- protected function guessFakerStub (Attribute $ attribute , PropertySchema $ property ):?string
415+ protected function guessFakerStub (Attribute $ attribute , PropertySchema $ property ): ?string
419416 {
420417 $ resolver = Yii::createObject (['class ' => FakerStubResolver::class], [$ attribute , $ property , $ this ->config ]);
421418 return $ resolver ->resolve ();
@@ -424,9 +421,9 @@ protected function guessFakerStub(Attribute $attribute, PropertySchema $property
424421 /**
425422 * @param array $indexes
426423 * @return array|DbIndex[]
427- * @throws \cebe\yii2openapi\lib\exceptions\ InvalidDefinitionException
424+ * @throws InvalidDefinitionException
428425 */
429- protected function prepareIndexes (array $ indexes ):array
426+ protected function prepareIndexes (array $ indexes ): array
430427 {
431428 $ dbIndexes = [];
432429 foreach ($ indexes as $ index ) {
@@ -481,12 +478,12 @@ protected function prepareIndexes(array $indexes):array
481478 }
482479
483480 /**
484- * @param \cebe\yii2openapi\lib\openapi\ PropertySchema $property
485- * @param \cebe\yii2openapi\lib\items\ Attribute $attribute
481+ * @param PropertySchema $property
482+ * @param Attribute $attribute
486483 * @return void
487- * @throws \yii\base\ InvalidConfigException
484+ * @throws InvalidConfigException|InvalidDefinitionException
488485 */
489- protected function resolvePropertyRef (PropertySchema $ property , Attribute $ attribute ):void
486+ protected function resolvePropertyRef (PropertySchema $ property , Attribute $ attribute ): void
490487 {
491488 $ fkProperty = new PropertySchema (
492489 $ property ->getRefSchema ()->getSchema (),
@@ -495,11 +492,11 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri
495492 );
496493 [$ min , $ max ] = $ fkProperty ->guessMinMax ();
497494 $ 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 ());
503500 $ this ->attributes [$ property ->getName ()] =
504501 $ attribute ->setFakerStub ($ this ->guessFakerStub ($ attribute , $ fkProperty ));
505502 }
@@ -513,4 +510,22 @@ public static function relationName(string $propertyName, ?string $fkColumnName)
513510 }
514511 return $ relationName ;
515512 }
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+ }
516531}
0 commit comments