Completed
Push — master ( fcfa74...4ea0bb )
by Paweł
20s
created

src/Sylius/Behat/Context/Setup/ProductContext.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Behat\Context\Setup;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Gherkin\Node\TableNode;
16
use Behat\Mink\Element\NodeElement;
17
use Doctrine\Common\Persistence\ObjectManager;
18
use Sylius\Component\Attribute\Factory\AttributeFactoryInterface;
19
use Sylius\Component\Core\Formatter\StringInflector;
20
use Sylius\Component\Core\Model\ChannelInterface;
21
use Sylius\Component\Core\Model\ChannelPricingInterface;
22
use Sylius\Component\Core\Model\ImageInterface;
23
use Sylius\Component\Core\Model\ProductInterface;
24
use Sylius\Component\Core\Model\ProductTranslationInterface;
25
use Sylius\Component\Core\Model\ProductVariantInterface;
26
use Sylius\Component\Core\Pricing\Calculators;
27
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
28
use Sylius\Behat\Service\SharedStorageInterface;
29
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
30
use Sylius\Component\Product\Factory\ProductFactoryInterface;
31
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
32
use Sylius\Component\Product\Model\ProductAttributeInterface;
33
use Sylius\Component\Product\Model\ProductAttributeValueInterface;
34
use Sylius\Component\Product\Model\ProductOptionInterface;
35
use Sylius\Component\Product\Model\ProductOptionValueInterface;
36
use Sylius\Component\Resource\Factory\FactoryInterface;
37
use Sylius\Component\Shipping\Model\ShippingCategoryInterface;
38
use Sylius\Component\Resource\Model\TranslationInterface;
39
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
40
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
41
use Symfony\Component\HttpFoundation\File\UploadedFile;
42
use Webmozart\Assert\Assert;
43
44
/**
45
 * @author Arkadiusz Krakowiak <[email protected]>
46
 * @author Mateusz Zalewski <[email protected]>
47
 * @author Magdalena Banasiak <[email protected]>
48
 */
