Issues (244)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Sylius/Behat/Context/Setup/ProductContext.php (1 issue)

Labels
Severity

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
declare(strict_types=1);
13
14
namespace Sylius\Behat\Context\Setup;
15
16
use Behat\Behat\Context\Context;
17
use Behat\Gherkin\Node\TableNode;
18
use Behat\Mink\Element\NodeElement;
19
use Doctrine\Common\Persistence\ObjectManager;
20
use Sylius\Behat\Service\SharedStorageInterface;
21
use Sylius\Component\Core\Formatter\StringInflector;
22
use Sylius\Component\Core\Model\ChannelInterface;
23
use Sylius\Component\Core\Model\ChannelPricingInterface;
24
use Sylius\Component\Core\Model\ImageInterface;
25
use Sylius\Component\Core\Model\ProductInterface;
26
use Sylius\Component\Core\Model\ProductTranslationInterface;
27
use Sylius\Component\Core\Model\ProductVariantInterface;
28
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
29
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
30
use Sylius\Component\Product\Factory\ProductFactoryInterface;
31
use Sylius\Component\Product\Generator\ProductVariantGeneratorInterface;
32
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
33
use Sylius\Component\Product\Model\ProductOptionInterface;
34
use Sylius\Component\Product\Model\ProductOptionValueInterface;
35
use Sylius\Component\Product\Model\ProductVariantTranslationInterface;
36
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
37
use Sylius\Component\Resource\Factory\FactoryInterface;
38
use Sylius\Component\Shipping\Model\ShippingCategoryInterface;
39
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
40
use Symfony\Component\HttpFoundation\File\UploadedFile;
41
use Webmozart\Assert\Assert;
42
43
final class ProductContext implements Context
44
{
45
    /**
46
     * @var SharedStorageInterface
47
     */
48
    private $sharedStorage;
49
50
    /**
51
     * @var ProductRepositoryInterface
52
     */
53
    private $productRepository;
54
55
    /**
56
     * @var ProductFactoryInterface
57
     */
58
    private $productFactory;
59
60
    /**
61
     * @var FactoryInterface
62
     */
63
    private $productTranslationFactory;
64
65
    /**
66
     * @var FactoryInterface
67
     */
68
    private $productVariantFactory;
69
70
    /**
71
     * @var FactoryInterface
72
     */
73
    private $productVariantTranslationFactory;
74
75
    /**
76
     * @var FactoryInterface
77
     */
78
    private $channelPricingFactory;
79
80
    /**
81
     * @var FactoryInterface
82
     */
83
    private $productOptionFactory;
84
85
    /**
86
     * @var FactoryInterface
87
     */
88
    private $productOptionValueFactory;
89
90
    /**
91
     * @var FactoryInterface
92
     */
93
    private $productImageFactory;
94
95
    /**
96
     * @var ObjectManager
97
     */
98
    private $objectManager;
99
100
    /**
101
     * @var ProductVariantGeneratorInterface
102
     */
103
    private $productVariantGenerator;
104
105
    /**
106
     * @var ProductVariantResolverInterface
107
     */
108
    private $defaultVariantResolver;
109
110
    /**
111
     * @var ImageUploaderInterface
112
     */
113
    private $imageUploader;
114
115
    /**
116
     * @var SlugGeneratorInterface
117
     */
118
    private $slugGenerator;
119
120
    /**
121
     * @var array
122
     */
123
    private $minkParameters;
124
125
    /**
126
     * @param SharedStorageInterface $sharedStorage
127
     * @param ProductRepositoryInterface $productRepository
128
     * @param ProductFactoryInterface $productFactory
129
     * @param FactoryInterface $productTranslationFactory
130
     * @param FactoryInterface $productVariantFactory
131
     * @param FactoryInterface $productVariantTranslationFactory
132
     * @param FactoryInterface $channelPricingFactory
133
     * @param FactoryInterface $productOptionFactory
134
     * @param FactoryInterface $productOptionValueFactory
135
     * @param FactoryInterface $productImageFactory
136
     * @param ObjectManager $objectManager
137
     * @param ProductVariantGeneratorInterface $productVariantGenerator
138
     * @param ProductVariantResolverInterface $defaultVariantResolver
139
     * @param ImageUploaderInterface $imageUploader
140
     * @param SlugGeneratorInterface $slugGenerator
141
     * @param array $minkParameters
142
     */
143
    public function __construct(
144
        SharedStorageInterface $sharedStorage,
145
        ProductRepositoryInterface $productRepository,
146
        ProductFactoryInterface $productFactory,
147
        FactoryInterface $productTranslationFactory,
148
        FactoryInterface $productVariantFactory,
149
        FactoryInterface $productVariantTranslationFactory,
150
        FactoryInterface $channelPricingFactory,
151
        FactoryInterface $productOptionFactory,
152
        FactoryInterface $productOptionValueFactory,
153
        FactoryInterface $productImageFactory,
154
        ObjectManager $objectManager,
155
        ProductVariantGeneratorInterface $productVariantGenerator,
156
        ProductVariantResolverInterface $defaultVariantResolver,
157
        ImageUploaderInterface $imageUploader,
158
        SlugGeneratorInterface $slugGenerator,
159
        array $minkParameters
160
    ) {
161
        $this->sharedStorage = $sharedStorage;
162
        $this->productRepository = $productRepository;
163
        $this->productFactory = $productFactory;
164
        $this->productTranslationFactory = $productTranslationFactory;
165
        $this->productVariantFactory = $productVariantFactory;
166
        $this->productVariantTranslationFactory = $productVariantTranslationFactory;
167
        $this->channelPricingFactory = $channelPricingFactory;
168
        $this->productOptionFactory = $productOptionFactory;
169
        $this->productOptionValueFactory = $productOptionValueFactory;
170
        $this->productImageFactory = $productImageFactory;
171
        $this->objectManager = $objectManager;
172
        $this->productVariantGenerator = $productVariantGenerator;
173
        $this->defaultVariantResolver = $defaultVariantResolver;
174
        $this->imageUploader = $imageUploader;
175
        $this->slugGenerator = $slugGenerator;
176
        $this->minkParameters = $minkParameters;
177
    }
178
179
    /**
180
     * @Given the store has a product :productName
181
     * @Given the store has a :productName product
182
     * @Given I added a product :productName
183
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+")$/
184
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") in ("[^"]+" channel)$/
185
     */
186
    public function storeHasAProductPricedAt($productName, $price = 100, ChannelInterface $channel = null)
187
    {
188
        $product = $this->createProduct($productName, $price, $channel);
189
190
        $this->saveProduct($product);
191
    }
192
193
    /**
194
     * @Given /^(this product) is(?:| also) priced at ("[^"]+") in ("[^"]+" channel)$/
195
     */
196
    public function thisProductIsAlsoPricedAtInChannel(ProductInterface $product, $price, ChannelInterface $channel)
197
    {
198
        $product->addChannel($channel);
199
200
        /** @var ProductVariantInterface $productVariant */
201
        $productVariant = $this->defaultVariantResolver->getVariant($product);
202
        $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
203
204
        $this->objectManager->flush();
205
    }
206
207
    /**
208
     * @Given /^(this product) is(?:| also) available in ("[^"]+" channel)$/
209
     */
210
    public function thisProductIsAlsoAvailableInChannel(ProductInterface $product, ChannelInterface $channel): void
211
    {
212
        $this->thisProductIsAlsoPricedAtInChannel($product, 0, $channel);
213
    }
214
215
    /**
216
     * @Given the store( also) has a product :productName with code :code
217
     * @Given the store( also) has a product :productName with code :code, created at :date
218
     */
219
    public function storeHasProductWithCode($productName, $code, $date = 'now')
220
    {
221
        $product = $this->createProduct($productName);
222
        $product->setCreatedAt(new \DateTime($date));
223
        $product->setCode($code);
224
225
        $this->saveProduct($product);
226
    }
227
228
    /**
229
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") available in (channel "[^"]+") and (channel "[^"]+")$/
230
     */
231
    public function storeHasAProductPricedAtAvailableInChannels($productName, $price = 100, ...$channels)
232
    {
233
        $product = $this->createProduct($productName, $price);
234
        /** @var ProductVariantInterface $productVariant */
235
        $productVariant = $this->defaultVariantResolver->getVariant($product);
236
237
        foreach ($channels as $channel) {
238
            $product->addChannel($channel);
239
            if (!$productVariant->hasChannelPricingForChannel($channel)) {
240
                $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
241
            }
242
        }
243
244
        $this->saveProduct($product);
245
    }
246
247
    /**
248
     * @Given /^(this product) is named "([^"]+)" (in the "([^"]+)" locale)$/
249
     * @Given /^the (product "[^"]+") is named "([^"]+)" (in the "([^"]+)" locale)$/
250
     */
251
    public function thisProductIsNamedIn(ProductInterface $product, $name, $locale)
252
    {
253
        $this->addProductTranslation($product, $name, $locale);
254
255
        $this->objectManager->flush();
256
    }
257
258
    /**
259
     * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
260
     */
261
    public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale)
