Completed
Push — master ( 49a1ab...66d152 )
by Paweł
19:14 queued 06:15
created

thisProductIsTrackedByTheInventory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
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\Behat\Context\Setup;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Gherkin\Node\TableNode;
16
use Doctrine\Common\Persistence\ObjectManager;
17
use Sylius\Component\Attribute\Factory\AttributeFactoryInterface;
18
use Sylius\Component\Core\Formatter\StringInflector;
19
use Sylius\Component\Core\Model\ChannelInterface;
20
use Sylius\Component\Core\Model\ProductInterface;
21
use Sylius\Component\Core\Model\ProductVariantInterface;
22
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
23
use Sylius\Behat\Service\SharedStorageInterface;
24
use Sylius\Component\Product\Factory\ProductFactoryInterface;
25
use Sylius\Component\Product\Model\AttributeInterface;
26
use Sylius\Component\Product\Model\AttributeValueInterface;
27
use Sylius\Component\Product\Model\OptionInterface;
28
use Sylius\Component\Product\Model\OptionValueInterface;
29
use Sylius\Component\Resource\Factory\FactoryInterface;
30
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
31
use Sylius\Component\Variation\Resolver\VariantResolverInterface;
32
33
/**
34
 * @author Arkadiusz Krakowiak <[email protected]>
35
 * @author Mateusz Zalewski <[email protected]>
36
 * @author Magdalena Banasiak <[email protected]>
37
 */
38
final class ProductContext implements Context
39
{
40
    /**
41
     * @var SharedStorageInterface
42
     */
43
    private $sharedStorage;
44
45
    /**
46
     * @var ProductRepositoryInterface
47
     */
48
    private $productRepository;
49
50
    /**
51
     * @var ProductFactoryInterface
52
     */
53
    private $productFactory;
54
55
    /**
56
     * @var AttributeFactoryInterface
57
     */
58
    private $productAttributeFactory;
59
60
    /**
61
     * @var FactoryInterface
62
     */
63
    private $productVariantFactory;
64
65
    /**
66
     * @var FactoryInterface
67
     */
68
    private $attributeValueFactory;
69
70
    /**
71
     * @var FactoryInterface
72
     */
73
    private $productOptionFactory;
74
75
    /**
76
     * @var FactoryInterface
77
     */
78
    private $productOptionValueFactory;
79
80
    /**
81
     * @var ObjectManager
82
     */
83
    private $objectManager;
84
85
    /**
86
     * @var VariantResolverInterface
87
     */
88
    private $defaultVariantResolver;
89
90
    /**
91
     * @param SharedStorageInterface $sharedStorage
92
     * @param ProductRepositoryInterface $productRepository
93
     * @param ProductFactoryInterface $productFactory
94
     * @param AttributeFactoryInterface $productAttributeFactory
95
     * @param FactoryInterface $productVariantFactory
96
     * @param FactoryInterface $attributeValueFactory
97
     * @param FactoryInterface $productOptionFactory
98
     * @param FactoryInterface $productOptionValueFactory
99
     * @param ObjectManager $objectManager
100
     * @param VariantResolverInterface $defaultVariantResolver
101
     */
102
    public function __construct(
103
        SharedStorageInterface $sharedStorage,
104
        ProductRepositoryInterface $productRepository,
105
        ProductFactoryInterface $productFactory,
106
        AttributeFactoryInterface $productAttributeFactory,
107
        FactoryInterface $attributeValueFactory,
108
        FactoryInterface $productVariantFactory,
109
        FactoryInterface $productOptionFactory,
110
        FactoryInterface $productOptionValueFactory,
111
        ObjectManager $objectManager,
112
        VariantResolverInterface $defaultVariantResolver
113
    ) {
114
        $this->sharedStorage = $sharedStorage;
115
        $this->productRepository = $productRepository;
116
        $this->productFactory = $productFactory;
117
        $this->productAttributeFactory = $productAttributeFactory;
118
        $this->attributeValueFactory = $attributeValueFactory;
119
        $this->productVariantFactory = $productVariantFactory;
120
        $this->productOptionFactory = $productOptionFactory;
121
        $this->productOptionValueFactory = $productOptionValueFactory;
122
        $this->objectManager = $objectManager;
123
        $this->defaultVariantResolver = $defaultVariantResolver;
124
    }
125
126
    /**
127
     * @Given the store has a product :productName
128
     * @Given the store has a :productName product
129
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+")$/
130
     */
131
    public function storeHasAProductPricedAt($productName, $price = 0)
132
    {
133
        $product = $this->createProduct($productName, $price);
134
135
        $product->setDescription('Awesome '.$productName);
136
137
        if ($this->sharedStorage->has('channel')) {
138
            $channel = $this->sharedStorage->get('channel');
139
            $product->addChannel($channel);
140
        }
141
142
        $this->saveProduct($product);
143
    }
144
145
    /**
146
     * @Given the store has a :productName configurable product
147
     */
148
    public function storeHasAConfigurableProduct($productName)
149
    {
150
        $product = $this->productFactory->createNew();
151
152
        $product->setName($productName);
153
        $product->setCode($this->convertToCode($productName));
154
        $product->setDescription('Awesome '.$productName);
155
156
        $this->saveProduct($product);
157
    }
158
159
    /**
160
     * @Given the store has :firstProductName and :secondProductName products
161
     */
162
    public function theStoreHasAProductAnd($firstProductName, $secondProductName)
163
    {
164
        $this->saveProduct($this->createProduct($firstProductName));
165
        $this->saveProduct($this->createProduct($secondProductName));
166
    }
167
168
    /**
169
     * @Given /^the (product "[^"]+") has "([^"]+)" variant priced at ("[^"]+")$/
170
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/
171
     */
172
    public function theProductHasVariantPricedAt(ProductInterface $product, $productVariantName, $price)
173
    {
174
        /** @var ProductVariantInterface $variant */
175
        $variant = $this->productVariantFactory->createNew();
176
177
        $variant->setName($productVariantName);
178
        $variant->setCode($this->convertToCode($productVariantName));
179
        $variant->setPrice($price);
180
        $variant->setProduct($product);
181
        $product->addVariant($variant);
182
183
        $this->objectManager->flush();
184
185
        $this->sharedStorage->set('variant', $variant);
186
    }
187
188
    /**
189
     * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/
190
     * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/
191
     */
192
    public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel)
