Skip to content

Commit 6f757c3

Browse files
committed
nonDbFakerStub on items with ref
1 parent 1f16eb6 commit 6f757c3

2 files changed

Lines changed: 49 additions & 35 deletions

File tree

src/lib/FakerStubResolver.php

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/lib/openapi/PropertySchema.php

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -130,26 +130,20 @@ public function __construct(SpecObjectInterface $property, string $name, Compone
130130
}
131131
}
132132

133-
if (
134-
($onUpdate !== null || $onDelete !== null) &&
135-
($reference instanceof Reference)
136-
) {
137-
$this->onUpdateFkConstraint = $onUpdate;
138-
$this->onDeleteFkConstraint = $onDelete;
139-
$this->property = $reference;
140-
$property = $this->property;
141-
} elseif (
142-
($fkColName !== null) &&
143-
($reference instanceof Reference)
144-
) {
145-
$this->fkColName = $fkColName;
146-
$this->property = $reference;
147-
$property = $this->property;
148-
} elseif ($xFaker !== null && $reference instanceof Reference) {
149-
$this->xFaker = $xFaker;
150-
$this->property = $reference;
151-
$property = $this->property;
152-
} elseif ($xDbTypeFalse && $reference instanceof Reference) {
133+
if ($reference instanceof Reference) {
134+
if ($onUpdate !== null) {
135+
$this->onUpdateFkConstraint = $onUpdate;
136+
}
137+
if ($onDelete !== null) {
138+
$this->onDeleteFkConstraint = $onDelete;
139+
}
140+
if ($fkColName !== null) {
141+
$this->fkColName = $fkColName;
142+
}
143+
if ($xFaker !== null) {
144+
$this->xFaker = $xFaker;
145+
}
146+
// covers: fk constraints, fkColName, xFaker, xDbTypeFalse, and plain allOf[$ref]
153147
$this->property = $reference;
154148
$property = $this->property;
155149
}

0 commit comments

Comments
 (0)