Completed
Push — master ( 1212b8...011c4b )
by Paweł
29:44 queued 19:42
created

getRandomValueForProductAttribute()   D

Complexity

Conditions 10
Paths 10

Size

Total Lines 29
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 29
rs 4.8196
cc 10
eloc 22
nc 10
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Bundle\CoreBundle\Fixture\Factory;
13
14
use Sylius\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption;
15
use Sylius\Component\Attribute\AttributeType\SelectAttributeType;
16
use Sylius\Component\Core\Formatter\StringInflector;
17
use Sylius\Component\Core\Model\ChannelInterface;
18
use Sylius\Component\Core\Model\ChannelPricingInterface;
19
use Sylius\Component\Core\Model\ImageInterface;
20
use Sylius\Component\Core\Model\ProductInterface;
21
use Sylius\Component\Core\Model\ProductVariantInterface;
22
use Sylius\Component\Core\Model\TaxonInterface;
23
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
24
use Sylius\Component\Locale\Model\LocaleInterface;
25
use Sylius\Component\Product\Generator\ProductVariantGeneratorInterface;
26
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
27
use Sylius\Component\Product\Model\ProductAttributeInterface;
28
use Sylius\Component\Product\Model\ProductAttributeValueInterface;
29
use Sylius\Component\Resource\Factory\FactoryInterface;
30
use Sylius\Component\Resource\Repository\RepositoryInterface;
31
use Symfony\Component\HttpFoundation\File\UploadedFile;
32
use Symfony\Component\OptionsResolver\Options;
33
use Symfony\Component\OptionsResolver\OptionsResolver;
34
use Webmozart\Assert\Assert;
35
36
/**
37
 * @author Kamil Kokot <[email protected]>
38
 */