49
final class ProductContext implements Context
50
{
51
    /**
52
     * @var SharedStorageInterface
53
     */
54
    private $sharedStorage;
55
56
    /**
57
     * @var ProductRepositoryInterface
58
     */
59
    private $productRepository;
60
61
    /**
62
     * @var ProductFactoryInterface
63
     */
64
    private $productFactory;
65
66
    /**
67
     * @var FactoryInterface
68
     */
69
    private $productTranslationFactory;
70
71
    /**
72
     * @var AttributeFactoryInterface
73
     */
74
    private $productAttributeFactory;
75
76
    /**
77
     * @var FactoryInterface
78
     */
79
    private $productVariantFactory;
80
81
    /**
82
     * @var FactoryInterface
83
     */
84
    private $channelPricingFactory;
85
86
    /**
87
     * @var FactoryInterface
88
     */
89
    private $attributeValueFactory;
90
91
    /**
92
     * @var FactoryInterface
93
     */
94
    private $productOptionFactory;
95
96
    /**
97
     * @var FactoryInterface
98
     */
99
    private $productOptionValueFactory;
100
101
    /**
102
     * @var FactoryInterface
103
     */
104
    private $productImageFactory;
105
106
    /**
107
     * @var ObjectManager
108
     */
109
    private $objectManager;
110
111
    /**
112
     * @var ProductVariantResolverInterface
113
     */
114
    private $defaultVariantResolver;
115
116
    /**
117
     * @var ImageUploaderInterface
118
     */
119
    private $imageUploader;
120
121
    /**
122
     * @var SlugGeneratorInterface
123
     */
124
    private $slugGenerator;
125
126
    /**
127
     * @var array
128
     */
129
    private $minkParameters;
130
131
    /**
132
     * @param SharedStorageInterface $sharedStorage
133
     * @param ProductRepositoryInterface $productRepository
134
     * @param ProductFactoryInterface $productFactory
135
     * @param FactoryInterface $productTranslationFactory
136
     * @param AttributeFactoryInterface $productAttributeFactory
137
     * @param FactoryInterface $attributeValueFactory
138
     * @param FactoryInterface $productVariantFactory
139
     * @param FactoryInterface $channelPricingFactory
140
     * @param FactoryInterface $productOptionFactory
141
     * @param FactoryInterface $productOptionValueFactory
142
     * @param FactoryInterface $productImageFactory
143
     * @param ObjectManager $objectManager
144
     * @param ProductVariantResolverInterface $defaultVariantResolver
145
     * @param ImageUploaderInterface $imageUploader
146
     * @param SlugGeneratorInterface $slugGenerator
147
     * @param array $minkParameters
148
     */
149
    public function __construct(
150
        SharedStorageInterface $sharedStorage,
151
        ProductRepositoryInterface $productRepository,
152
        ProductFactoryInterface $productFactory,
153
        FactoryInterface $productTranslationFactory,
154
        AttributeFactoryInterface $productAttributeFactory,
155
        FactoryInterface $attributeValueFactory,
156
        FactoryInterface $productVariantFactory,
157
        FactoryInterface $channelPricingFactory,
158
        FactoryInterface $productOptionFactory,
159
        FactoryInterface $productOptionValueFactory,
160
        FactoryInterface $productImageFactory,
161
        ObjectManager $objectManager,
162
        ProductVariantResolverInterface $defaultVariantResolver,
163
        ImageUploaderInterface $imageUploader,
164
        SlugGeneratorInterface $slugGenerator,
165
        array $minkParameters
166
    ) {
167
        $this->sharedStorage = $sharedStorage;
168
        $this->productRepository = $productRepository;
169
        $this->productFactory = $productFactory;
170
        $this->productTranslationFactory = $productTranslationFactory;
171
        $this->productAttributeFactory = $productAttributeFactory;
172
        $this->attributeValueFactory = $attributeValueFactory;
173
        $this->productVariantFactory = $productVariantFactory;
174
        $this->channelPricingFactory = $channelPricingFactory;
175
        $this->productOptionFactory = $productOptionFactory;
176
        $this->productOptionValueFactory = $productOptionValueFactory;
177
        $this->productImageFactory = $productImageFactory;
178
        $this->objectManager = $objectManager;
179
        $this->defaultVariantResolver = $defaultVariantResolver;
180
        $this->imageUploader = $imageUploader;
181
        $this->slugGenerator = $slugGenerator;
182
        $this->minkParameters = $minkParameters;
183
    }
184
185
    /**
186
     * @Given the store has a product :productName
187
     * @Given the store has a :productName product
188
     * @Given I added a product :productName
189
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+")$/
190
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") in ("[^"]+" channel)$/
191
     */
192
    public function storeHasAProductPricedAt($productName, $price = 0, ChannelInterface $channel = null)
193
    {
194
        $product = $this->createProduct($productName, $price, null, $channel);
195
196
        $product->setDescription('Awesome '.$productName);
197
198
        if (null !== $channel) {
199
            $product->addChannel($channel);
200
        } else if ($this->sharedStorage->has('channel')) {
201
            $product->addChannel($this->sharedStorage->get('channel'));
202
        }
203
204
        $this->saveProduct($product);
205
    }
206
207
    /**
208
     * @Given /^(this product) is also priced at ("[^"]+") in ("[^"]+" channel)$/
209
     */
210
    public function thisProductIsAlsoPricedAtInChannel(ProductInterface $product, $price, ChannelInterface $channel)
211
    {
212
        $product->addChannel($channel);
213
214
        /** @var ProductVariantInterface $productVariant */
215
        $productVariant = $this->defaultVariantResolver->getVariant($product);
216
217
        /** @var ChannelPricingInterface $channelPricing */
218
        $channelPricing = $this->channelPricingFactory->createNew();
219
        $channelPricing->setPrice($price);
220
        $channelPricing->setChannel($channel);
221
222
        $productVariant->addChannelPricing($channelPricing);
223
224
        $this->objectManager->flush();
225
    }
226
227
    /**
228
     * @Given the store( also) has a product :productName with code :code
229
     * @Given the store( also) has a product :productName with code :code, created at :date
230
     */
231
    public function storeHasProductWithCode($productName, $code, $date = null)
232
    {
233
        $product = $this->createProduct($productName, 0, $date);
234
235
        $product->setCode($code);
236
237
        if ($this->sharedStorage->has('channel')) {
238
            $product->addChannel($this->sharedStorage->get('channel'));
239
        }
240
241
        $this->saveProduct($product);
242
    }
243
244
    /**
245
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") available in (channel "[^"]+") and (channel "[^"]+")$/
246
     */
247
    public function storeHasAProductPricedAtAvailableInChannels($productName, $price = 0, ...$channels)
248
    {
249
        $product = $this->createProduct($productName, $price);
250
        /** @var ProductVariantInterface $productVariant */
251
        $productVariant = $this->defaultVariantResolver->getVariant($product);
252
253
        $product->setDescription('Awesome '.$productName);
254
255
        foreach ($channels as $channel) {
256
            $product->addChannel($channel);
257
            if (!$productVariant->hasChannelPricingForChannel($channel)) {
258
                $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
259
            }
260
        }
261
262
        $this->saveProduct($product);
263
    }
264
265
    /**
266
     * @Given /^(this product) is named "([^"]+)" (in the "([^"]+)" locale)$/
267
     * @Given /^the (product "[^"]+") is named "([^"]+)" (in the "([^"]+)" locale)$/
268
     */
269
    public function thisProductIsNamedIn(ProductInterface $product, $name, $locale)
270
    {
271
        $this->addProductTranslation($product, $name, $locale);
272
273
        $this->objectManager->flush();
274
    }
275
276
    /**
277
     * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
278
     */
279
    public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale)
