Completed
Push — 4.0 ( 268f2c...88f012 )
by Hideki
05:48 queued 10s
created

src/Eccube/Form/Type/Shopping/OrderType.php (1 issue)

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 EC-CUBE
5
 *
6
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Form\Type\Shopping;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Eccube\Entity\Delivery;
18
use Eccube\Entity\Order;
19
use Eccube\Entity\Payment;
20
use Eccube\Repository\BaseInfoRepository;
21
use Eccube\Repository\DeliveryRepository;
22
use Eccube\Repository\OrderRepository;
23
use Eccube\Repository\PaymentRepository;
24
use Eccube\Request\Context;
25
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
26
use Symfony\Component\Form\AbstractType;
27
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
28
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
29
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
30
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
31
use Symfony\Component\Form\FormBuilderInterface;
32
use Symfony\Component\Form\FormEvent;
33
use Symfony\Component\Form\FormEvents;
34
use Symfony\Component\Form\FormInterface;
35
use Symfony\Component\OptionsResolver\OptionsResolver;
36
use Symfony\Component\Validator\Constraints\Length;
37
use Symfony\Component\Validator\Constraints\NotBlank;
38
use Symfony\Component\Validator\Constraints\Regex;
39
40
class OrderType extends AbstractType
41
{
42
    /**
43
     * @var OrderRepository
44
     */
45
    protected $orderRepository;
46
47
    /**
48
     * @var DeliveryRepository
49
     */
50
    protected $deliveryRepository;
51
52
    /**
53
     * @var PaymentRepository
54
     */
55
    protected $paymentRepository;
56
57
    /**
58
     * @var BaseInfoRepository
59 50
     */
60
    protected $baseInfoRepository;
61
62
    /**
63
     * @var Context
64 50
     */
65 50
    protected $requestContext;
66 50
67
    /**
68
     * OrderType constructor.
69
     *
70
     * @param OrderRepository $orderRepository
71
     * @param DeliveryRepository $deliveryRepository
72 50
     * @param PaymentRepository $paymentRepository
73
     * @param BaseInfoRepository $baseInfoRepository
74
     * @param Context $requestContext
75 50
     */
76 50
    public function __construct(
77 50
        OrderRepository $orderRepository,
78
        DeliveryRepository $deliveryRepository,
79 50
        PaymentRepository $paymentRepository,
80
        BaseInfoRepository $baseInfoRepository,
81 50
        Context $requestContext
82
    ) {
83
        $this->orderRepository = $orderRepository;
84
        $this->deliveryRepository = $deliveryRepository;
85 50
        $this->paymentRepository = $paymentRepository;
86 50
        $this->baseInfoRepository = $baseInfoRepository;
87 50
        $this->requestContext = $requestContext;
88
    }
89 50
90 50
    /**
91
     * {@inheritdoc}
92 50
     */
93 50
    public function buildForm(FormBuilderInterface $builder, array $options)
94
    {
95
        // ShoppingController::checkoutから呼ばれる場合は, フォーム項目の定義をスキップする.
96 50
        if ($options['skip_add_form']) {
97
            return;
98
        }
99
100 50
        $builder->add('message', TextareaType::class, [
101 50
            'required' => false,
102 50
            'constraints' => [
103
                new Length(['min' => 0, 'max' => 3000]),
104 50
            ],
105
        ])->add('Shippings', CollectionType::class, [
106
            'entry_type' => ShippingType::class,
107 50
            'by_reference' => false,
108 50
        ])->add('redirect_to', HiddenType::class, [
109 50
            'mapped' => false,
110
        ]);
111 50
112
        if ($this->baseInfoRepository->get()->isOptionPoint() && $this->requestContext->getCurrentUser()) {
113 50
            $builder->add('use_point', IntegerType::class, [
114 50
                'required' => false,
115 50
                'constraints' => [
116
                    new NotBlank(),
117 50
                    new Regex([
118
                        'pattern' => "/^\d+$/u",
119
                        'message' => 'form_error.numeric_only',
120
                    ]),
121
                    new Length(['max' => 11]),
122 50
                ],
123 50
            ]);
124 50
        }
125
126 50
        // 支払い方法のプルダウンを生成
127 50
        $builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
128
            /** @var Order $Order */
129
            $Order = $event->getData();
130
            if (null === $Order || !$Order->getId()) {
131
                return;
132 50
            }
133
134 50
            $Deliveries = $this->getDeliveries($Order);
135 50
            $Payments = $this->getPayments($Deliveries);
136 50
            $Payments = $this->filterPayments($Payments, $Order->getPaymentTotal());
137 50
138
            $form = $event->getForm();
139
            $this->addPaymentForm($form, $Payments, $Order->getPayment());
140 50
        });
141 50
142
        // 支払い方法のプルダウンを生成(Submit時)
143
        // 配送方法の選択によって使用できる支払い方法がかわるため, フォームを再生成する.
144 50
        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
145
            /** @var Order $Order */
146 50
            $Order = $event->getForm()->getData();
147
            $data = $event->getData();
148 50
149 50
            $Deliveries = [];
