Completed
Push — master ( 94302c...12e4a7 )
by Paweł
104:24 queued 84:06
created

ProductExampleFactory::createVariants()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
cc 3
eloc 13
nc 4
nop 2
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\Core\Formatter\StringInflector;
16
use Sylius\Component\Core\Model\ImageInterface;
17
use Sylius\Component\Core\Model\ProductInterface;
18
use Sylius\Component\Core\Model\ProductVariantInterface;
19
use Sylius\Component\Core\Model\TaxonInterface;
20
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
21
use Sylius\Component\Locale\Model\LocaleInterface;
22
use Sylius\Component\Product\Generator\ProductVariantGeneratorInterface;
23
use Sylius\Component\Product\Model\ProductAttributeInterface;
24
use Sylius\Component\Product\Model\ProductAttributeValueInterface;
25
use Sylius\Component\Resource\Factory\FactoryInterface;
26
use Sylius\Component\Resource\Repository\RepositoryInterface;
27
use Symfony\Component\HttpFoundation\File\UploadedFile;
28
use Symfony\Component\OptionsResolver\Options;
29
use Symfony\Component\OptionsResolver\OptionsResolver;
30
use Webmozart\Assert\Assert;
31
32
/**
33
 * @author Kamil Kokot <[email protected]>
34
 */
35
final class ProductExampleFactory implements ExampleFactoryInterface
36
{
37
    /**
38
     * @var FactoryInterface
39
     */
40
    private $productFactory;
41
42
    /**
43
     * @var FactoryInterface
44
     */
45
    private $productVariantFactory;
46
47
    /**
48
     * @var ProductVariantGeneratorInterface
49
     */
50
    private $variantGenerator;
51
52
    /**
53
     * @var FactoryInterface
54
     */
55
    private $productImageFactory;
56
57
    /**
58
     * @var ImageUploaderInterface
59
     */
60
    private $imageUploader;
61
62
    /**
63
     * @var RepositoryInterface
64
     */
65
    private $localeRepository;
66
67
    /**
68
     * @var \Faker\Generator
69
     */
70
    private $faker;
71
72
    /**
73
     * @var OptionsResolver
74
     */
75
    private $optionsResolver;
76
77
    /**
78
     * @param FactoryInterface $productFactory
79
     * @param FactoryInterface $productVariantFactory
80
     * @param ProductVariantGeneratorInterface $variantGenerator
81
     * @param FactoryInterface $productAttributeValueFactory
82
     * @param FactoryInterface $productImageFactory
83
     * @param ImageUploaderInterface $imageUploader
84
     * @param RepositoryInterface $taxonRepository
85
     * @param RepositoryInterface $productAttributeRepository
86
     * @param RepositoryInterface $productOptionRepository
87
     * @param RepositoryInterface $channelRepository
88
     * @param RepositoryInterface $localeRepository
89
     */
90
    public function __construct(
91
        FactoryInterface $productFactory,
92
        FactoryInterface $productVariantFactory,
93
        ProductVariantGeneratorInterface $variantGenerator,
94
        FactoryInterface $productAttributeValueFactory,
95
        FactoryInterface $productImageFactory,
96
        ImageUploaderInterface $imageUploader,
97
        RepositoryInterface $taxonRepository,
98
        RepositoryInterface $productAttributeRepository,
99
        RepositoryInterface $productOptionRepository,
100
        RepositoryInterface $channelRepository,
101
        RepositoryInterface $localeRepository
102
    ) {
103
        $this->productFactory = $productFactory;
104
        $this->productVariantFactory = $productVariantFactory;
105
        $this->variantGenerator = $variantGenerator;
106
        $this->productImageFactory = $productImageFactory;
107
        $this->imageUploader = $imageUploader;
108
        $this->localeRepository = $localeRepository;
109
110
        $this->faker = \Faker\Factory::create();
111
        $this->optionsResolver =
112
            (new OptionsResolver())
113
                ->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...
114
                    return $this->faker->words(3, true);
115
                })
116
117
                ->setDefault('code', function (Options $options) {
118
                    return StringInflector::nameToCode($options['name']);
119
                })
120
121
                ->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...
122
                    return $this->faker->boolean(90);
123
                })
124
                ->setAllowedTypes('enabled', 'bool')
125
126
                ->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...
127
                    return $this->faker->paragraph;
128
                })
129
130
                ->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...
131
                    return $this->faker->paragraphs(3, true);
132
                })
133
134
                ->setDefault('main_taxon', LazyOption::randomOne($taxonRepository))
135
                ->setAllowedTypes('main_taxon', ['null', 'string', TaxonInterface::class])
136
                ->setNormalizer('main_taxon', LazyOption::findOneBy($taxonRepository, 'code'))
137
138
                ->setDefault('taxons', LazyOption::randomOnes($taxonRepository, 3))
139
                ->setAllowedTypes('taxons', 'array')
140
                ->setNormalizer('taxons', LazyOption::findBy($taxonRepository, 'code'))
141
142
                ->setDefault('channels', LazyOption::randomOnes($channelRepository, 3))
143
                ->setAllowedTypes('channels', 'array')
144
                ->setNormalizer('channels', LazyOption::findBy($channelRepository, 'code'))
145
146
                ->setDefault('product_attributes', [])
147
                ->setAllowedTypes('product_attributes', 'array')