39
class ProductExampleFactory extends AbstractExampleFactory implements ExampleFactoryInterface
40
{
41
    /**
42
     * @var FactoryInterface
43
     */
44
    private $productFactory;
45
46
    /**
47
     * @var FactoryInterface
48
     */
49
    private $productVariantFactory;
50
51
    /**
52
     * @var FactoryInterface
53
     */
54
    private $channelPricingFactory;
55
56
    /**
57
     * @var FactoryInterface
58
     */
59
    private $productTaxonFactory;
60
61
    /**
62
     * @var ProductVariantGeneratorInterface
63
     */
64
    private $variantGenerator;
65
66
    /**
67
     * @var FactoryInterface
68
     */
69
    private $productAttributeValueFactory;
70
71
    /**
72
     * @var FactoryInterface
73
     */
74
    private $productImageFactory;
75
76
    /**
77
     * @var ImageUploaderInterface
78
     */
79
    private $imageUploader;
80
81
    /**
82
     * @var SlugGeneratorInterface
83
     */
84
    private $slugGenerator;
85
86
    /**
87
     * @var RepositoryInterface
88
     */
89
    private $taxonRepository;
90
91
    /**
92
     * @var RepositoryInterface
93
     */
94
    private $productAttributeRepository;
95
96
    /**
97
     * @var RepositoryInterface
98
     */
99
    private $productOptionRepository;
100
101
    /**
102
     * @var RepositoryInterface
103
     */
104
    private $channelRepository;
105
106
    /**
107
     * @var RepositoryInterface
108
     */
109
    private $localeRepository;
110
111
    /**
112
     * @var \Faker\Generator
113
     */
114
    private $faker;
115
116
    /**
117
     * @var OptionsResolver
118
     */
119
    private $optionsResolver;
120
121
    /**
122
     * @param FactoryInterface $productFactory
123
     * @param FactoryInterface $productVariantFactory
124
     * @param FactoryInterface $channelPricing
125
     * @param ProductVariantGeneratorInterface $variantGenerator
126
     * @param FactoryInterface $productAttributeValueFactory
127
     * @param FactoryInterface $productImageFactory
128
     * @param FactoryInterface $productTaxonFactory
129
     * @param ImageUploaderInterface $imageUploader
130
     * @param SlugGeneratorInterface $slugGenerator
131
     * @param RepositoryInterface $taxonRepository
132
     * @param RepositoryInterface $productAttributeRepository
133
     * @param RepositoryInterface $productOptionRepository
134
     * @param RepositoryInterface $channelRepository
135
     * @param RepositoryInterface $localeRepository
136
     */
137
    public function __construct(
138
        FactoryInterface $productFactory,
139
        FactoryInterface $productVariantFactory,
140
        FactoryInterface $channelPricing,
141
        ProductVariantGeneratorInterface $variantGenerator,
142
        FactoryInterface $productAttributeValueFactory,
143
        FactoryInterface $productImageFactory,
144
        FactoryInterface $productTaxonFactory,
145
        ImageUploaderInterface $imageUploader,
146
        SlugGeneratorInterface $slugGenerator,
147
        RepositoryInterface $taxonRepository,
148
        RepositoryInterface $productAttributeRepository,
149
        RepositoryInterface $productOptionRepository,
150
        RepositoryInterface $channelRepository,
151
        RepositoryInterface $localeRepository
152
    ) {
153
        $this->productFactory = $productFactory;
154
        $this->productVariantFactory = $productVariantFactory;
155
        $this->channelPricingFactory = $channelPricing;
156
        $this->variantGenerator = $variantGenerator;
157
        $this->productAttributeValueFactory = $productAttributeValueFactory;
158
        $this->productImageFactory = $productImageFactory;
159
        $this->productTaxonFactory = $productTaxonFactory;
160
        $this->imageUploader = $imageUploader;
161
        $this->slugGenerator = $slugGenerator;
162
        $this->taxonRepository = $taxonRepository;
163
        $this->productAttributeRepository = $productAttributeRepository;
164
        $this->productOptionRepository = $productOptionRepository;
165
        $this->channelRepository = $channelRepository;
166
        $this->localeRepository = $localeRepository;
167
168
        $this->faker = \Faker\Factory::create();
169
        $this->optionsResolver = new OptionsResolver();
170
171
        $this->configureOptions($this->optionsResolver);
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function create(array $options = [])
178
    {
179
        $options = $this->optionsResolver->resolve($options);
180
181
        /** @var ProductInterface $product */
182
        $product = $this->productFactory->createNew();
183
        $product->setVariantSelectionMethod($options['variant_selection_method']);
184
        $product->setCode($options['code']);
185
        $product->setEnabled($options['enabled']);
186
        $product->setMainTaxon($options['main_taxon']);
187
        $product->setCreatedAt($this->faker->dateTimeBetween('-1 week', 'now'));
188
189
        $this->createTranslations($product, $options);
190
        $this->createRelations($product, $options);
191
        $this->createVariants($product, $options);
192
        $this->createImages($product, $options);
193
        $this->createProductTaxons($product, $options);
194
195
        return $product;
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201
    protected function configureOptions(OptionsResolver $resolver)
202
    {
203
        $resolver
204
            ->setDefault('name', function (Options $options) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
205
                return $this->faker->words(3, true);
206
            })
207
208
            ->setDefault('code', function (Options $options) {
209
                return StringInflector::nameToCode($options['name']);
210
            })
211
212
            ->setDefault('enabled', function (Options $options) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
213
                return $this->faker->boolean(90);
214
            })
215
            ->setAllowedTypes('enabled', 'bool')
216
217
            ->setDefault('short_description', function (Options $options) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
218
                return $this->faker->paragraph;
219
            })
220
221
            ->setDefault('description', function (Options $options) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
222
                return $this->faker->paragraphs(3, true);
223
            })
224
225
            ->setDefault('main_taxon', LazyOption::randomOne($this->taxonRepository))
226
            ->setAllowedTypes('main_taxon', ['null', 'string', TaxonInterface::class])
227
            ->setNormalizer('main_taxon', LazyOption::findOneBy($this->taxonRepository, 'code'))
228
229
            ->setDefault('taxons', LazyOption::randomOnes($this->taxonRepository, 3))
230
            ->setAllowedTypes('taxons', 'array')