193
    {
194
        $product = $this->createProduct($productName);
195
196
        $product->setDescription('Awesome ' . $productName);
197
        $product->addChannel($channel);
198
199
        $this->saveProduct($product);
200
    }
201
202
    /**
203
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
204
     */
205
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
206
    {
207
        $variant = $this->defaultVariantResolver->getVariant($product);
208
        $variant->setTaxCategory($taxCategory);
209
        $this->objectManager->flush();
210
    }
211
212
    /**
213
     * @Given /^(it) comes in the following variations:$/
214
     */
215
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
216
    {
217
        foreach ($table->getHash() as $variantHash) {
218
            /** @var ProductVariantInterface $variant */
219
            $variant = $this->productVariantFactory->createNew();
220
221
            $variant->setName($variantHash['name']);
222
            $variant->setCode($this->convertToCode($variantHash['name']));
223
            $variant->setPrice($this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])));
224
            $variant->setProduct($product);
225
            $product->addVariant($variant);
226
        }
227
228
        $this->objectManager->flush();
229
    }
230
231
    /**
232
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
233
     */
234
    public function productVariantBelongsToTaxCategory(
235
        ProductVariantInterface $productVariant,
236
        TaxCategoryInterface $taxCategory
237
    ) {
238
        $productVariant->setTaxCategory($taxCategory);
239
        $this->objectManager->flush($productVariant);
240
    }
241
242
    /**
243
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" with value "([^"]+)"$/
244
     */
245
    public function thisProductHasAttributeWithValue(ProductInterface $product, $productAttributeType, $productAttributeName, $value)
246
    {
247
        $attribute = $this->createProductAttribute($productAttributeType,$productAttributeName);
248
        $attributeValue = $this->createProductAttributeValue($value, $attribute);
249
        $product->addAttribute($attributeValue);
250
251
        $this->objectManager->flush();
252
    }