262
    {
263
        $product = $this->createProduct($firstName);
264
265
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
266
        foreach ($names as $name => $locale) {
267
            $this->addProductTranslation($product, $name, $locale);
268
        }
269
270
        $this->saveProduct($product);
271
    }
272
273
    /**
274
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product$/
275
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product with "([^"]+)" slug$/
276
     */
277
    public function storeHasAConfigurableProduct($productName, $slug = null)
278
    {
279
        /** @var ChannelInterface|null $channel */
280
        $channel = null;
281
        if ($this->sharedStorage->has('channel')) {
282
            $channel = $this->sharedStorage->get('channel');
283
        }
284
285
        /** @var ProductInterface $product */
286
        $product = $this->productFactory->createNew();
287
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
288
289
        if (null !== $channel) {
290
            $product->addChannel($channel);
291
292
            foreach ($channel->getLocales() as $locale) {
293
                $product->setFallbackLocale($locale->getCode());
294
                $product->setCurrentLocale($locale->getCode());
295
296
                $product->setName($productName);
297
                $product->setSlug($slug ?: $this->slugGenerator->generate($productName));
298
            }
299
        }
300
301
        $this->saveProduct($product);
302
    }
303
304
    /**
305
     * @Given the store has( also) :firstProductName and :secondProductName products
306
     * @Given the store has( also) :firstProductName, :secondProductName and :thirdProductName products
307
     * @Given the store has( also) :firstProductName, :secondProductName, :thirdProductName and :fourthProductName products
308
     */