280
    {
281
        $product = $this->createProduct($firstName);
282
283
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
284
        foreach ($names as $name => $locale) {
285
            $this->addProductTranslation($product, $name, $locale);
286
        }
287
288
        $this->saveProduct($product);
289
    }
290
291
    /**
292
     * @Given the store has a :productName configurable product
293
     */
294
    public function storeHasAConfigurableProduct($productName)
295
    {
296
        /** @var ProductInterface $product */
297
        $product = $this->productFactory->createNew();
298
299
        $product->setName($productName);
300
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
301
        $product->setSlug($this->slugGenerator->generate($productName));
302
303
        $product->setDescription('Awesome '.$productName);
304
305
        if ($this->sharedStorage->has('channel')) {
306
            $channel = $this->sharedStorage->get('channel');
307
            $product->addChannel($channel);
308
        }
309
310
        $this->saveProduct($product);
311
    }
312
313
    /**
314
     * @Given the store has( also) :firstProductName and :secondProductName products
315
     * @Given the store has( also) :firstProductName, :secondProductName and :thirdProductName products
316
     * @Given the store has( also) :firstProductName, :secondProductName, :thirdProductName and :fourthProductName products
317
     */
318
    public function theStoreHasProducts(...$productsNames)
319
    {
320
        foreach ($productsNames as $productName) {
321
            $this->saveProduct($this->createProduct($productName));
322
        }
323
    }
324
325
    /**
326
     * @Given /^(this channel) has "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" products$/
327
     */
328
    public function thisChannelHasProducts(ChannelInterface $channel, ...$productsNames)
329
    {
330
        foreach ($productsNames as $productName) {
331
            $product = $this->createProduct($productName);
332
            $product->addChannel($channel);
333
334
            $this->saveProduct($product);
335
        }
336
    }
337
338
    /**
339
     * @Given /^the (product "[^"]+") has(?:| a) "([^"]+)" variant priced at ("[^"]+")$/
340
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/
341
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") in ("[^"]+" channel)$/
342
     */
343
    public function theProductHasVariantPricedAt(
344
        ProductInterface $product,
345
        $productVariantName,
346
        $price,
347
        ChannelInterface $channel = null
348
    ) {
349
        $this->createProductVariant(
350
            $product,
351
            $productVariantName,
352
            $price,
353
            StringInflector::nameToUppercaseCode($productVariantName),
354
            (null !== $channel) ? $channel : $this->sharedStorage->get('channel')
355
        );
356
    }
357
358
    /**
359
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/
360
     */
361
    public function theProductHasVariantPricedAtIdentifiedBy(
362
        ProductInterface $product,
363
        $productVariantName,
364
        $price,
365
        $code
366
    ) {
367
        $this->createProductVariant($product, $productVariantName, $price, $code, $this->sharedStorage->get('channel'));
368
    }
369
370
    /**
371
     * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/
372
     * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/
373
     */
374
    public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel)
375
    {
376
        $product = $this->createProduct($productName, 0, null, $channel);
377
378
        $product->setDescription('Awesome ' . $productName);
379
        $product->addChannel($channel);
380
381
        $this->saveProduct($product);
382
    }
