Skip to content

Commit 9f83e78

Browse files
committed
Implement oneOf and refactor - WIP 2
1 parent 2f08c41 commit 9f83e78

File tree

2 files changed

+89
-95
lines changed

2 files changed

+89
-95
lines changed

src/lib/FakerStubResolver.php

Lines changed: 89 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,23 @@
77

88
/** @noinspection InterfacesAsConstructorDependenciesInspection */
99
/** @noinspection PhpUndefinedFieldInspection */
10+
1011
namespace cebe\yii2openapi\lib;
1112

13+
use cebe\openapi\exceptions\TypeErrorException;
14+
use cebe\openapi\exceptions\UnresolvableReferenceException;
1215
use cebe\openapi\spec\Reference;
1316
use cebe\openapi\spec\Schema;
1417
use cebe\openapi\SpecObjectInterface;
18+
use cebe\yii2openapi\lib\exceptions\InvalidDefinitionException;
1519
use cebe\yii2openapi\lib\items\Attribute;
1620
use cebe\yii2openapi\lib\items\JunctionSchemas;
1721
use cebe\yii2openapi\lib\openapi\ComponentSchema;
1822
use cebe\yii2openapi\lib\openapi\PropertySchema;
23+
use Symfony\Component\VarExporter\Exception\ExceptionInterface;
1924
use Symfony\Component\VarExporter\VarExporter;
25+
use yii\base\InvalidConfigException;
26+
use yii\helpers\Json;
2027
use yii\helpers\VarDumper;
2128
use function str_replace;
2229
use const PHP_EOL;
@@ -28,18 +35,12 @@
2835
class FakerStubResolver
2936
{
3037
public const MAX_INT = 1000000;
31-
/**
32-
* @var \cebe\yii2openapi\lib\items\Attribute
33-
*/
34-
private $attribute;
3538

36-
/**
37-
* @var \cebe\yii2openapi\lib\openapi\PropertySchema
38-
*/
39-
private $property;
39+
private Attribute $attribute;
4040

41-
/** @var Config */
42-
private $config;
41+
private PropertySchema $property;
42+
43+
private ?Config $config;
4344

4445
public function __construct(Attribute $attribute, PropertySchema $property, ?Config $config = null)
4546
{
@@ -48,7 +49,14 @@ public function __construct(Attribute $attribute, PropertySchema $property, ?Con
4849
$this->config = $config;
4950
}
5051

51-
public function resolve():?string
52+
/**
53+
* @throws InvalidConfigException
54+
* @throws TypeErrorException
55+
* @throws UnresolvableReferenceException
56+
* @throws InvalidDefinitionException
57+
* @throws ExceptionInterface
58+
*/
59+
public function resolve(): ?string
5260
{
5361
if ($this->property->xFaker === false) {
5462
$this->attribute->setFakerStub(null);
@@ -74,9 +82,9 @@ public function resolve():?string
7482
$config = new Config;
7583
}
7684
$mn = $config->modelNamespace;
77-
return '$faker->randomElement(\\'.$mn
78-
. ($mn ? '\\' : '')
79-
. ucfirst($this->attribute->reference).'::find()->select("id")->column())';
85+
return '$faker->randomElement(\\' . $mn
86+
. ($mn ? '\\' : '')
87+
. ucfirst($this->attribute->reference) . '::find()->select("id")->column())';
8088
}
8189

8290
$limits = $this->attribute->limits;
@@ -96,18 +104,18 @@ public function resolve():?string
96104
return null;
97105
}
98106

99-
if (! $this->property->hasAttr('example')) {
107+
if (!$this->property->hasAttr('example')) {
100108
return $result;
101109
}
102110
if (stripos($result, 'uniqueFaker') !== false) {
103111
return $result;
104112
}
105113
$example = $this->property->getAttr('example');
106114
$example = VarExporter::export($example);
107-
return str_replace('$faker->', '$faker->optional(0.92, '.$example.')->', $result);
115+
return str_replace('$faker->', '$faker->optional(0.92, ' . $example . ')->', $result);
108116
}
109117

110-
private function fakeForString():?string
118+
private function fakeForString(): ?string
111119
{
112120
$formats = [
113121
'date' => '$faker->dateTimeThisCentury->format(\'Y-m-d\')',
@@ -127,7 +135,7 @@ private function fakeForString():?string
127135
}
128136
$enum = $this->property->getAttr('enum');
129137
if (!empty($enum) && is_array($enum)) {
130-
$items = str_replace([PHP_EOL, ' ',',]'], ['', '', ']'], VarDumper::export($enum));
138+
$items = str_replace([PHP_EOL, ' ', ',]'], ['', '', ']'], VarDumper::export($enum));
131139
return '$faker->randomElement(' . $items . ')';
132140
}
133141
if ($this->attribute->columnName === 'title'
@@ -172,7 +180,7 @@ private function fakeForString():?string
172180
'~(url|site|website|href)~i' => '$faker->url',
173181
'~(username|login)~i' => '$faker->userName',
174182
];
175-
$size = $this->attribute->size > 0 ? $this->attribute->size: null;
183+
$size = $this->attribute->size > 0 ? $this->attribute->size : null;
176184
foreach ($patterns as $pattern => $fake) {
177185
if (preg_match($pattern, $this->attribute->columnName)) {
178186
if ($size) {
@@ -187,27 +195,27 @@ private function fakeForString():?string
187195
if ($size < 5) {
188196
$method = 'word';
189197
}
190-
return 'substr($faker->'.$method.'(' . $size . '), 0, ' . $size . ')';
198+
return 'substr($faker->' . $method . '(' . $size . '), 0, ' . $size . ')';
191199
}
192200
return '$faker->sentence';
193201
}
194202

195-
private function fakeForInt(?int $min, ?int $max):?string
203+
private function fakeForInt(?int $min, ?int $max): ?string
196204
{
197205
$fakerVariable = 'faker';
198206
if (preg_match('~_?id$~', $this->attribute->columnName)) {
199207
$fakerVariable = 'uniqueFaker';
200208
}
201209
if ($min !== null && $max !== null) {
202-
return "\${$fakerVariable}->numberBetween($min, $max)";
210+
return "\$$fakerVariable->numberBetween($min, $max)";
203211
}
204212

205213
if ($min !== null) {
206-
return "\${$fakerVariable}->numberBetween($min, ".self::MAX_INT.")";
214+
return "\$$fakerVariable->numberBetween($min, " . self::MAX_INT . ")";
207215
}
208216

209217
if ($max !== null) {
210-
return "\${$fakerVariable}->numberBetween(0, $max)";
218+
return "\$$fakerVariable->numberBetween(0, $max)";
211219
}
212220

213221
$patterns = [
@@ -221,10 +229,10 @@ private function fakeForInt(?int $min, ?int $max):?string
221229
return $fake;
222230
}
223231
}
224-
return "\${$fakerVariable}->numberBetween(0, ".self::MAX_INT.")";
232+
return "\$$fakerVariable->numberBetween(0, " . self::MAX_INT . ")";
225233
}
226234

227-
private function fakeForFloat(?int $min, ?int $max):?string
235+
private function fakeForFloat(?int $min, ?int $max): ?string
228236
{
229237
if ($min !== null && $max !== null) {
230238
return "\$faker->randomFloat(null, $min, $max)";
@@ -238,69 +246,74 @@ private function fakeForFloat(?int $min, ?int $max):?string
238246
return '$faker->randomFloat()';
239247
}
240248

249+
/**
250+
* @throws InvalidConfigException
251+
* @throws TypeErrorException
252+
* @throws UnresolvableReferenceException
253+
* @throws InvalidDefinitionException|ExceptionInterface
254+
*/
241255
private function fakeForArray(SpecObjectInterface $property, int $count = 4): string
242256
{
243-
// TODO consider example of OpenAPI spec
244-
$arbitrary = false;
245257
$uniqueItems = false;
258+
$arbitrary = false;
246259
$type = null;
260+
if ($property->minItems) {
261+
$count = $property->minItems;
262+
}
263+
if ($property->maxItems) {
264+
$maxItems = $property->maxItems;
265+
if ($maxItems < $count) {
266+
$count = $maxItems;
267+
}
268+
}
269+
if (isset($property->uniqueItems)) {
270+
$uniqueItems = $property->uniqueItems;
271+
}
272+
273+
// TODO consider example of OpenAPI spec
274+
247275
// $count = 4; # let's set a number to default number of elements
248276

249277
/** @var Schema|Reference|null $items */
250278
$items = $property->items ?? $property; # later is used in `oneOf`
251279

280+
$aElementData = Json::decode(Json::encode($this->property->getProperty()->getSerializableData()));
281+
$compoSchemaArr = [
282+
'properties' => [
283+
'unnamedProp' => $aElementData['items']
284+
]
285+
];
286+
252287
if ($items) {
253288
if ($items instanceof Reference) {
254289
$class = str_replace('#/components/schemas/', '', $items->getReference());
255290
$class .= 'Faker';
256-
return '(new ' . $class . ')->generateModel()->attributes';
291+
return $this->wrapAsArray('(new ' . $class . ')->generateModel()->attributes', false, $count);
257292
} elseif (!empty($items->oneOf)) {
258293
return $this->handleOneOf($items, $count);
259294
} else {
260295
$type = $items->type;
261296
if ($type === null) {
262297
$arbitrary = true;
263298
}
299+
$cs = new ComponentSchema(new Schema($compoSchemaArr), 'UnnamedCompo');
300+
$dbModels = (new AttributeResolver('UnnamedCompo', $cs, new JunctionSchemas([])))->resolve();
301+
$aElementFaker = (new static($dbModels->attributes['unnamedProp'], $cs->getProperty('unnamedProp')))->resolve();
264302
}
265303
} else {
266304
$arbitrary = true;
267305
}
268306

269-
if ($property->minItems) {
270-
$count = $property->minItems;
307+
if ($arbitrary) {
308+
return '$faker->words()';
271309
}
272310

273-
if ($property->maxItems) {
274-
$maxItems = $property->maxItems;
275-
if ($maxItems < $count) {
276-
$count = $maxItems;
277-
}
278-
}
279-
280-
if (isset($property->uniqueItems)) {
281-
$uniqueItems = $property->uniqueItems;
282-
}
283-
284-
if ($arbitrary || $type === 'string') {
285-
return ($uniqueItems ? '$uniqueFaker' : '$faker') . '->words(' . $count . ')';
286-
}
287-
288-
if (in_array($type, ['number', 'integer'])) {
289-
return 'array_map(function () use ($faker, $uniqueFaker) {
290-
return ' . ($uniqueItems ? '$uniqueFaker' : '$faker') . '->randomNumber();
291-
}, range(1, ' . $count . '))';
292-
}
293-
294-
if ($type === 'boolean') {
295-
return 'array_map(function () use ($faker, $uniqueFaker) {
296-
return ' . ($uniqueItems ? '$uniqueFaker' : '$faker') . '->boolean();
297-
}, range(1, ' . $count . '))';
311+
if (in_array($type, ['string', 'number', 'integer', 'boolean'])) {
312+
return $this->wrapAsArray($aElementFaker, $uniqueItems, $count);
298313
}
299314

300315
if ($type === 'array') { # array or nested arrays
301-
return 'array_map(function () use ($faker, $uniqueFaker) {
302-
return ' . $this->{__FUNCTION__}($items) . ';
303-
}, range(1, ' . $count . '))';
316+
return $this->{__FUNCTION__}($items);
304317
}
305318