309
    public function theStoreHasProducts(...$productsNames)
310
    {
311
        foreach ($productsNames as $productName) {
312
            $this->saveProduct($this->createProduct($productName));
313
        }
314
    }
315
316
    /**
317
     * @Given /^(this channel) has "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" products$/
318
     */
319
    public function thisChannelHasProducts(ChannelInterface $channel, ...$productsNames)
320
    {
321
        foreach ($productsNames as $productName) {
322
            $product = $this->createProduct($productName, 0, $channel);
323
324
            $this->saveProduct($product);
325
        }
326
    }
327
328
    /**
329
     * @Given /^the (product "[^"]+") has(?:| a) "([^"]+)" variant priced at ("[^"]+")$/
330
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/
331
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") in ("([^"]+)" channel)$/
332
     */
333
    public function theProductHasVariantPricedAt(
334
        ProductInterface $product,
335
        $productVariantName,
336
        $price,
337
        ChannelInterface $channel = null
338
    ) {
339
        $this->createProductVariant(
340
            $product,
341
            $productVariantName,
342
            $price,
343
            StringInflector::nameToUppercaseCode($productVariantName),
344
            $channel ?? $this->sharedStorage->get('channel')
345
        );
346
    }
347
348
    /**
349
     * @Given /^the (product "[^"]+") has(?:| a| an) "([^"]+)" variant$/
350
     * @Given /^(this product) has(?:| a| an) "([^"]+)" variant$/
351
     * @Given /^(this product) has "([^"]+)", "([^"]+)" and "([^"]+)" variants$/
352
     */
353
    public function theProductHasVariants(ProductInterface $product, ...$variantNames)
354
    {
355
        $channel = $this->sharedStorage->get('channel');
356
357
        foreach ($variantNames as $name) {
358
            $this->createProductVariant(
359
                $product,
360
                $name,
361
                0,
362
                StringInflector::nameToUppercaseCode($name),
363
                $channel
364
            );
365
        }
366
    }
367
368
    /**
369
     * @Given /^the (product "[^"]+")(?:| also) has a nameless variant with code "([^"]+)"$/
370
     * @Given /^(this product)(?:| also) has a nameless variant with code "([^"]+)"$/
371
     * @Given /^(it)(?:| also) has a nameless variant with code "([^"]+)"$/
372
     */
373
    public function theProductHasNamelessVariantWithCode(ProductInterface $product, $variantCode)