383
384
    /**
385
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
386
     */
387
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
388
    {
389
        /** @var ProductVariantInterface $variant */
390
        $variant = $this->defaultVariantResolver->getVariant($product);
391
        $variant->setTaxCategory($taxCategory);
392
393
        $this->objectManager->flush();
394
    }
395
396
    /**
397
     * @Given /^(it) comes in the following variations:$/
398
     */
399
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
400
    {
401
        foreach ($table->getHash() as $variantHash) {
402
            /** @var ProductVariantInterface $variant */
403
            $variant = $this->productVariantFactory->createNew();
404
405
            $variant->setName($variantHash['name']);
406
            $variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
407
            $variant->addChannelPricing($this->createChannelPricingForChannel(
408
                $this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
409
                $this->sharedStorage->get('channel')
410
            ));
411
            $variant->setProduct($product);
412
            $product->addVariant($variant);
413
        }
414
415
        $this->objectManager->flush();
416
    }
417
418
    /**
419
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
420
     */
421
    public function productVariantBelongsToTaxCategory(
422
        ProductVariantInterface $productVariant,
423
        TaxCategoryInterface $taxCategory
424
    ) {
425
        $productVariant->setTaxCategory($taxCategory);
426
        $this->objectManager->flush($productVariant);
427
    }
428
429
    /**
430
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" with value "([^"]+)"$/
431
     */
432
    public function thisProductHasAttributeWithValue(ProductInterface $product, $productAttributeType, $productAttributeName, $value)
433
    {
434
        $attribute = $this->createProductAttribute($productAttributeType,$productAttributeName);
435
        $attributeValue = $this->createProductAttributeValue($value, $attribute);
436
        $product->addAttribute($attributeValue);
437
438
        $this->objectManager->flush();
439
    }
440
441
    /**
442
     * @Given /^(this product) has percent attribute "([^"]+)" with value ([^"]+)%$/
443
     */
444
    public function thisProductHasPercentAttributeWithValue(ProductInterface $product, $productAttributeName, $value)
445
    {
446
        $attribute = $this->createProductAttribute('percent',$productAttributeName);
447
        $attributeValue = $this->createProductAttributeValue($value/100, $attribute);
448
        $product->addAttribute($attributeValue);
449
450
        $this->objectManager->flush();
451
    }
452
453
    /**
454
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" set to "([^"]+)"$/
455
     */
456
    public function thisProductHasCheckboxAttributeWithValue(ProductInterface $product, $productAttributeType, $productAttributeName, $value)
457
    {
458
        $attribute = $this->createProductAttribute($productAttributeType, $productAttributeName);
459
        $booleanValue = ('Yes' === $value);
460
        $attributeValue = $this->createProductAttributeValue($booleanValue, $attribute);
0 ignored issues
show
$booleanValue is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
461
        $product->addAttribute($attributeValue);
462
463
        $this->objectManager->flush();
464
    }
465
466
    /**
467
     * @Given /^(this product) has percent attribute "([^"]+)" at position (\d+)$/
468
     */
469
    public function thisProductHasPercentAttributeWithValueAtPosition(ProductInterface $product, $productAttributeName, $position)
470
    {
471
        $attribute = $this->createProductAttribute('percent',$productAttributeName);
472
        $attribute->setPosition($position);
473
        $attributeValue = $this->createProductAttributeValue(rand(1,100)/100, $attribute);
474
475
        $product->addAttribute($attributeValue);
476
477
        $this->objectManager->flush();
478
    }
479
480
    /**
481
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" with date "([^"]+)"$/
482
     */
483
    public function thisProductHasDateTimeAttributeWithDate(ProductInterface $product, $productAttributeType, $productAttributeName, $date)
484
    {
485
        $attribute = $this->createProductAttribute($productAttributeType, $productAttributeName);
486
        $attributeValue = $this->createProductAttributeValue(new \DateTime($date), $attribute);
0 ignored issues
show
new \DateTime($date) is of type object<DateTime>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
487
488
        $product->addAttribute($attributeValue);
489
490
        $this->objectManager->flush();
491
    }
492
493
    /**
494
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/
495
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)", "([^"]+)" and "([^"]+)"$/
496
     */
497
    public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, ...$values)
