Completed
Push — master ( f34924...c8c8f9 )
by Paweł
13:31
created

getRandomValueForProductAttribute()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 18
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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