374
    {
375
        $channel = $this->sharedStorage->get('channel');
376
377
        $this->createProductVariant($product, null, 0, $variantCode, $channel);
378
    }
379
380
    /**
381
     * @Given /^the (product "[^"]+")(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
382
     * @Given /^(this product)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
383
     * @Given /^(it)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
384
     */
385
    public function theProductHasVariantWithCode(ProductInterface $product, $variantName, $variantCode)
386
    {
387
        $channel = $this->sharedStorage->get('channel');
388
389
        $this->createProductVariant($product, $variantName, 0, $variantCode, $channel);
390
    }
391
392
    /**
393
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") which does not require shipping$/
394
     */
395
    public function theProductHasVariantWhichDoesNotRequireShipping(
396
        ProductInterface $product,
397
        $productVariantName,
398
        $price
399
    ) {
400
        $this->createProductVariant(
401
            $product,
402
            $productVariantName,
403
            $price,
404
            StringInflector::nameToUppercaseCode($productVariantName),
405
            $this->sharedStorage->get('channel'),
406
            null,
407
            false
408
        );
409
    }
410
411
    /**
412
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant$/
413
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
414
     * @Given /^(this product) has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
415
     */
416
    public function theProductHasVariantAtPosition(
417
        ProductInterface $product,
418
        $productVariantName,
419
        $position = null
420
    ) {
421
        $this->createProductVariant(
422
            $product,
423
            $productVariantName,
424
            0,
425
            StringInflector::nameToUppercaseCode($productVariantName),
426
            $this->sharedStorage->get('channel'),
427
            $position
428
        );
429
    }
430
431
    /**
432
     * @Given /^(this variant) is also priced at ("[^"]+") in ("([^"]+)" channel)$/
433
     */
434
    public function thisVariantIsAlsoPricedAtInChannel(ProductVariantInterface $productVariant, $price, ChannelInterface $channel)
435
    {
436
        $productVariant->addChannelPricing($this->createChannelPricingForChannel(
437
            $this->getPriceFromString(str_replace(['$', '€', '£'], '', $price)),
438
            $channel
439
        ));
440
441
        $this->objectManager->flush();
442
    }
443
444
    /**
445
     * @Given /^(it|this product) has(?:| also) variant named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
446
     */
447
    public function itHasVariantNamedInAndIn(ProductInterface $product, $firstName, $firstLocale, $secondName, $secondLocale)
448
    {
449
        $productVariant = $this->createProductVariant(
450
            $product,
451
            $firstName,
452
            100,
453
            StringInflector::nameToUppercaseCode($firstName),
454
            $this->sharedStorage->get('channel')
455
        );
456
457
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
458
        foreach ($names as $name => $locale) {
459
            $this->addProductVariantTranslation($productVariant, $name, $locale);
460
        }
461
462
        $this->objectManager->flush();
463
    }
464
465
    /**
466
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/
467
     */
468
    public function theProductHasVariantPricedAtIdentifiedBy(
469
        ProductInterface $product,
470
        $productVariantName,
471
        $price,
472
        $code
473
    ) {
474
        $this->createProductVariant($product, $productVariantName, $price, $code, $this->sharedStorage->get('channel'));
475
    }
476
477
    /**
478
     * @Given /^(this product) only variant was renamed to "([^"]+)"$/
479
     */
480
    public function productOnlyVariantWasRenamed(ProductInterface $product, $variantName)
481
    {
482
        Assert::true($product->isSimple());
483
484
        /** @var ProductVariantInterface $productVariant */
485
        $productVariant = $product->getVariants()->first();
486
        $productVariant->setName($variantName);
487
488
        $this->objectManager->flush();
489
    }
490
491
    /**
492
     * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/
493
     * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/
494
     */
495
    public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel)
496
    {
497
        $product = $this->createProduct($productName, 0, $channel);
498
499
        $this->saveProduct($product);
500
    }
501
502
    /**
503
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
504
     */
505
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
506
    {
507
        /** @var ProductVariantInterface $variant */
508
        $variant = $this->defaultVariantResolver->getVariant($product);
509
        $variant->setTaxCategory($taxCategory);
510
511
        $this->objectManager->flush();
512
    }
513
514
    /**
515
     * @Given /^(it) comes in the following variations:$/
516
     */