498
    {
499
        /** @var ProductOptionInterface $option */
500
        $option = $this->productOptionFactory->createNew();
501
502
        $option->setName($optionName);
503
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
504
505
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
506
507
        foreach ($values as $key => $value) {
508
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
509
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
510
        }
511
512
        $product->addOption($option);
513
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
514
515
        $this->objectManager->persist($option);
516
        $this->objectManager->flush();
517
    }
518
519
    /**
520
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
521
     * @When product :product quantity is changed to :quantity
522
     */
523
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
524
    {
525
        /** @var ProductVariantInterface $productVariant */
526
        $productVariant = $this->defaultVariantResolver->getVariant($product);
527
        $productVariant->setOnHand($quantity);
528
529
        $this->objectManager->flush();
530
    }
531
532
    /**
533
     * @Given /^the (product "([^"]+)") is out of stock$/
534
     */
535
    public function theProductIsOutOfStock(ProductInterface $product)
536
    {
537
        /** @var ProductVariantInterface $productVariant */
538
        $productVariant = $this->defaultVariantResolver->getVariant($product);
539
        $productVariant->setTracked(true);
540
        $productVariant->setOnHand(0);
541
542
        $this->objectManager->flush();
543
    }
544
545
    /**
546
     * @When other customer has bought :quantity :product products by this time
547
     */
548
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
549
    {
550
        /** @var ProductVariantInterface $productVariant */
551
        $productVariant = $this->defaultVariantResolver->getVariant($product);
552
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
553
554
        $this->objectManager->flush();
555
    }
556
557
    /**
558
     * @Given /^(this product) is tracked by the inventory$/
559
     * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/
560
     */
561
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
562
    {
563
        /** @var ProductVariantInterface $productVariant */
564
        $productVariant = $this->defaultVariantResolver->getVariant($product);
565
        $productVariant->setTracked(true);
566
567
        $this->objectManager->flush();
568
    }
569
570
    /**
571
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
572
     */
573
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, $price)
574
    {
575
        /** @var ProductVariantInterface $variant */
576
        $variant = $this->productVariantFactory->createNew();
577
578
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
579
580
        $variant->addOptionValue($optionValue);
581
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
582
        $variant->setCode(sprintf("%s_%s", $product->getCode(), $optionValueName));
583
584
        $product->addVariant($variant);
585
        $this->objectManager->flush();
586
    }
587
588
    /**
589
     * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category
590
     * @Given /^(this product) "([^"]+)" size belongs to ("([^"]+)" shipping category)$/
591
     */
592
    public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory)
593
    {
594
        $code = sprintf("%s_%s", $product->getCode(), $optionValueName);
595
        /** @var ProductVariantInterface $productVariant */
596
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
597
            return $code === $variant->getCode();
598
        })->first();
599
        
600
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
601
        
602
        $productVariant->setShippingCategory($shippingCategory);
603
        $this->objectManager->flush();
604
    }
605
606
    /**
607
     * @Given /^(this product) has (this product option)$/
608
     * @Given /^(this product) has a ("[^"]+" option)$/
609
     * @Given /^(this product) has an ("[^"]+" option)$/
610
     */
611
    public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option)
612
    {
613
        $product->addOption($option);
614
615
        $this->objectManager->flush();
616
    }
617
618
    /**
619
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
620
     */
621
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
622
    {
623
        $productVariant->setTracked(true);
624
        $productVariant->setOnHand($quantity);
625
626
        $this->objectManager->flush();
627
    }
628
629
    /**
630
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
631
     */
632
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
633
    {
634
        $productVariant->setTracked(true);
635
636
        $this->objectManager->flush();
637
    }
638
639
    /**
640
     * @Given /^(this product)'s price is ("[^"]+")$/
641
     * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
642
     */
643
    public function theProductChangedItsPriceTo(ProductInterface $product, $price)
644
    {
645
        /** @var ProductVariantInterface $productVariant */
646
        $productVariant = $this->defaultVariantResolver->getVariant($product);
647
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
648
        $channelPricing->setPrice($price);
649
650
        $this->objectManager->flush();
651
    }
652
653
    /**
654
     * @Given /^(this product) has(?:| also) an image "([^"]+)" with a code "([^"]+)"$/
655
     * @Given /^the ("[^"]+" product) has(?:| also) an image "([^"]+)" with a code "([^"]+)"$/
656
     */
