Issues (3641)

src/Spryker/Shared/Testify/AbstractDataBuilder.php (1 issue)

1
<?php
2
3
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace Spryker\Shared\Testify;
9
10
use Faker\Factory;
11
use Spryker\Shared\Kernel\Transfer\AbstractTransfer;
12
use Spryker\Shared\Testify\Exception\DependencyNotDefinedException;
13
use Spryker\Shared\Testify\Exception\FieldNotDefinedException;
14
use Spryker\Shared\Testify\Exception\RuleNotDefinedException;
15
16
abstract class AbstractDataBuilder
17
{
18
    /**
19
     * @var \Faker\Generator
20
     */
21
    protected static $faker;
22
23
    /**
24
     * @var array<string, string>
25
     */
26
    protected $defaultRules = [];
27
28
    /**
29
     * @var array<string, string>
30
     */
31
    protected $rules = [];
32
33
    /**
34
     * @var array<string, string>
35
     */
36
    protected $dependencies = [];
37
38
    /**
39
     * @var array
40
     */
41
    protected $nestedBuilders = [];
42
43
    /**
44
     * @var array<string, mixed>
45
     */
46
    protected $seedData = [];
47
48
    /**
49
     * @return \Spryker\Shared\Kernel\Transfer\AbstractTransfer
50
     */
51
    abstract protected function getTransfer();
52
53
    /**
54
     * @param string $builder
55
     *
56
     * @throws \Exception
57
     *
58
     * @return \Spryker\Shared\Testify\AbstractDataBuilder
59
     */
60
    abstract protected function locateDataBuilder($builder);
61
62
    /**
63
     * @param array<string, mixed> $seed
64
     */
65
    public function __construct($seed = [])
66
    {
67
        $this->seedData = $seed;
68
        $this->rules = $this->defaultRules;
69
70
        if (static::$faker === null) {
71
            static::$faker = Factory::create();
72
        }
73
    }
74
75
    /**
76
     * Removes all rules
77
     *
78
     * @return $this
79
     */
80
    public function makeEmpty()
81
    {
82
        $this->rules = [];
83
84
        return $this;
85
    }
86
87
    /**
88
     * @return $this
89
     */
90
    public function resetData()
91
    {
92
        $this->seedData = [];
93
94
        return $this;
95
    }
96
97
    /**
98
     * @param array<string, mixed> $seed
99
     *
100
     * @return $this
101
     */
102
    public function seed(array $seed = [])
103
    {
104
        $this->seedData += $seed;
105
106
        return $this;
107
    }
108
109
    /**
110
     * @param array<string>|string $rules
111
     *
112
     * @throws \Spryker\Shared\Testify\Exception\RuleNotDefinedException
113
     *
114
     * @return $this
115
     */
116
    public function with($rules)
117
    {
118
        if (!is_array($rules)) {
119
            $rules = [];
120
        }
121
        foreach ($rules as $rule) {
122
            if (!isset($this->defaultRules[$rule])) {
123
                throw new RuleNotDefinedException(sprintf('No rule for "%s" defined', $rule));
124
            }
125
            $this->rules[$rule] = $this->defaultRules[$rule];
126
        }
127
128
        return $this;
129
    }
130
131
    /**
132
     * @param array|string $rules
133
     *
134
     * @return $this
135
     */
136
    public function except($rules)
137
    {
138
        if (!is_array($rules)) {
139
            $rules = [];
140
        }
141
        foreach ($rules as $rule) {
142
            unset($this->rules[$rule]);
143
        }
144
145
        return $this;
146
    }
147
148
    /**
149
     * @return \Spryker\Shared\Kernel\Transfer\AbstractTransfer
150
     */
151
    public function build()
152
    {
153
        $transfer = $this->getTransfer();
154
        $transfer->fromArray($this->generateFields());
155
        $this->seedData = array_merge($this->getScalarValues($transfer), $this->seedData);
156
157
        $this->generateDependencies($transfer);
158
        $transfer->fromArray($this->seedData, true);
159
160
        return $transfer;
161
    }
162
163
    /**
164
     * @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer $transfer
165
     *
166
     * @return array
167
     */
168
    private function getScalarValues(AbstractTransfer $transfer)
169
    {
170
        return array_filter($transfer->toArray(false), 'is_scalar');
171
    }
172
173
    /**
174
     * @return array
175
     */
176
    protected function generateFields()
177
    {
178
        $data = [];
179
        foreach ($this->rules as $field => $rule) {
180
            $data[$field] = $this->generateFromRule($rule);
181
        }
182
183
        return $data;
184
    }
185
186
    /**
187
     * @param string $field
188
     * @param array $override
189
     * @param bool $randomize
190
     *
191
     * @throws \Spryker\Shared\Testify\Exception\FieldNotDefinedException
192
     *
193
     * @return void
194
     */
195
    protected function buildDependency($field, $override = [], $randomize = false)
196
    {
197
        if (!isset($this->dependencies[$field])) {
198
            throw new FieldNotDefinedException(sprintf('Field "%s" not defined in dependencies list', $field));
199
        }
200
        $builder = $this->locateDataBuilder($this->dependencies[$field]);
201
        $builder->seed($override);
202
        $this->addDependencyBuilder($field, $builder, $randomize);
203
    }
204
205
    /**
206
     * @param string $field
207
     * @param \Spryker\Shared\Testify\AbstractDataBuilder $builder
208
     * @param bool $randomize
209
     *
210
     * @return void
211
     */
212
    protected function addDependencyBuilder($field, $builder, $randomize)
213
    {
214
        $this->nestedBuilders[] = [$field, $builder, $randomize];
215
    }
216
217
    /**
218
     * @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer $transfer
219
     *
220
     * @throws \Spryker\Shared\Testify\Exception\DependencyNotDefinedException
221
     *
222
     * @return void
223
     */
224
    protected function generateDependencies(AbstractTransfer $transfer)
225
    {
226
        foreach ($this->nestedBuilders as $builderInfo) {
227
            [$name, $dependencyBuilder, $randomize] = $builderInfo;
228
229
            // add currently generated values
230
            if (!$randomize) {
231
                $dependencyBuilder->seed($this->seedData);
232
            }
233
            $nestedTransfer = $dependencyBuilder->build();
234
235
            // reuse generated values from nested objects
236
            if (!$randomize) {
237
                $this->seedData = array_merge($this->seedData, $dependencyBuilder->getSeedData());
238
            }
239
240
            if (method_exists($transfer, 'add' . $name)) {
241
                /** @var callable $callable */
242
                $callable = [$transfer, 'add' . $name];
243
                call_user_func($callable, $nestedTransfer);
244
245
                continue;
246
            }
247
248
            if (method_exists($transfer, 'set' . $name)) {
249
                /** @var callable $callable */
250
                $callable = [$transfer, 'set' . $name];
251
                call_user_func($callable, $nestedTransfer);
252
253
                continue;
254
            }
255
256
            throw new DependencyNotDefinedException(sprintf('Dependency "%s" not defined in "%s"', $name, static::class));
257
        }
258
    }
259
260
    /**
261
     * @SuppressWarning(PHPMD.EvalSniff)
262
     *
263
     * @param string $rule
264
     *
265
     * @return string|bool
266
     */
267
    protected function generateFromRule($rule)
268
    {
269
        if (strpos($rule, '=') === 0) {
270
            return substr($rule, 1);
271
        }
272
273
        // @codingStandardsIgnoreStart
274
        if (strpos($rule, '(') !== false) {
275
            return eval("return static::\$faker->$rule;");
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
276
        }
277
        return eval("return static::\$faker->$rule();");
278
        // @codingStandardsIgnoreEnd
279
    }
280
281
    /**
282
     * @return array<string, mixed>
283
     */
284
    public function getSeedData()
285
    {
286
        return $this->seedData;
287
    }
288
}
289