517
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
518
    {
519
        $channel = $this->sharedStorage->get('channel');
520
521
        foreach ($table->getHash() as $variantHash) {
522
            /** @var ProductVariantInterface $variant */
523
            $variant = $this->productVariantFactory->createNew();
524
525
            $variant->setName($variantHash['name']);
526
            $variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
527
            $variant->addChannelPricing($this->createChannelPricingForChannel(
528
                $this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
529
                $channel
530
            ));
531
532
            $variant->setProduct($product);
533
            $product->addVariant($variant);
534
        }
535
536
        $this->objectManager->flush();
537
    }
538
539
    /**
540
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
541
     */
542
    public function productVariantBelongsToTaxCategory(
543
        ProductVariantInterface $productVariant,
544
        TaxCategoryInterface $taxCategory
545
    ) {
546
        $productVariant->setTaxCategory($taxCategory);
547
548
        $this->objectManager->persist($productVariant);
549
        $this->objectManager->flush();
550
    }
551
552
    /**
553
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/
554
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)", "([^"]+)" and "([^"]+)"$/
555
     */
556
    public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, ...$values)
557
    {
558
        /** @var ProductOptionInterface $option */
559
        $option = $this->productOptionFactory->createNew();
560
561
        $option->setName($optionName);
562
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
563
564
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
565
566
        foreach ($values as $key => $value) {
567
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
568
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
569
        }
570
571
        $product->addOption($option);
572
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
573
574
        $this->objectManager->persist($option);
575
        $this->objectManager->flush();
576
    }
577
578
    /**
579
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
580
     */
581
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
582
    {
583
        /** @var ProductVariantInterface $productVariant */
584
        $productVariant = $this->defaultVariantResolver->getVariant($product);
585
        $productVariant->setOnHand((int) $quantity);
586
587
        $this->objectManager->flush();
588
    }
589
590
    /**
591
     * @Given /^the (product "([^"]+)") is out of stock$/
592
     */
593
    public function theProductIsOutOfStock(ProductInterface $product)
594
    {
595
        /** @var ProductVariantInterface $productVariant */
596
        $productVariant = $this->defaultVariantResolver->getVariant($product);
597
        $productVariant->setTracked(true);
598
        $productVariant->setOnHand(0);
599
600
        $this->objectManager->flush();
601
    }
602
603
    /**
604
     * @When other customer has bought :quantity :product products by this time
605
     */
606
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
607
    {
608
        /** @var ProductVariantInterface $productVariant */
609
        $productVariant = $this->defaultVariantResolver->getVariant($product);
610
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
611
612
        $this->objectManager->flush();
613
    }
614
615
    /**
616
     * @Given /^(this product) is tracked by the inventory$/
617
     * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/
618
     */
619
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
620
    {
621
        /** @var ProductVariantInterface $productVariant */
622
        $productVariant = $this->defaultVariantResolver->getVariant($product);
623
        $productVariant->setTracked(true);
624
625
        $this->objectManager->flush();
626
    }
627
628
    /**
629
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
630
     */
631
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, $price)
632
    {
633
        /** @var ProductVariantInterface $variant */
634
        $variant = $this->productVariantFactory->createNew();
635
636
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
637
638
        $variant->addOptionValue($optionValue);
639
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
640
        $variant->setCode(sprintf('%s_%s', $product->getCode(), $optionValueName));
641
        $variant->setName($product->getName());
642
643
        $product->addVariant($variant);
644
        $this->objectManager->flush();
645
    }
646
647
    /**
648
     * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category
649
     */
650
    public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory)
651
    {
652
        $code = sprintf('%s_%s', $product->getCode(), $optionValueName);
653
        /** @var ProductVariantInterface $productVariant */
654
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
655
            return $code === $variant->getCode();
656
        })->first();
657
658
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
659
660
        $productVariant->setShippingCategory($shippingCategory);
661
        $this->objectManager->flush();
662
    }
663
664
    /**
665
     * @Given /^(this product) has (this product option)$/
666
     * @Given /^(this product) has (?:a|an) ("[^"]+" option)$/
667
     */
668
    public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option)
669
    {
670
        $product->addOption($option);
671
672
        $this->objectManager->flush();
673
    }
674
675
    /**
676
     * @Given /^(this product) has all possible variants$/
677
     */
678
    public function thisProductHasAllPossibleVariants(ProductInterface $product)