231
            ->setNormalizer('taxons', LazyOption::findBy($this->taxonRepository, 'code'))
232
233
            ->setDefault('channels', LazyOption::randomOnes($this->channelRepository, 3))
234
            ->setAllowedTypes('channels', 'array')
235
            ->setNormalizer('channels', LazyOption::findBy($this->channelRepository, 'code'))
236
237
            ->setDefault('variant_selection_method', ProductInterface::VARIANT_SELECTION_MATCH)
238
            ->setAllowedValues('variant_selection_method', [ProductInterface::VARIANT_SELECTION_MATCH, ProductInterface::VARIANT_SELECTION_CHOICE])
239
240
            ->setDefault('product_attributes', [])
241
            ->setAllowedTypes('product_attributes', 'array')
242
            ->setNormalizer('product_attributes', function (Options $options, array $productAttributes) {
243
                $productAttributesValues = [];
244
                foreach ($productAttributes as $code => $value) {
245
                    foreach ($this->getLocales() as $localeCode) {
246
                        /** @var ProductAttributeInterface $productAttribute */
247
                        $productAttribute = $this->productAttributeRepository->findOneBy(['code' => $code]);
248
249
                        Assert::notNull($productAttribute);
250
251
                        /** @var ProductAttributeValueInterface $productAttributeValue */
252
                        $productAttributeValue = $this->productAttributeValueFactory->createNew();
253
                        $productAttributeValue->setAttribute($productAttribute);
254
                        $productAttributeValue->setValue($value ?: $this->getRandomValueForProductAttribute($productAttribute));
255
                        $productAttributeValue->setLocaleCode($localeCode);
256
257
                        $productAttributesValues[] = $productAttributeValue;
258
                    }
259
                }
260
261
                return $productAttributesValues;
262
            })
263
264
            ->setDefault('product_options', [])
265
            ->setAllowedTypes('product_options', 'array')
266
            ->setNormalizer('product_options', LazyOption::findBy($this->productOptionRepository, 'code'))
267
268
            ->setDefault('images', [])
269
            ->setAllowedTypes('images', 'array')
270
271
            ->setDefault('shipping_required', true)
272
        ;
273
    }
274
275
    /**
276
     * @param ProductInterface $product
277
     * @param array $options
278
     */
279
    private function createTranslations(ProductInterface $product, array $options)
280
    {
281
        foreach ($this->getLocales() as $localeCode) {
282
            $product->setCurrentLocale($localeCode);
283
            $product->setFallbackLocale($localeCode);
284
285
            $product->setName($options['name']);
286
            $product->setSlug($this->slugGenerator->generate($options['name']));
287
            $product->setShortDescription($options['short_description']);
288
            $product->setDescription($options['description']);
289
        }
290
    }
291
292
    /**
293
     * @param ProductInterface $product
294
     * @param array $options
295
     */
296
    private function createRelations(ProductInterface $product, array $options)
297
    {
298
        foreach ($options['channels'] as $channel) {
299
            $product->addChannel($channel);
300
        }
301
302
        foreach ($options['product_options'] as $option) {
303
            $product->addOption($option);
304
        }
305
306
        foreach ($options['product_attributes'] as $attribute) {
307
            $product->addAttribute($attribute);
308
        }
309
    }
310
311
    /**
312
     * @param ProductInterface $product
313
     * @param array $options
314
     */
315
    private function createVariants(ProductInterface $product, array $options)
316
    {
317
        try {
318
            $this->variantGenerator->generate($product);
319
        } catch (\InvalidArgumentException $exception) {
320
            /** @var ProductVariantInterface $productVariant */
321
            $productVariant = $this->productVariantFactory->createNew();
322
323
            $product->addVariant($productVariant);
324
        }
325
326
        $i = 0;
327
        /** @var ProductVariantInterface $productVariant */
328
        foreach ($product->getVariants() as $productVariant) {
329
            $productVariant->setName($this->faker->word);
330
            $productVariant->setAvailableOn($this->faker->dateTimeThisYear);
331
            $productVariant->setCode(sprintf('%s-variant-%d', $options['code'], $i));
332
            $productVariant->setOnHand($this->faker->randomNumber(1));
333
            $productVariant->setShippingRequired($options['shipping_required']);
334
335
            foreach ($this->channelRepository->findAll() as $channel) {
336
                $this->createChannelPricings($productVariant, $channel);
337
            }
338
339
            ++$i;
340
        }
341
    }