148
                ->setNormalizer('product_attributes', function (Options $options, array $productAttributes) use ($productAttributeRepository, $productAttributeValueFactory) {
149
                    $productAttributesValues = [];
150
                    foreach ($productAttributes as $code => $value) {
151
                        /** @var ProductAttributeInterface $productAttribute */
152
                        $productAttribute = $productAttributeRepository->findOneBy(['code' => $code]);
153
154
                        Assert::notNull($productAttribute);
155
156
                        /** @var ProductAttributeValueInterface $productAttributeValue */
157
                        $productAttributeValue = $productAttributeValueFactory->createNew();
158
                        $productAttributeValue->setAttribute($productAttribute);
159
                        $productAttributeValue->setValue($value ?: $this->getRandomValueForProductAttribute($productAttribute));
160
161
                        $productAttributesValues[] = $productAttributeValue;
162
                    }
163
164
                    return $productAttributesValues;
165
                })
166
167
                ->setDefault('product_options', [])
168
                ->setAllowedTypes('product_options', 'array')
169
                ->setNormalizer('product_options', LazyOption::findBy($productOptionRepository, 'code'))
170
171
                ->setDefault('images', [])
172
                ->setAllowedTypes('images', 'array')
173
        ;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function create(array $options = [])
180
    {
181
        $options = $this->optionsResolver->resolve($options);
182
183
        /** @var ProductInterface $product */
184
        $product = $this->productFactory->createNew();
185
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
186
        $product->setCode($options['code']);
187
        $product->setEnabled($options['enabled']);
188
        $product->setMainTaxon($options['main_taxon']);
189
        $product->setCreatedAt($this->faker->dateTimeBetween('-1 week', 'now'));
190
191
        $this->createTranslations($product, $options);
192
        $this->createRelations($product, $options);
193
        $this->createVariants($product, $options);
194
        $this->createImages($product, $options);
195
196
        return $product;
197
    }
198
199
    /**
200
     * @param ProductInterface $product
201
     * @param array $options
202
     */
203
    private function createTranslations(ProductInterface $product, array $options)
204
    {
205
        foreach ($this->getLocales() as $localeCode) {
206
            $product->setCurrentLocale($localeCode);
207
            $product->setFallbackLocale($localeCode);
208
209
            $product->setName($options['name']);
210
            $product->setShortDescription($options['short_description']);
211
            $product->setDescription($options['description']);
212
        }
213
    }
214
215
    /**
216
     * @param ProductInterface $product
217
     * @param array $options
218
     */
219
    private function createRelations(ProductInterface $product, array $options)
220
    {
221
        foreach ($options['taxons'] as $taxon) {
222
            $product->addTaxon($taxon);
223
        }
224
225
        foreach ($options['channels'] as $channel) {
226
            $product->addChannel($channel);
227
        }
228
229
        foreach ($options['product_options'] as $option) {
230
            $product->addOption($option);
231
        }
232
233
        foreach ($options['product_attributes'] as $attribute) {
234
            $product->addAttribute($attribute);
235
        }
236
    }
237
238
    /**
239
     * @param ProductInterface $product
240
     * @param array $options
241
     */
242
    private function createVariants(ProductInterface $product, array $options)
243
    {
244
        try {
245
            $this->variantGenerator->generate($product);
246
        } catch (\InvalidArgumentException $exception) {
247
            /** @var ProductVariantInterface $productVariant */
248
            $productVariant = $this->productVariantFactory->createNew();
249
250
            $product->addVariant($productVariant);
251
        }
252
253
        $i = 0;
254
        /** @var ProductVariantInterface $productVariant */
255
        foreach ($product->getVariants() as $productVariant) {
256
            $productVariant->setAvailableOn($this->faker->dateTimeThisYear);
257
            $productVariant->setPrice($this->faker->randomNumber(4));
258
            $productVariant->setCode(sprintf('%s-variant#%d', $options['code'], $i));
259
            $productVariant->setOnHand($this->faker->randomNumber(1));
260
261
            ++$i;
262
        }
263
    }
264
265
    /**
266
     * @param ProductInterface $product
267
     * @param array $options
268
     */
269
    private function createImages(ProductInterface $product, array $options)
270
    {
271
        foreach ($options['images'] as $imageCode => $imagePath) {
272
            /** @var ImageInterface $productImage */
273
            $productImage = $this->productImageFactory->createNew();
274
            $productImage->setCode($imageCode);
275
            $productImage->setFile(new UploadedFile($imagePath, basename($imagePath)));
276
277
            $this->imageUploader->upload($productImage);
278
279
            $product->addImage($productImage);
280
        }
281
    }
282
283
    /**
284
     * @return \Generator
285
     */
286
    private function getLocales()
287
    {
288
        /** @var LocaleInterface[] $locales */
289
        $locales = $this->localeRepository->findAll();
290
        foreach ($locales as $locale) {
291
            yield $locale->getCode();
292
        }
293
    }
294
295
    /**
296
     * @param ProductAttributeInterface $productAttribute
297
     *
298
     * @return mixed
299
     */
300
    private function getRandomValueForProductAttribute(ProductAttributeInterface $productAttribute)
301
    {
302
        switch ($productAttribute->getStorageType()) {
303
            case ProductAttributeValueInterface::STORAGE_BOOLEAN:
304
                return $this->faker->boolean;
305
            case ProductAttributeValueInterface::STORAGE_INTEGER:
306
                return $this->faker->numberBetween(0, 10000);
307
            case ProductAttributeValueInterface::STORAGE_FLOAT:
308
                return $this->faker->randomFloat(4, 0, 10000);
309
            case ProductAttributeValueInterface::STORAGE_TEXT:
310
                return $this->faker->sentence;
311
            case ProductAttributeValueInterface::STORAGE_DATE:
312
            case ProductAttributeValueInterface::STORAGE_DATETIME:
313
                return $this->faker->dateTimeThisCentury;
314
            default:
315
                throw new \BadMethodCallException();
316
        }
317
    }
318
}
319