679
    {
680
        try {
681
            foreach ($product->getVariants() as $productVariant) {
682
                $product->removeVariant($productVariant);
683
            }
684
685
            $this->productVariantGenerator->generate($product);
686
        } catch (\InvalidArgumentException $exception) {
687
            /** @var ProductVariantInterface $productVariant */
688
            $productVariant = $this->productVariantFactory->createNew();
689
690
            $product->addVariant($productVariant);
691
        }
692
693
        $i = 0;
694
        /** @var ProductVariantInterface $productVariant */
695
        foreach ($product->getVariants() as $productVariant) {
696
            $productVariant->setCode(sprintf('%s-variant-%d', $product->getCode(), $i));
697
698
            foreach ($product->getChannels() as $channel) {
699
                $productVariant->addChannelPricing($this->createChannelPricingForChannel(1000, $channel));
700
            }
701
702
            ++$i;
703
        }
704
705
        $this->objectManager->flush();
706
    }
707
708
    /**
709
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
710
     */
711
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
712
    {
713
        $productVariant->setTracked(true);
714
        $productVariant->setOnHand((int) $quantity);
715
716
        $this->objectManager->flush();
717
    }
718
719
    /**
720
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
721
     */
722
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
723
    {
724
        $productVariant->setTracked(true);
725
726
        $this->objectManager->flush();
727
    }
728
729
    /**
730
     * @Given /^(this product)'s price is ("[^"]+")$/
731
     * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
732
     * @Given /^(this product) price has been changed to ("[^"]+")$/
733
     */
734
    public function theProductChangedItsPriceTo(ProductInterface $product, $price)
735
    {
736
        /** @var ProductVariantInterface $productVariant */
737
        $productVariant = $this->defaultVariantResolver->getVariant($product);
738
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
739
        $channelPricing->setPrice($price);
740
741
        $this->objectManager->flush();
742
    }
743
744
    /**
745
     * @Given /^(this product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
746
     * @Given /^the ("[^"]+" product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
747
     * @Given /^(it)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
748
     */
749
    public function thisProductHasAnImageWithType(ProductInterface $product, $imagePath, $imageType)
750
    {
751
        $filesPath = $this->getParameter('files_path');
752
753
        /** @var ImageInterface $productImage */
754
        $productImage = $this->productImageFactory->createNew();
755
        $productImage->setFile(new UploadedFile($filesPath . $imagePath, basename($imagePath)));
756
        $productImage->setType($imageType);
757
        $this->imageUploader->upload($productImage);
758
759
        $product->addImage($productImage);
760
761
        $this->objectManager->persist($product);
762
        $this->objectManager->flush();
763
    }
764
765
    /**
766
     * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/
767
     * @Given product :product shipping category has been changed to :shippingCategory
768
     */
769
    public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory)
770
    {
771
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
772
        $this->objectManager->flush();
773
    }
774
775
    /**
776
     * @Given /^(this product) has been disabled$/
777
     */
778
    public function thisProductHasBeenDisabled(ProductInterface $product)
779
    {
780
        $product->disable();
781
        $this->objectManager->flush();
782
    }
783
784
    /**
785
     * @Given the product :product was renamed to :productName
786
     */
787
    public function theProductWasRenamedTo(ProductInterface $product, string $productName): void
788
    {
789
        $product->setName($productName);
790
791
        $this->objectManager->flush();
792
    }
793
794
    private function getPriceFromString(string $price): int
795
    {
796
        return (int) round((float) str_replace(['€', '£', '$'], '', $price) * 100, 2);
797
    }
798
799
    /**
800
     * @param string $productName
801
     * @param int $price
802
     * @param ChannelInterface|null $channel
803
     *
804
     * @return ProductInterface
805
     */
806
    private function createProduct($productName, $price = 100, ChannelInterface $channel = null)
807
    {
808
        if (null === $channel && $this->sharedStorage->has('channel')) {
809
            $channel = $this->sharedStorage->get('channel');
810
        }
811
812
        /** @var ProductInterface $product */
813
        $product = $this->productFactory->createWithVariant();
814
815
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
816
        $product->setName($productName);
817
        $product->setSlug($this->slugGenerator->generate($productName));
818
819
        if (null !== $channel) {
820
            $product->addChannel($channel);
821
822
            foreach ($channel->getLocales() as $locale) {
823
                $product->setFallbackLocale($locale->getCode());
824
                $product->setCurrentLocale($locale->getCode());
825
826
                $product->setName($productName);
827
                $product->setSlug($this->slugGenerator->generate($productName));
828
            }
829
        }
830
831
        /** @var ProductVariantInterface $productVariant */
832
        $productVariant = $this->defaultVariantResolver->getVariant($product);
833
834
        if (null !== $channel) {
835
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
836
        }
837
838
        $productVariant->setCode($product->getCode());
839
        $productVariant->setName($product->getName());
840
841
        return $product;
842
    }