253
254
    /**
255
     * @Given /^(this product) has percent attribute "([^"]+)" with value ([^"]+)%$/
256
     */
257
    public function thisProductHasPercentAttributeWithValue(ProductInterface $product, $productAttributeName, $value)
258
    {
259
        $attribute = $this->createProductAttribute('percent',$productAttributeName);
260
        $attributeValue = $this->createProductAttributeValue($value/100, $attribute);
261
        $product->addAttribute($attributeValue);
262
263
        $this->objectManager->flush();
264
    }
265
266
    /**
267
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" set to "([^"]+)"$/
268
     */
269
    public function thisProductHasCheckboxAttributeWithValue(ProductInterface $product, $productAttributeType, $productAttributeName, $value)
270
    {
271
        $attribute = $this->createProductAttribute($productAttributeType, $productAttributeName);
272
        $booleanValue = ('Yes' === $value);
273
        $attributeValue = $this->createProductAttributeValue($booleanValue, $attribute);
0 ignored issues
show
Documentation introduced by
$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...
274
        $product->addAttribute($attributeValue);
275
276
        $this->objectManager->flush();
277
    }
278
279
    /**
280
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" with date "([^"]+)"$/
281
     */
282
    public function thisProductHasDateTimeAttributeWithDate(ProductInterface $product, $productAttributeType, $productAttributeName, $date)
283
    {
284
        $attribute = $this->createProductAttribute($productAttributeType, $productAttributeName);
285
        $attributeValue = $this->createProductAttributeValue(new \DateTime($date), $attribute);
0 ignored issues
show
Documentation introduced by
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...
286
287
        $product->addAttribute($attributeValue);
288
289
        $this->objectManager->flush();
290
    }
291
292
    /**
293
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/
294
     */
295
    public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, $firstValue, $secondValue)
296
    {
297
        /** @var OptionInterface $variant */
298
        $option = $this->productOptionFactory->createNew();
299
300
        $option->setName($optionName);
301
        $option->setCode('PO1');
302
303
        /** @var OptionValueInterface $optionValue */
304
        $firstOptionValue = $this->productOptionValueFactory->createNew();
305
306
        $firstOptionValue->setValue($firstValue);
307
        $firstOptionValue->setCode('POV1');
308
        $firstOptionValue->setOption($option);
309
310
        /** @var OptionValueInterface $optionValue */
311
        $secondOptionValue = $this->productOptionValueFactory->createNew();
312
313
        $secondOptionValue->setValue($secondValue);
314
        $secondOptionValue->setCode('POV2');
315
        $secondOptionValue->setOption($option);
316
317
        $option->addValue($firstOptionValue);
318
        $option->addValue($secondOptionValue);
319
320
        $product->addOption($option);
321
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
322
323
        $this->sharedStorage->set(sprintf('%s_option',$optionName), $option);
324
        $this->sharedStorage->set(sprintf('%s_option_value',$firstValue), $firstOptionValue);
325
        $this->sharedStorage->set(sprintf('%s_option_value',$secondValue), $secondOptionValue);
326
327
        $this->objectManager->persist($option);
328
        $this->objectManager->persist($firstOptionValue);
329
        $this->objectManager->persist($secondOptionValue);
330
        $this->objectManager->flush();
331
    }
332
333
    /**
334
     * @Given /^there (?:is|are) (\d+) (?:item|unit)(?:s) of (product "([^"]+)") available in the inventory$/
335
     * @When product :product quantity is changed to :quantity
336
     */
337
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
338
    {
339
        $this->setProductsQuantity($product, $quantity);
340
    }
341
342
    /**
343
     * @Given /^the (product "([^"]+)") is out of stock$/
344
     */
345
    public function theProductIsNotAvailable(ProductInterface $product)
346
    {
347
        $product->getFirstVariant()->setTracked(true);
348
349
        $this->setProductsQuantity($product, 0);
350
    }
351
352
    /**
353
     * @When other customer has bought :quantity :product products by this time
354
     */
355
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
356
    {
357
        /** @var ProductVariantInterface $productVariant */
358
        $productVariant = $this->defaultVariantResolver->getVariant($product);
359
        $productQuantity = $productVariant->getOnHand() - $quantity;
360
361
        $this->setProductsQuantity($product, $productQuantity);
362
    }
