Completed
Push — 1.4 ( 4d2cb6...71b698 )
by Kamil
09:52 queued 04:24
created

OrderFixture::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 9.456
c 0
b 0
f 0
cc 1
nc 1
nop 12

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\Bundle\CoreBundle\Fixture;
15
16
use Doctrine\Common\Persistence\ObjectManager;
17
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
18
use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture;
19
use Sylius\Component\Core\Model\AddressInterface;
20
use Sylius\Component\Core\Model\ChannelInterface;
21
use Sylius\Component\Core\Model\OrderInterface;
22
use Sylius\Component\Core\Model\OrderItemInterface;
23
use Sylius\Component\Core\Model\ProductInterface;
24
use Sylius\Component\Core\OrderCheckoutStates;
25
use Sylius\Component\Core\OrderCheckoutTransitions;
26
use Sylius\Component\Core\Repository\PaymentMethodRepositoryInterface;
27
use Sylius\Component\Core\Repository\ShippingMethodRepositoryInterface;
28
use Sylius\Component\Order\Modifier\OrderItemQuantityModifierInterface;
29
use Sylius\Component\Resource\Factory\FactoryInterface;
30
use Sylius\Component\Resource\Repository\RepositoryInterface;
31
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
32
use Webmozart\Assert\Assert;
33
34
class OrderFixture extends AbstractFixture
35
{
36
    /** @var FactoryInterface */
37
    private $orderFactory;
38
39
    /** @var FactoryInterface */
40
    private $orderItemFactory;
41
42
    /** @var OrderItemQuantityModifierInterface */
43
    private $orderItemQuantityModifier;
44
45
    /** @var ObjectManager */
46
    private $orderManager;
47
48
    /** @var RepositoryInterface */
49
    private $channelRepository;
50
51
    /** @var RepositoryInterface */
52
    private $customerRepository;
53
54
    /** @var RepositoryInterface */
55
    private $productRepository;
56
57
    /** @var RepositoryInterface */
58
    private $countryRepository;
59
60
    /** @var PaymentMethodRepositoryInterface */
61
    private $paymentMethodRepository;
62
63
    /** @var ShippingMethodRepositoryInterface */
64
    private $shippingMethodRepository;
65
66
    /** @var FactoryInterface */
67
    private $addressFactory;
68
69
    /** @var StateMachineFactoryInterface */
70
    private $stateMachineFactory;
71
72
    /** @var \Faker\Generator */
73
    private $faker;
74
75
    public function __construct(
76
        FactoryInterface $orderFactory,
77
        FactoryInterface $orderItemFactory,
78
        OrderItemQuantityModifierInterface $orderItemQuantityModifier,
79
        ObjectManager $orderManager,
80
        RepositoryInterface $channelRepository,
81
        RepositoryInterface $customerRepository,
82
        RepositoryInterface $productRepository,
83
        RepositoryInterface $countryRepository,
84
        PaymentMethodRepositoryInterface $paymentMethodRepository,
85
        ShippingMethodRepositoryInterface $shippingMethodRepository,
86
        FactoryInterface $addressFactory,
87
        StateMachineFactoryInterface $stateMachineFactory
88
    ) {
89
        $this->orderFactory = $orderFactory;
90
        $this->orderItemFactory = $orderItemFactory;
91
        $this->orderItemQuantityModifier = $orderItemQuantityModifier;
92
        $this->orderManager = $orderManager;
93
        $this->channelRepository = $channelRepository;
94
        $this->customerRepository = $customerRepository;
95
        $this->productRepository = $productRepository;
96
        $this->countryRepository = $countryRepository;
97
        $this->paymentMethodRepository = $paymentMethodRepository;
98
        $this->shippingMethodRepository = $shippingMethodRepository;
99
        $this->addressFactory = $addressFactory;
100
        $this->stateMachineFactory = $stateMachineFactory;
101
102
        $this->faker = \Faker\Factory::create();
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function load(array $options): void
109
    {
110
        $channels = $this->channelRepository->findAll();
111
        $customers = $this->customerRepository->findAll();
112
        $countries = $this->countryRepository->findAll();
113
114
        for ($i = 0; $i < $options['amount']; ++$i) {
115
            $channel = $this->faker->randomElement($channels);
116
            $customer = $this->faker->randomElement($customers);
117
            $countryCode = $this->faker->randomElement($countries)->getCode();
118
119
            $currencyCode = $channel->getBaseCurrency()->getCode();
120
            $localeCode = $this->faker->randomElement($channel->getLocales()->toArray())->getCode();
121
122
            /** @var OrderInterface $order */
123
            $order = $this->orderFactory->createNew();
124
            $order->setChannel($channel);
125
            $order->setCustomer($customer);
126
            $order->setCurrencyCode($currencyCode);
127
            $order->setLocaleCode($localeCode);
128
129
            $this->generateItems($order);
130
131
            $this->address($order, $countryCode);
132
            $this->selectShipping($order);
133
            $this->selectPayment($order);
134
            $this->completeCheckout($order);
135
136
            $this->orderManager->persist($order);
137
138
            if (0 === ($i % 50)) {
139
                $this->orderManager->flush();
140
            }
141
        }
142
143
        $this->orderManager->flush();
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function getName(): string
150
    {
151
        return 'order';
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void
158
    {
159
        $optionsNode
160
            ->children()
161
                ->integerNode('amount')->isRequired()->min(0)->end()
162
        ;
163
    }
164
165
    private function generateItems(OrderInterface $order): void
166
    {
167
        $numberOfItems = random_int(1, 5);
168
        $products = $this->productRepository->findAll();
169
        $generatedItems = [];
170
171
        for ($i = 0; $i < $numberOfItems; ++$i) {
172
            /** @var ProductInterface $product */
173
            $product = $this->faker->randomElement($products);
174
175
            if (!$product->hasChannel($order->getChannel())) {
176
                $i--;
177
                continue;
178
            }
179
180
            $variant = $this->faker->randomElement($product->getVariants()->toArray());
181
182
            if (array_key_exists($variant->getCode(), $generatedItems)) {
183
                /** @var OrderItemInterface $item */
184
                $item = $generatedItems[$variant->getCode()];
185
                $this->orderItemQuantityModifier->modify($item, $item->getQuantity() + random_int(1, 5));
186
187
                continue;
188
            }
189
190
            /** @var OrderItemInterface $item */
191
            $item = $this->orderItemFactory->createNew();
192
193
            $item->setVariant($variant);
194
            $this->orderItemQuantityModifier->modify($item, random_int(1, 5));
195
196
            $generatedItems[$variant->getCode()] = $item;
197
            $order->addItem($item);
198
        }
199
    }
200
201
    private function address(OrderInterface $order, string $countryCode): void
202
    {
203
        /** @var AddressInterface $address */
204
        $address = $this->addressFactory->createNew();
205
        $address->setFirstName($this->faker->firstName);
206
        $address->setLastName($this->faker->lastName);
207
        $address->setStreet($this->faker->streetAddress);
208
        $address->setCountryCode($countryCode);
209
        $address->setCity($this->faker->city);
210
        $address->setPostcode($this->faker->postcode);
211
212
        $order->setShippingAddress($address);
213
        $order->setBillingAddress(clone $address);
214
215
        $this->applyCheckoutStateTransition($order, OrderCheckoutTransitions::TRANSITION_ADDRESS);
216
    }
217
218
    private function selectShipping(OrderInterface $order): void
219
    {
220
        if ($order->getCheckoutState() === OrderCheckoutStates::STATE_SHIPPING_SKIPPED) {
221
            return;
222
        }
223
224
        $channel = $order->getChannel();
225
        $shippingMethods = $this->shippingMethodRepository->findEnabledForChannel($channel);
226
227
        if (count($shippingMethods) === 0) {
228
            throw new \InvalidArgumentException(sprintf(
229
                'You have no shipping method available for the channel with code "%s", but they are required to proceed an order',
230
                $channel->getCode()
231
            ));
232
        }
233
234
        $shippingMethod = $this->faker->randomElement($shippingMethods);
235
236
        /** @var ChannelInterface $channel */
237
        $channel = $order->getChannel();
238
        Assert::notNull($shippingMethod, $this->generateInvalidSkipMessage('shipping', $channel->getCode()));
239
240
        foreach ($order->getShipments() as $shipment) {
241
            $shipment->setMethod($shippingMethod);
242
        }
243
244
        $this->applyCheckoutStateTransition($order, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING);
245
    }
246
247
    private function selectPayment(OrderInterface $order): void
248
    {
249
        if ($order->getCheckoutState() === OrderCheckoutStates::STATE_PAYMENT_SKIPPED) {
250
            return;
251
        }
252
253
        $paymentMethod = $this
254
            ->faker
255
            ->randomElement($this->paymentMethodRepository->findEnabledForChannel($order->getChannel()))
256
        ;
257
258
        /** @var ChannelInterface $channel */
259
        $channel = $order->getChannel();
260
        Assert::notNull($paymentMethod, $this->generateInvalidSkipMessage('payment', $channel->getCode()));
261
262
        foreach ($order->getPayments() as $payment) {
263
            $payment->setMethod($paymentMethod);
264
        }
265
266
        $this->applyCheckoutStateTransition($order, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT);
267
    }
268
269
    private function completeCheckout(OrderInterface $order): void
270
    {
271
        if ($this->faker->boolean(25)) {
272
            $order->setNotes($this->faker->sentence);
273
        }
274
275
        $this->applyCheckoutStateTransition($order, OrderCheckoutTransitions::TRANSITION_COMPLETE);
276
    }
277
278
    private function applyCheckoutStateTransition(OrderInterface $order, string $transition): void
279
    {
280
        $this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->apply($transition);
281
    }
282
283
    private function generateInvalidSkipMessage(string $type, string $channelCode): string
284
    {
285
        return sprintf(
286
            "No enabled %s method was found for the channel '%s'. " .
287
            "Set 'skipping_%s_step_allowed' option to true for this channel if you want to skip %s method selection.",
288
            $type, $channelCode, $type, $type
289
        );
290
    }
291
}
292