843
844
    /**
845
     * @param ProductOptionInterface $option
846
     * @param string $value
847
     * @param string $code
848
     *
849
     * @return ProductOptionValueInterface
850
     */
851
    private function addProductOption(ProductOptionInterface $option, $value, $code)
852
    {
853
        /** @var ProductOptionValueInterface $optionValue */
854
        $optionValue = $this->productOptionValueFactory->createNew();
855
856
        $optionValue->setValue($value);
857
        $optionValue->setCode($code);
858
        $optionValue->setOption($option);
859
860
        $option->addValue($optionValue);
861
862
        return $optionValue;
863
    }
864
865
    /**
866
     * @param ProductInterface $product
867
     */
868
    private function saveProduct(ProductInterface $product)
869
    {
870
        $this->productRepository->add($product);
871
        $this->sharedStorage->set('product', $product);
872
    }
873
874
    /**
875
     * @param string $name
876
     *
877
     * @return NodeElement
878
     */
879
    private function getParameter($name)
880
    {
881
        return $this->minkParameters[$name] ?? null;
882
    }
883
884
    /**
885
     * @param ProductInterface $product
886
     * @param string $productVariantName
887
     * @param int $price
888
     * @param string $code
889
     * @param ChannelInterface $channel
890
     * @param int $position
891
     * @param bool $shippingRequired
892
     *
893
     * @return ProductVariantInterface
894
     */
895
    private function createProductVariant(
896
        ProductInterface $product,
897
        $productVariantName,
898
        $price,
899
        $code,
900
        ChannelInterface $channel = null,
901
        $position = null,
902
        $shippingRequired = true
903
    ) {
904
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
905
906
        /** @var ProductVariantInterface $variant */
907
        $variant = $this->productVariantFactory->createNew();
908
909
        $variant->setName($productVariantName);
910
        $variant->setCode($code);
911
        $variant->setProduct($product);
912
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
913
        $variant->setPosition((null === $position) ? null : (int) $position);
914
        $variant->setShippingRequired($shippingRequired);
915
916
        $product->addVariant($variant);
917
918
        $this->objectManager->flush();
919
        $this->sharedStorage->set('variant', $variant);
920
921
        return $variant;
922
    }
923
924
    /**
925
     * @param ProductInterface $product
926
     * @param string $name
927
     * @param string $locale
928
     */
929
    private function addProductTranslation(ProductInterface $product, $name, $locale)
930
    {
931
        /** @var ProductTranslationInterface $translation */
932
        $translation = $product->getTranslation($locale);
933
        if ($translation->getLocale() !== $locale) {
934
            /** @var ProductTranslationInterface $translation */
935
            $translation = $this->productTranslationFactory->createNew();
936
        }
937
938
        $translation->setLocale($locale);
939
        $translation->setName($name);
940
        $translation->setSlug($this->slugGenerator->generate($name));
941
942
        $product->addTranslation($translation);
943
    }
944
945
    /**
946
     * @param ProductVariantInterface $productVariant
947
     * @param string $name
948
     * @param string $locale
949
     */
950
    private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale)
951
    {
952
        /** @var ProductVariantTranslationInterface $translation */
953
        $translation = $this->productVariantTranslationFactory->createNew();
954
        $translation->setLocale($locale);
955
        $translation->setName($name);
956
957
        $productVariant->addTranslation($translation);
958
    }
959
960
    /**
961
     * @param int $price
962
     * @param ChannelInterface|null $channel
963
     *
964
     * @return ChannelPricingInterface
965
     */
966
    private function createChannelPricingForChannel($price, ChannelInterface $channel = null)
967
    {
968
        /** @var ChannelPricingInterface $channelPricing */
969
        $channelPricing = $this->channelPricingFactory->createNew();
970
        $channelPricing->setPrice($price);
971
        $channelPricing->setChannelCode($channel->getCode());
0 ignored issues
show
It seems like $channel is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
972
973
        return $channelPricing;
974
    }
975
}
976