306319
if ($type === 'object') {
@@ -321,13 +334,16 @@ private function fakeForArray(SpecObjectInterface $property, int $count = 4): st
321334
/**
322335
* @param $items Schema|Reference|null
323336
* @param $count int
337+
* @param bool $nested
324338
* @return string
325-
* @throws \cebe\openapi\exceptions\UnresolvableReferenceException
326-
* @throws \yii\base\InvalidConfigException
327-
* @throws exceptions\InvalidDefinitionException
339+
* @throws ExceptionInterface
340+
* @throws InvalidConfigException
341+
* @throws InvalidDefinitionException
342+
* @throws TypeErrorException
343+
* @throws UnresolvableReferenceException
328344
* @internal
329345
*/
330-
public function handleObject($items, $count, $nested = false): string
346+
public function handleObject(Schema $items, int $count, bool $nested = false): string
331347
{
332348
$props = '[' . PHP_EOL;
333349
$cs = new ComponentSchema($items, 'unnamed');
@@ -361,53 +377,32 @@ public function handleObject($items, $count, $nested = false): string
361377
* @param $items
362378
* @param $count
363379
* @return string
380+
* @throws ExceptionInterface
381+
* @throws InvalidConfigException
382+
* @throws InvalidDefinitionException
383+
* @throws TypeErrorException
384+
* @throws UnresolvableReferenceException
364385
* @internal
365386
*/
366387
public function handleOneOf($items, $count): string
367388
{
368-
$fakerForADataType = [];
369389
$result = 'array_map(function () use ($faker, $uniqueFaker) {';
370390
foreach ($items->oneOf as $key => $aDataType) {
371391
/** @var Schema|Reference $aDataType */
372-
// $fakerForADataType[] = $this->{__FUNCTION__}($aDataType);
392+
373393
$a1 = $this->fakeForArray($aDataType, 1);
374394
$result .= '$dataType' . $key . ' = ' . $a1 . ';';
375395
}
376396
$ct = count($items->oneOf) - 1;
377397
$result .= 'return ${"dataType".rand(0, ' . $ct . ')};';
378-
// $items = $items->oneOf[$rand];
379-
380-
// $dataType'..'
381-
// return "";
382398
$result .= '}, range(1, ' . $count . '))';
383399
return $result;
384400
}
385401

386-
public function aString()
387-
{
388-
}
389-
390-
public function aNumber()
391-
{
392-
}
393-
394-
public function aInteger()
395-
{
396-
}
397-
398-
public function aBoolean()
399-
{
400-
}
401-
402-
public function aArray()
403-
{
404-
}
405-
406-
public function aObject()
407-
{
408-
}
409-
410-
public function aRefObj()
402+
public function wrapAsArray($aElementFaker, $uniqueItems, $count): string
411403
{
404+
return 'array_map(function () use ($faker, $uniqueFaker) {
405+
return ' . ($uniqueItems ? str_replace('$faker->', '$uniqueFaker->', $aElementFaker) : $aElementFaker) . ';
406+
}, range(1, ' . $count . '))';
412407
}
413408
}

tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ components:
121121
items:
122122
type: string
123123
uniqueItems: true
124-
125124
arr_arr_int_2: # [ [1, 2], [3, 4], [5, 6, 7] ]
126125
type: array
127126
items:

0 commit comments

Comments
 (0)