363
364
    /**
365
     * @Given /^(this product) is tracked by the inventory$/
366
     * @Given /^("[^"]+" product) is(?:| also) tracked by the inventory$/
367
     */
368
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
369
    {
370
        $variant = $this->defaultVariantResolver->getVariant($product);
371
        $variant->setTracked(true);
372
373
        $this->objectManager->flush();
374
    }
375
376
    /**
377
     * @Given /^(this product) is available in "([^"]+)" size priced at ("[^"]+")$/
378
     */
379
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $price)
380
    {
381
        /** @var ProductVariantInterface $variant */
382
        $variant = $this->productVariantFactory->createNew();
383
384
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_value',$optionValueName));
385
386
        $variant->addOption($optionValue);
387
        $variant->setPrice($price);
388
        $variant->setCode(sprintf("%s_%s", $product->getCode(), $optionValueName));
389
390
        $product->addVariant($variant);
391
        $this->objectManager->flush();
392
    }
393
394
    /**
395
     * @Given /^(this product) has (this product option)$/
396
     * @Given /^(this product) has a ("[^"]+" option)$/
397
     * @Given /^(this product) has an ("[^"]+" option)$/
398
     */
399
    public function thisProductHasThisProductOption(ProductInterface $product, OptionInterface $option)
400
    {
401
        $product->addOption($option);
402
403
        $this->objectManager->flush();
404
    }
405
406
    /**
407
     * @param string $type
408
     * @param string $name
409
     * @param string $code
410
     *
411
     * @return AttributeInterface
412
     */
413
    private function createProductAttribute($type, $name, $code = 'PA112')
414
    {
415
        $productAttribute = $this->productAttributeFactory->createTyped($type);
416
        $productAttribute->setCode($code);
417
        $productAttribute->setName($name);
418
419
        $this->objectManager->persist($productAttribute);
420
421
        return $productAttribute;
422
    }
423
424
    /**
425
     * @param string $value
426
     *
427
     * @return AttributeValueInterface
428
     */
429
    private function createProductAttributeValue($value, AttributeInterface $attribute)
430
    {
431
        /** @var AttributeValueInterface $attributeValue */
432
        $attributeValue = $this->attributeValueFactory->createNew();
433
        $attributeValue->setAttribute($attribute);
434
        $attributeValue->setValue($value);
435
436
        $this->objectManager->persist($attributeValue);
437
438
        return $attributeValue;
439
    }
440
441
    /**
442
     * @param string $price
443
     *
444
     * @return int
445
     */
446
    private function getPriceFromString($price)
447
    {
448
        return (int) round(($price * 100), 2);
449
    }
450
451
    /**
452
     * @param string $productName
453
     * @param int $price
454
     *
455
     * @return ProductInterface
456
     */
457
    private function createProduct($productName, $price = 0)
458
    {
459
        /** @var ProductInterface $product */
460
        $product = $this->productFactory->createWithVariant();
461
462
        $product->setName($productName);
463
        $product->setCode($this->convertToCode($productName));
464
465
        $variant = $this->defaultVariantResolver->getVariant($product);
466
        $variant->setPrice($price);
467
        $variant->setCode($product->getCode());
468
469
        return $product;
470
    }
471
472
    /**
473
     * @param ProductInterface $product
474
     * @param int $quantity
475
     */
476
    private function setProductsQuantity(ProductInterface $product, $quantity)
477
    {
478
        $product->getFirstVariant()->setOnHand($quantity);
479
480
        $this->saveProduct($product);
481
    }
482
483
    /**
484
     * @param ProductInterface $product
485
     */
486
    private function saveProduct(ProductInterface $product)
487
    {
488
        $this->productRepository->add($product);
489
        $this->sharedStorage->set('product', $product);
490
    }
491
492
    /**
493
     * @param string $productName
494
     *
495
     * @return string
496
     */
497
    private function convertToCode($productName)
498
    {
499
        return StringInflector::nameToUpercaseCode($productName);
500
    }
501
}
502