657
    public function thisProductHasAnImageWithACode(ProductInterface $product, $imagePath, $imageCode)
658
    {
659
        $filesPath = $this->getParameter('files_path');
660
661
        /** @var ImageInterface $productImage */
662
        $productImage = $this->productImageFactory->createNew();
663
        $productImage->setFile(new UploadedFile($filesPath.$imagePath, basename($imagePath)));
664
        $productImage->setCode($imageCode);
665
        $this->imageUploader->upload($productImage);
666
667
        $product->addImage($productImage);
668
669
        $this->objectManager->flush($product);
670
    }
671
672
    /**
673
     * @Given /^(it) has different prices for different channels and currencies$/
674
     */
675
    public function itHasDifferentPricesForDifferentChannelsAndCurrencies(ProductInterface $product)
676
    {
677
        /** @var ProductVariantInterface $variant */
678
        $variant = $this->defaultVariantResolver->getVariant($product);
679
680
        $variant->setPricingCalculator(Calculators::CHANNEL_AND_CURRENCY_BASED);
0 ignored issues
show
The method setPricingCalculator() does not seem to exist on object<Sylius\Component\...roductVariantInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
681
    }
682
683
    /**
684
     * @Given /^(it) has price ("[^"]+") for ("[^"]+" channel) and "([^"]+)" currency$/
685
     */
686
    public function itHasPriceForChannelAndCurrency(
687
        ProductInterface $product,
688
        $price,
689
        ChannelInterface $channel,
690
        $currency
691
    ) {
692
        /** @var ProductVariantInterface $variant */
693
        $variant = $this->defaultVariantResolver->getVariant($product);
694
695
        $pricingConfiguration = $variant->getPricingConfiguration();
0 ignored issues
show
The method getPricingConfiguration() does not seem to exist on object<Sylius\Component\...roductVariantInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
696
        $pricingConfiguration[$channel->getCode()][$currency] = $price;
697
698
        $variant->setPricingConfiguration($pricingConfiguration);
0 ignored issues
show
The method setPricingConfiguration() does not seem to exist on object<Sylius\Component\...roductVariantInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
699
700
        $this->objectManager->flush();
701
    }
702
703
    /**
704
     * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/
705
     */
706
    public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory)
707
    {
708
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
709
        $this->objectManager->flush();
710
    }
711
712
    /**
713
     * @param string $type
714
     * @param string $name
715
     * @param string|null $code
716
     *
717
     * @return ProductAttributeInterface
718
     */
719
    private function createProductAttribute($type, $name, $code = null)
720
    {
721
        $productAttribute = $this->productAttributeFactory->createTyped($type);
722
723
        if (null === $code) {
724
            $code = StringInflector::nameToCode($name);
725
        }
726
727
        $productAttribute->setCode($code);
728
        $productAttribute->setName($name);
729
730
        $this->objectManager->persist($productAttribute);
731
732
        return $productAttribute;
733
    }
734
735
    /**
736
     * @param string $value
737
     * @param ProductAttributeInterface $attribute
738
     *
739
     * @return ProductAttributeValueInterface
740
     */
741
    private function createProductAttributeValue($value, ProductAttributeInterface $attribute)
742
    {
743
        /** @var ProductAttributeValueInterface $attributeValue */
744
        $attributeValue = $this->attributeValueFactory->createNew();
745
        $attributeValue->setAttribute($attribute);
746
        $attributeValue->setValue($value);
747
748
        $this->objectManager->persist($attributeValue);
749
750
        return $attributeValue;
751
    }
752
753
    /**
754
     * @param string $price
755
     *
756
     * @return int
757
     */
758
    private function getPriceFromString($price)
759
    {
760
        return (int) round(($price * 100), 2);
761
    }
762
763
    /**
764
     * @param string $productName
765
     * @param int $price
766
     * @param string|null $date
767
     * @param ChannelInterface|null $channel
768
     *
769
     * @return ProductInterface
770
     */
771
    private function createProduct($productName, $price = 0, $date = null, ChannelInterface $channel = null)
