OrderFormBuilder   C
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 308
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Importance

Changes 0
Metric Value
wmc 8
lcom 1
cbo 19
dl 0
loc 308
rs 6.875
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getAlias() 0 4 1
A getOrderProvider() 0 4 1
A getShippingMethodProvider() 0 4 1
A addShippingOptions() 0 19 1
A addPaymentOptions() 0 12 2
B buildForm() 0 247 2
1
<?php
2
/*
3
 * WellCommerce Open-Source E-Commerce Platform
4
 *
5
 * This file is part of the WellCommerce package.
6
 *
7
 * (c) Adam Piotrowski <[email protected]>
8
 *
9
 * For the full copyright and license information,
10
 * please view the LICENSE file that was distributed with this source code.
11
 */
12
namespace WellCommerce\Bundle\OrderBundle\Form\Admin;
13
14
use WellCommerce\Bundle\CoreBundle\Form\AbstractFormBuilder;
15
use WellCommerce\Bundle\CouponBundle\Entity\Coupon;
16
use WellCommerce\Bundle\CouponBundle\Helper\CouponHelper;
17
use WellCommerce\Bundle\OrderBundle\Context\OrderContext;
18
use WellCommerce\Bundle\OrderBundle\Entity\OrderModifier;
19
use WellCommerce\Bundle\OrderBundle\Entity\PaymentMethod;
20
use WellCommerce\Bundle\OrderBundle\Entity\ShippingMethod;
21
use WellCommerce\Bundle\OrderBundle\Entity\ShippingMethodCost;
22
use WellCommerce\Bundle\OrderBundle\Provider\Admin\OrderProviderInterface;
23
use WellCommerce\Bundle\OrderBundle\Provider\ShippingMethodProviderInterface;
24
use WellCommerce\Component\Form\Elements\ElementInterface;
25
use WellCommerce\Component\Form\Elements\FormInterface;
26
use WellCommerce\Component\Form\Elements\Optioned\Select;
27
28
/**
29
 * Class OrderFormBuilder
30
 *
31
 * @author Adam Piotrowski <[email protected]>
32
 */