342
343
    /**
344
     * @param ProductVariantInterface $productVariant
345
     * @param ChannelInterface $channel
346
     */
347
    private function createChannelPricings(ProductVariantInterface $productVariant, ChannelInterface $channel)
348
    {
349
        /** @var ChannelPricingInterface $channelPricing */
350
        $channelPricing = $this->channelPricingFactory->createNew();
351
        $channelPricing->setChannel($channel);
352
        $channelPricing->setPrice($this->faker->randomNumber(3));
353
354
        $productVariant->addChannelPricing($channelPricing);
355
    }
356
357
    /**
358
     * @param ProductInterface $product
359
     * @param array $options
360
     */
361
    private function createImages(ProductInterface $product, array $options)
362
    {
363
        foreach ($options['images'] as $image) {
364
            $imagePath = array_shift($image);
365
            $uploadedImage = new UploadedFile($imagePath, basename($imagePath));
366
367
            /** @var ImageInterface $productImage */
368
            $productImage = $this->productImageFactory->createNew();
369
            $productImage->setFile($uploadedImage);
370
            $productImage->setType(end($image) ?: null);
371
372
            $this->imageUploader->upload($productImage);
373
374
            $product->addImage($productImage);
375
        }
376
    }
377
378
    /**
379
     * @param ProductInterface $product
380
     * @param array $options
381
     */
382
    private function createProductTaxons(ProductInterface $product, array $options)
383
    {
384
        foreach ($options['taxons'] as $taxon) {
385
            $productTaxon = $this->productTaxonFactory->createNew();
386
            $productTaxon->setProduct($product);
387
            $productTaxon->setTaxon($taxon);
388
389
            $product->addProductTaxon($productTaxon);
390
        }
391
    }
392
393
    /**
394
     * @return \Generator
395
     */
396
    private function getLocales()
397
    {
398
        /** @var LocaleInterface[] $locales */
399
        $locales = $this->localeRepository->findAll();
400
        foreach ($locales as $locale) {
401
            yield $locale->getCode();
402
        }
403
    }
404
405
    /**
406
     * @param ProductAttributeInterface $productAttribute
407
     *
408
     * @return mixed
409
     */
410
    private function getRandomValueForProductAttribute(ProductAttributeInterface $productAttribute)
411
    {
412
        switch ($productAttribute->getStorageType()) {
413
            case ProductAttributeValueInterface::STORAGE_BOOLEAN:
414
                return $this->faker->boolean;
415
            case ProductAttributeValueInterface::STORAGE_INTEGER:
416
                return $this->faker->numberBetween(0, 10000);
417
            case ProductAttributeValueInterface::STORAGE_FLOAT:
418
                return $this->faker->randomFloat(4, 0, 10000);
419
            case ProductAttributeValueInterface::STORAGE_TEXT:
420
                return $this->faker->sentence;
421
            case ProductAttributeValueInterface::STORAGE_DATE:
422
            case ProductAttributeValueInterface::STORAGE_DATETIME:
423
                return $this->faker->dateTimeThisCentury;
424
            case ProductAttributeValueInterface::STORAGE_JSON:
425
                if ($productAttribute->getType() == SelectAttributeType::TYPE) {
426
                    if ($productAttribute->getConfiguration()['multiple']) {
427
                        return array_keys($this->faker->randomElements(
428
                            $productAttribute->getConfiguration()['choices'],
429
                            $this->faker->randomKey($productAttribute->getConfiguration()['choices']) + 1
430
                        ));
431
                    }
432
433
                    return [$this->faker->randomKey($productAttribute->getConfiguration()['choices'])];
434
                }
435
            default:
436
                throw new \BadMethodCallException();
437
        }
438
    }
439
}
440