150 50
            if (!empty($data['Shippings'])) {
151 50
                foreach ($data['Shippings'] as $Shipping) {
152
                    if (!empty($Shipping['Delivery'])) {
153 50
                        $Delivery = $this->deliveryRepository->find($Shipping['Delivery']);
154 50
                        if ($Delivery) {
155 50
                            $Deliveries[] = $Delivery;
156 50
                        }
157
                    }
158
                }
159
            }
160
161 50
            $Payments = $this->getPayments($Deliveries);
162
            $Payments = $this->filterPayments($Payments, $Order->getPaymentTotal());
163 50
164
            $form = $event->getForm();
165
            $this->addPaymentForm($form, $Payments);
166 50
        });
167
168
        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
169
            /** @var Order $Order */
170
            $Order = $event->getData();
171 50
            $Payment = $Order->getPayment();
172 50
            if ($Payment && $Payment->getMethod()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $Payment->getMethod() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
173 50
                $Order->setPaymentMethod($Payment->getMethod());
174
            }
175 25
        });
176
    }
177 25
178 25
    public function configureOptions(OptionsResolver $resolver)
179
    {
180 25
        $resolver->setDefaults(
181 25
            [
182
                'data_class' => 'Eccube\Entity\Order',
183
                'skip_add_form' => false,
184 50
            ]
185
        );
186
    }
187
188 50
    public function getBlockPrefix()
189
    {
190 50
        return '_shopping_order';
191
    }
192 50
193
    private function addPaymentForm(FormInterface $form, array $choices, Payment $data = null)
194
    {
195
        $message = trans('front.shopping.payment_method_unselected');
196
197 50
        if (empty($choices)) {
198
            $message = trans('front.shopping.payment_method_not_fount');
199 50
        }
200
201
        $form->add('Payment', EntityType::class, [
202
            'class' => Payment::class,
203
            'choice_label' => 'method',
204
            'expanded' => true,
205
            'multiple' => false,
206
            'placeholder' => false,
207
            'constraints' => [
208
                new NotBlank(['message' => $message]),
209
            ],
210
            'choices' => $choices,
211
            'data' => $data,
212
            'invalid_message' => $message,
213
        ]);
214
    }
215
216
    /**
217
     * 出荷に紐づく配送方法を取得する.
218
     *
219
     * @param Order $Order
220
     *
221
     * @return Delivery[]
222
     */
223
    private function getDeliveries(Order $Order)
224
    {
225
        $Deliveries = [];
226
        foreach ($Order->getShippings() as $Shipping) {
227
            $Delivery = $Shipping->getDelivery();
228
            if ($Delivery->isVisible()) {
229
                $Deliveries[] = $Shipping->getDelivery();
230
            }
231
        }
232
233
        return array_unique($Deliveries);
234
    }
235
236
    /**
237
     * 配送方法に紐づく支払い方法を取得する
238
     * 各配送方法に共通する支払い方法のみ返す.
239
     *
240
     * @param Delivery[] $Deliveries
241
     *
242
     * @return ArrayCollection
243
     */
244
    private function getPayments($Deliveries)
245
    {
246
        $PaymentsByDeliveries = [];
247
        foreach ($Deliveries as $Delivery) {
248
            $PaymentOptions = $Delivery->getPaymentOptions();
249
            foreach ($PaymentOptions as $PaymentOption) {
250
                /** @var Payment $Payment */
251
                $Payment = $PaymentOption->getPayment();
252
                if ($Payment->isVisible()) {
253
                    $PaymentsByDeliveries[$Delivery->getId()][] = $Payment;
254
                }
255
            }
256
        }
257
258
        if (empty($PaymentsByDeliveries)) {
259
            return new ArrayCollection();
260
        }
261
262
        $i = 0;
263
        $PaymentsIntersected = [];
264
        foreach ($PaymentsByDeliveries as $Payments) {
265
            if ($i === 0) {
266
                $PaymentsIntersected = $Payments;
267
            } else {
268
                $PaymentsIntersected = array_intersect($PaymentsIntersected, $Payments);
269
            }
270
            $i++;
271
        }
272
273
        return new ArrayCollection($PaymentsIntersected);
274
    }
275
276
    /**
277
     * 支払い方法の利用条件でフィルタをかける.
278
     *
279
     * @param ArrayCollection $Payments
280
     * @param $total
281
     *
282
     * @return Payment[]
283
     */
284
    private function filterPayments(ArrayCollection $Payments, $total)
285
    {
286
        $PaymentArrays = $Payments->filter(function (Payment $Payment) use ($total) {
287
            $min = $Payment->getRuleMin();
288
            $max = $Payment->getRuleMax();
289
290
            if (null !== $min && $total < $min) {
291
                return false;
292
            }
293
294
            if (null !== $max && $total > $max) {
295
                return false;
296
            }
297
298
            return true;
299
        })->toArray();
300
        usort($PaymentArrays, function (Payment $a, Payment $b) {
301
            return $a->getSortNo() < $b->getSortNo() ? 1 : -1;
302
        });
303
304
        return $PaymentArrays;
305
    }
306
}
307