33
class OrderFormBuilder extends AbstractFormBuilder
34
{
35
    public function getAlias(): string
36
    {
37
        return 'admin.order';
38
    }
39
    
40
    public function buildForm(FormInterface $form)
41
    {
42
        $order     = $this->getOrderProvider()->getCurrentOrder();
43
        $countries = $this->get('country.repository')->all();
44
        
45
        $requiredData = $form->addChild($this->getElement('nested_fieldset', [
46
            'name'  => 'required_data',
47
            'label' => 'order.form.fieldset.products',
48
        ]));
49
        
50
        $requiredData->addChild($this->getElement('order_editor', [
51
            'name'                => 'products',
52
            'label'               => 'order.heading.products',
53
            'repeat_min'          => 1,
54
            'repeat_max'          => ElementInterface::INFINITE,
55
            'load_products_route' => 'admin.product.grid',
56
            'on_change'           => 'OnProductListChange',
57
            'on_before_change'    => 'OnProductListBeforeChange',
58
            'transformer'         => $this->getRepositoryTransformer('order_product_collection', $this->get('order_product.repository')),
59
        ]));
60
        
61
        $contactDetails = $form->addChild($this->getElement('nested_fieldset', [
62
            'name'  => 'contactDetails',
63
            'label' => 'order.heading.contact_details',
64
        ]));
65
        
66
        $contactDetails->addChild($this->getElement('text_field', [
67
            'name'  => 'contactDetails.firstName',
68
            'label' => 'client.label.contact_details.first_name',
69
        ]));
70
        
71
        $contactDetails->addChild($this->getElement('text_field', [
72
            'name'  => 'contactDetails.lastName',
73
            'label' => 'client.label.contact_details.last_name',
74
        ]));
75
        
76
        $contactDetails->addChild($this->getElement('text_field', [
77
            'name'  => 'contactDetails.phone',
78
            'label' => 'client.label.contact_details.phone',
79
        ]));
80
        
81
        $contactDetails->addChild($this->getElement('text_field', [
82
            'name'  => 'contactDetails.secondaryPhone',
83
            'label' => 'client.label.contact_details.secondary_phone',
84
        ]));
85
        
86
        $contactDetails->addChild($this->getElement('text_field', [
87
            'name'  => 'contactDetails.email',
88
            'label' => 'client.label.contact_details.email',
89
        ]));
90
        
91
        $addresses = $form->addChild($this->getElement('columns', [
92
            'name'  => 'addresses',
93
            'label' => 'order.heading.address',
94
        ]));
95
        
96
        $billingAddress = $addresses->addChild($this->getElement('nested_fieldset', [
97
            'name'  => 'billingAddress',
98
            'label' => 'client.heading.billing_address',
99
        ]));
100
        
101
        $billingAddress->addChild($this->getElement('text_field', [
102
            'name'  => 'billingAddress.firstName',
103
            'label' => 'client.label.address.first_name',
104
        ]));
105
        
106
        $billingAddress->addChild($this->getElement('text_field', [
107
            'name'  => 'billingAddress.lastName',
108
            'label' => 'client.label.address.last_name',
109
        ]));
110
        
111
        $billingAddress->addChild($this->getElement('text_field', [
112
            'name'  => 'billingAddress.line1',
113
            'label' => 'client.label.address.line1',
114
        ]));
115
        
116
        $billingAddress->addChild($this->getElement('text_field', [
117
            'name'  => 'billingAddress.line2',
118
            'label' => 'client.label.address.line2',
119
        ]));
120
        
121
        $billingAddress->addChild($this->getElement('text_field', [
122
            'name'  => 'billingAddress.postalCode',
123
            'label' => 'client.label.address.postal_code',
124
        ]));
125
        
126
        $billingAddress->addChild($this->getElement('text_field', [
127
            'name'  => 'billingAddress.state',
128
            'label' => 'client.label.address.state',
129
        ]));
130
        
131
        $billingAddress->addChild($this->getElement('text_field', [
132
            'name'  => 'billingAddress.city',
133
            'label' => 'client.label.address.city',
134
        ]));
135
        
136
        $billingAddress->addChild($this->getElement('select', [
137
            'name'    => 'billingAddress.country',
138
            'label'   => 'client.label.address.country',
139
            'options' => $countries,
140
        ]));
141
        
142
        $billingAddress->addChild($this->getElement('text_field', [
143
            'name'  => 'billingAddress.vatId',
144
            'label' => 'client.label.address.vat_id',
145
        ]));
146
        
147
        $billingAddress->addChild($this->getElement('text_field', [
148
            'name'  => 'billingAddress.companyName',
149
            'label' => 'client.label.address.company_name',
150
        ]));
151
        
152
        $shippingAddress = $addresses->addChild($this->getElement('nested_fieldset', [
153
            'name'  => 'shippingAddress',
154
            'label' => 'client.heading.shipping_address',
155
        ]));
156
        
157
        $shippingAddress->addChild($this->getElement('text_field', [
158
            'name'  => 'shippingAddress.firstName',
159
            'label' => 'client.label.address.first_name',
160
        ]));
161
        
162
        $shippingAddress->addChild($this->getElement('text_field', [
163
            'name'  => 'shippingAddress.lastName',
164
            'label' => 'client.label.address.last_name',
165
        ]));
166
        
167
        $shippingAddress->addChild($this->getElement('text_field', [
168
            'name'  => 'shippingAddress.companyName',
169
            'label' => 'client.label.address.company_name',
170
        ]));
171
        
172
        $shippingAddress->addChild($this->getElement('text_field', [
173
            'name'  => 'shippingAddress.line1',
174
            'label' => 'client.label.address.line1',
175
        ]));
176
        
177
        $shippingAddress->addChild($this->getElement('text_field', [
178
            'name'  => 'shippingAddress.line2',
179
            'label' => 'client.label.address.line2',
180
        ]));
181
        
182
        $shippingAddress->addChild($this->getElement('text_field', [
183
            'name'  => 'shippingAddress.postalCode',
184
            'label' => 'client.label.address.postal_code',
185
        ]));
186
        
187
        $shippingAddress->addChild($this->getElement('text_field', [
188
            'name'  => 'shippingAddress.state',
189
            'label' => 'client.label.address.state',
190
        ]));
191
        
192
        $shippingAddress->addChild($this->getElement('text_field', [
193
            'name'  => 'shippingAddress.city',
194
            'label' => 'client.label.address.city',
195
        ]));
196
        
197
        $shippingAddress->addChild($this->getElement('select', [
198
            'name'    => 'shippingAddress.country',
199
            'label'   => 'client.label.address.country',
200
            'options' => $countries,
201
        ]));
202
        
203
        $orderDetails = $form->addChild($this->getElement('columns', [
204
            'name'  => 'orderMethodsDetails',
205
            'label' => 'order.heading.order_methods_details',
206
        ]));
207
        
208
        $paymentShippingData = $orderDetails->addChild($this->getElement('nested_fieldset', [
209
            'name'  => 'methods',
210
            'label' => 'order.heading.methods',
211
        ]));
212
        
213
        $shippingMethod = $paymentShippingData->addChild($this->getElement('select', [
214
            'name'        => 'shippingMethod',
215
            'label'       => 'order.label.shipping_method',
216
            'transformer' => $this->getRepositoryTransformer('entity', $this->get('shipping_method.repository')),
217
        ]));
218
        
219
        $this->addShippingOptions($shippingMethod);
220
        
221
        $paymentMethod = $paymentShippingData->addChild($this->getElement('select', [
222
            'name'        => 'paymentMethod',
223
            'label'       => 'order.label.payment_method',
224
            'transformer' => $this->getRepositoryTransformer('entity', $this->get('payment_method.repository')),
225
        ]));
226
        
227
        $this->addPaymentOptions($paymentMethod);
228
        
229
        $order->getModifiers()->map(function (OrderModifier $modifier) use ($paymentShippingData) {
230
            $paymentShippingData->addChild($this->getElement('constant', [
231
                'name'  => 'summary.' . $modifier->getName(),
232
                'label' => $this->trans($modifier->getDescription()),
233
            ]))->setValue($modifier->getGrossAmount());
234
        });
235
        
236
        if ($order->getCoupon() instanceof Coupon) {
237
            $paymentShippingData->addChild($this->getElement('constant', [
238
                'name'  => 'coupon.code',
239
                'label' => 'order.label.coupon.code',
240
            ]))->setValue($order->getCoupon()->getCode());
241
            
242
            $paymentShippingData->addChild($this->getElement('constant', [
243
                'name'  => 'coupon.modifier',
244
                'label' => 'order.label.coupon.modifier',
245
            ]))->setValue(CouponHelper::formatModifier($order->getCoupon()));
246
        }
247
        
248
        $summaryData = $orderDetails->addChild($this->getElement('nested_fieldset', [
249
            'name'  => 'summary',
250
            'label' => 'order.heading.order_total',
251
        ]));
252
        
253
        $summaryData->addChild($this->getElement('constant', [
254
            'name'  => 'productTotal.netPrice',
255
            'label' => 'order.label.product_total.net_price',
256
        ]));
257
        
258
        $summaryData->addChild($this->getElement('constant', [
259
            'name'  => 'productTotal.taxAmount',
260
            'label' => 'order.label.product_total.tax_amount',
261
        ]));
262
        
263
        $summaryData->addChild($this->getElement('constant', [
264
            'name'  => 'productTotal.grossPrice',
265
            'label' => 'order.label.product_total.gross_price',
266
        ]));
267
        
268
        $summaryData->addChild($this->getElement('constant', [
269
            'name'  => 'summary.netAmount',
270
            'label' => 'order.label.summary.net_amount',
271
        ]));
272
        
273
        $summaryData->addChild($this->getElement('constant', [
274
            'name'  => 'summary.taxAmount',
275
            'label' => 'order.label.summary.tax_amount',
276
        ]));
277
        
278
        $summaryData->addChild($this->getElement('constant', [
279
            'name'  => 'summary.grossAmount',
280
            'label' => 'order.label.summary.gross_amount',
281
        ]));
282
        
283
        $form->addFilter($this->getFilter('no_code'));
284
        $form->addFilter($this->getFilter('trim'));
285
        $form->addFilter($this->getFilter('secure'));
286
    }
287
    
288
    private function getOrderProvider(): OrderProviderInterface
289
    {
290
        return $this->get('order.provider.admin');
291
    }
292
    
293
    private function getShippingMethodProvider(): ShippingMethodProviderInterface
294
    {
295
        return $this->get('shipping_method.provider');
296
    }
297
    
298
    /**
299
     * Adds shipping method options to select
300
     *
301
     * @param ElementInterface|Select $radioGroup
302
     */
303
    private function addShippingOptions(ElementInterface $radioGroup)
304
    {
305
        $order      = $this->getOrderProvider()->getCurrentOrder();
306
        $collection = $this->getShippingMethodProvider()->getCosts(new OrderContext($order));
307
        
308
        $collection->map(function (ShippingMethodCost $shippingMethodCost) use ($radioGroup) {
309
            $shippingMethod = $shippingMethodCost->getShippingMethod();
310
            $baseCurrency   = $shippingMethod->getCurrency()->getCode();
311
            $grossAmount    = $shippingMethodCost->getCost()->getGrossAmount();
312
            
313
            $label = sprintf(
314
                '%s (%s)',
315
                $shippingMethod->translate()->getName(),
316
                $this->getCurrencyHelper()->convertAndFormat($grossAmount, $baseCurrency)
317
            );
318
            
319
            $radioGroup->addOptionToSelect($shippingMethod->getId(), $label);
0 ignored issues
show
Bug introduced by Adam Piotrowski
It seems like you code against a concrete implementation and not the interface WellCommerce\Component\F...ements\ElementInterface as the method addOptionToSelect() does only exist in the following implementations of said interface: WellCommerce\Component\F...d\AbstractOptionedField, WellCommerce\Component\F...Elements\Optioned\Combo, WellCommerce\Component\F...Optioned\DataGridSelect, WellCommerce\Component\F...ts\Optioned\MultiSelect, WellCommerce\Component\F...\Optioned\ProductSelect, WellCommerce\Component\F...nts\Optioned\RadioGroup, WellCommerce\Component\F...lements\Optioned\Select.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
320
        });
321
    }
322
    
323
    /**
324
     * Adds payment method options to select
325
     *
326
     * @param ElementInterface|Select $radioGroup
327
     */
328
    private function addPaymentOptions(ElementInterface $radioGroup)
329
    {
330
        $order          = $this->getOrderProvider()->getCurrentOrder();
331
        $shippingMethod = $order->getShippingMethod();
332
        if ($shippingMethod instanceof ShippingMethod) {
333
            $collection = $shippingMethod->getPaymentMethods();
334
            
335
            $collection->map(function (PaymentMethod $paymentMethod) use ($radioGroup) {
336
                $radioGroup->addOptionToSelect($paymentMethod->getId(), $paymentMethod->translate()->getName());
0 ignored issues
show
Bug introduced by Adam Piotrowski
It seems like you code against a concrete implementation and not the interface WellCommerce\Component\F...ements\ElementInterface as the method addOptionToSelect() does only exist in the following implementations of said interface: WellCommerce\Component\F...d\AbstractOptionedField, WellCommerce\Component\F...Elements\Optioned\Combo, WellCommerce\Component\F...Optioned\DataGridSelect, WellCommerce\Component\F...ts\Optioned\MultiSelect, WellCommerce\Component\F...\Optioned\ProductSelect, WellCommerce\Component\F...nts\Optioned\RadioGroup, WellCommerce\Component\F...lements\Optioned\Select.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
337
            });
338
        }
339
    }
340
}
341