772
    {
773
        /** @var ProductInterface $product */
774
        $product = $this->productFactory->createWithVariant();
775
776
        $product->setName($productName);
777
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
778
        $product->setSlug($this->slugGenerator->generate($productName));
779
        $product->setCreatedAt(new \DateTime($date));
780
781
        /** @var ProductVariantInterface $productVariant */
782
        $productVariant = $this->defaultVariantResolver->getVariant($product);
783
784
        if (null === $channel && $this->sharedStorage->has('channel')) {
785
            $channel = $this->sharedStorage->get('channel');
786
        }
787
788
        if (null !== $channel) {
789
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
790
        }
791
        $productVariant->setCode($product->getCode());
792
793
        return $product;
794
    }
795
796
    /**
797
     * @param ProductOptionInterface $option
798
     * @param string $value
799
     * @param string $code
800
     *
801
     * @return ProductOptionValueInterface
802
     */
803
    private function addProductOption(ProductOptionInterface $option, $value, $code)
804
    {
805
        /** @var ProductOptionValueInterface $optionValue */
806
        $optionValue = $this->productOptionValueFactory->createNew();
807
808
        $optionValue->setValue($value);
809
        $optionValue->setCode($code);
810
        $optionValue->setOption($option);
811
812
        $option->addValue($optionValue);
813
814
        return $optionValue;
815
    }
816
817
    /**
818
     * @param ProductInterface $product
819
     */
820
    private function saveProduct(ProductInterface $product)
821
    {
822
        $this->productRepository->add($product);
823
        $this->sharedStorage->set('product', $product);
824
    }
825
826
    /**
827
     * @param string $name
828
     *
829
     * @return NodeElement
830
     */
831
    private function getParameter($name)
832
    {
833
        return isset($this->minkParameters[$name]) ? $this->minkParameters[$name] : null;
834
    }
835
836
    /**
837
     * @param ProductInterface $product
838
     * @param $productVariantName
839
     * @param int $price
840
     * @param string $code
841
     * @param ChannelInterface $channel
842
     */
843
    private function createProductVariant(
844
        ProductInterface $product,
845
        $productVariantName,
846
        $price,
847
        $code,
848
        ChannelInterface $channel = null
849
    ) {
850
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
851
852
        /** @var ProductVariantInterface $variant */
853
        $variant = $this->productVariantFactory->createNew();
854
855
        $variant->setName($productVariantName);
856
        $variant->setCode($code);
857
        $variant->setProduct($product);
858
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
859
860
        $product->addVariant($variant);
861
862
        $this->objectManager->flush();
863
864
        $this->sharedStorage->set('variant', $variant);
865
    }
866
867
    /**
868
     * @param ProductInterface $product
869
     * @param string $name
870
     * @param string $locale
871
     */
872
    private function addProductTranslation(ProductInterface $product, $name, $locale)
873
    {
874
        /** @var ProductTranslationInterface|TranslationInterface $translation */
875
        $translation = $this->productTranslationFactory->createNew();
876
        $translation->setLocale($locale);
0 ignored issues
show
The method setLocale does only exist in Sylius\Component\Resourc...el\TranslationInterface, but not in Sylius\Component\Core\Mo...uctTranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
877
        $translation->setName($name);
0 ignored issues
show
The method setName does only exist in Sylius\Component\Core\Mo...uctTranslationInterface, but not in Sylius\Component\Resourc...el\TranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
878
        $translation->setSlug($this->slugGenerator->generate($name));
0 ignored issues
show
The method setSlug does only exist in Sylius\Component\Core\Mo...uctTranslationInterface, but not in Sylius\Component\Resourc...el\TranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
879
880
        $product->addTranslation($translation);
0 ignored issues
show
It seems like $translation defined by $this->productTranslationFactory->createNew() on line 875 can also be of type object<Sylius\Component\...ctTranslationInterface>; however, Sylius\Component\Resourc...rface::addTranslation() does only seem to accept object<Sylius\Component\...l\TranslationInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
881
    }
882
883
    /**
884
     * @param int $price
885
     * @param ChannelInterface|null $channel
886
     *
887
     * @return ChannelPricingInterface
888
     */
889
    private function createChannelPricingForChannel($price, ChannelInterface $channel = null)
890
    {
891
        /** @var ChannelPricingInterface $channelPricing */
892
        $channelPricing = $this->channelPricingFactory->createNew();
893
        $channelPricing->setPrice($price);
894
        $channelPricing->setChannel($channel);
895
896
        return $channelPricing;
897
    }
898
}
899