@@ -80,8 +80,8 @@ public function resolve(): ?string
8080 return null ;
8181 }
8282
83- // column name ends with `_id`/FK
84- if (substr ( $ this -> attribute -> columnName , - 3 ) === ' _id ' || !empty ($ this ->attribute ->fkColName )) {
83+ // FK: determined by a $ref / allOf[$ref] — not by column name convention
84+ if (!empty ($ this ->attribute ->reference )) {
8585 $ config = $ this ->config ;
8686 if (!$ config ) {
8787 $ config = new Config ;
@@ -313,7 +313,7 @@ private function fakeForArray(SpecObjectInterface $property, int $count = 4): st
313313 }
314314
315315 if ($ type === 'object ' ) {
316- $ result = $ this ->fakeForObject ($ items );
316+ $ result = $ this ->fakeForObject ($ items, 1 );
317317 if ($ result === '(object) [] ' ) {
318318 return '[] ' ;
319319 }
@@ -339,7 +339,7 @@ private function fakeForArray(SpecObjectInterface $property, int $count = 4): st
339339 * defined properties this is acceptable, as no schema is enforced.
340340 * @internal
341341 */
342- public function fakeForObject (SpecObjectInterface $ items , int $ depth = 1 ): string
342+ public function fakeForObject (SpecObjectInterface $ items , int $ depth = 0 ): string
343343 {
344344 if (!$ items ->properties ) {
345345 return '(object) [] ' ;
@@ -353,14 +353,15 @@ public function fakeForObject(SpecObjectInterface $items, int $depth = 1): strin
353353 /** @var SpecObjectInterface $prop */
354354
355355 if (!$ prop instanceof Reference && ($ prop ->type === 'object ' || !empty ($ prop ->properties ))) {
356+ $ key = $ name ;
356357 $ result = $ this ->fakeForObject ($ prop , $ depth + 1 );
357358 } else {
358- $ result = $ this ->aElementFaker (['items ' => $ prop ->getSerializableData ()], $ name );
359+ [ ' columnName ' => $ key , ' fakerStub ' => $ result] = $ this ->resolveElement (['items ' => $ prop ->getSerializableData ()], $ name );
359360 if (str_starts_with ($ result , 'array_map ' )) {
360361 $ result = $ this ->reindentArrayMapForObject ($ result , $ depth );
361362 }
362363 }
363- $ parts [] = $ indent . '\'' . $ name . '\' => ' . $ result . ', ' ;
364+ $ parts [] = $ indent . '\'' . $ key . '\' => ' . $ result . ', ' ;
364365 }
365366
366367 $ props = '[ ' . PHP_EOL . implode (PHP_EOL , $ parts ) . PHP_EOL . $ closingIndent . '] ' ;
@@ -371,7 +372,7 @@ public function fakeForObject(SpecObjectInterface $items, int $depth = 1): strin
371372 /**
372373 * Re-indents a compact wrapInArray() output string to match the correct depth inside fakeForObject().
373374 * wrapInArray() always uses hardcoded 12/8-space indentation; when its result is embedded as a
374- * property value inside a fakeForObject() output at depth >= 1 , the indentation must be adjusted.
375+ * property value inside a fakeForObject() output, the indentation must be adjusted.
375376 * For a nested array_map body the inner call is expanded to multi-line style via expandCompactArrayMap().
376377 */
377378 private function reindentArrayMapForObject (string $ code , int $ depth ): string
@@ -385,19 +386,23 @@ private function reindentArrayMapForObject(string $code, int $depth): string
385386 }
386387 [$ body , $ count ] = [$ m [1 ], $ m [2 ]];
387388
389+ // Shift all continuation lines by the delta between the target indent and wrapInArray's hardcoded 12 spaces.
390+ $ shift = strlen ($ bodyIndent ) - 12 ;
391+ if ($ shift > 0 ) {
392+ $ body = preg_replace ('/\n/ ' , "\n" . str_repeat (' ' , $ shift ), $ body );
393+ }
394+
388395 if (str_starts_with ($ body , 'return array_map( ' )) {
389396 $ inner = substr ($ body , 7 , -1 ); // strip "return " prefix and trailing ";"
390397 $ expanded = $ this ->expandCompactArrayMap ($ inner , $ bodyIndent );
391398 return "array_map(function () use ( \$faker, \$uniqueFaker) { \n"
392399 . $ bodyIndent . "return {$ expanded }; \n"
393- . $ closeIndent . "}, \n"
394- . $ closeIndent . "range(1, {$ count })) " ;
400+ . $ closeIndent . "}, range(1, {$ count })) " ;
395401 }
396402
397403 return "array_map(function () use ( \$faker, \$uniqueFaker) { \n"
398404 . $ bodyIndent . $ body . "\n"
399- . $ closeIndent . "}, \n"
400- . $ closeIndent . "range(1, {$ count })) " ;
405+ . $ closeIndent . "}, range(1, {$ count })) " ;
401406 }
402407
403408 /**
@@ -492,11 +497,22 @@ public function arbitraryArray(): string
492497 * @internal
493498 */
494499 public function aElementFaker ($ data , ?string $ columnName = null ): ?string
500+ {
501+ return $ this ->resolveElement ($ data , $ columnName )['fakerStub ' ];
502+ }
503+
504+ /**
505+ * Resolves the faker stub and the effective column key for a single element.
506+ * For FK properties (direct $ref or allOf[$ref]), the key uses the same '_id' suffix
507+ * logic as Attribute::asReference() — both for real DB columns and JSONB sub-properties.
508+ * @return array
509+ * @example ['columnName' => 'payment_method_id', 'fakerStub' => '$faker->randomElement(...)']
510+ */
511+ private function resolveElement ($ data , ?string $ columnName = null ): array
495512 {
496513 if ($ data instanceof Reference) {
497- $ class = str_replace ('#/components/schemas/ ' , '' , $ data ->getReference ());
498- $ class .= 'Faker ' ;
499- return '(new ' . $ class . ')->generateModel()->attributes ' ;
514+ $ class = str_replace ('#/components/schemas/ ' , '' , $ data ->getReference ()) . 'Faker ' ;
515+ return ['columnName ' => $ columnName ?? 'unknownColumn ' , 'fakerStub ' => '(new ' . $ class . ')->generateModel()->attributes ' ];
500516 }
501517
502518 $ inp = $ data instanceof SpecObjectInterface ? $ data ->getSerializableData () : $ data ;
@@ -523,7 +539,11 @@ public function aElementFaker($data, ?string $columnName = null): ?string
523539 $ schema ->setReferenceContext ($ rc );
524540 }
525541 $ dbModels = (new AttributeResolver ($ compo , $ cs , new JunctionSchemas ([]), $ this ->config ))->resolve ();
542+ $ attr = $ dbModels ->attributes [$ columnName ];
526543
527- return (new static ($ dbModels ->attributes [$ columnName ], $ cs ->getProperty ($ columnName ), $ this ->config ))->resolve ();
544+ return [
545+ 'columnName ' => $ attr ->columnName ,
546+ 'fakerStub ' => (new static ($ attr , $ cs ->getProperty ($ columnName ), $ this ->config ))->resolve (),
547+ ];
528548 }
529549}
0 commit comments