src/Eccube/Form/Type/Admin/OrderType.php line 213

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Eccube\Form\Type\Admin;
  13. use Doctrine\Common\Collections\ArrayCollection;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use Eccube\Common\EccubeConfig;
  16. use Eccube\Entity\Master\OrderStatus;
  17. use Eccube\Entity\Order;
  18. use Eccube\Entity\Payment;
  19. use Eccube\Form\DataTransformer;
  20. use Eccube\Form\Type\AddressType;
  21. use Eccube\Form\Type\KanaType;
  22. use Eccube\Form\Type\NameType;
  23. use Eccube\Form\Type\PhoneNumberType;
  24. use Eccube\Form\Type\PostalType;
  25. use Eccube\Form\Type\PriceType;
  26. use Eccube\Form\Validator\Email;
  27. use Eccube\Repository\Master\OrderStatusRepository;
  28. use Eccube\Service\OrderStateMachine;
  29. use Symfony\Bridge\Doctrine\Form\Type\EntityType;
  30. use Symfony\Component\Form\AbstractType;
  31. use Symfony\Component\Form\Extension\Core\Type\CollectionType;
  32. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  33. use Symfony\Component\Form\Extension\Core\Type\HiddenType;
  34. use Symfony\Component\Form\Extension\Core\Type\NumberType;
  35. use Symfony\Component\Form\Extension\Core\Type\TextType;
  36. use Symfony\Component\Form\Extension\Core\Type\TextareaType;
  37. use Symfony\Component\Form\FormBuilderInterface;
  38. use Symfony\Component\Form\FormError;
  39. use Symfony\Component\Form\FormEvent;
  40. use Symfony\Component\Form\FormEvents;
  41. use Symfony\Component\OptionsResolver\OptionsResolver;
  42. use Symfony\Component\Validator\Constraints as Assert;
  43. class OrderType extends AbstractType
  44. {
  45.     /**
  46.      * @var EntityManagerInterface
  47.      */
  48.     protected $entityManager;
  49.     /**
  50.      * @var EccubeConfig
  51.      */
  52.     protected $eccubeConfig;
  53.     /**
  54.      * @var OrderStateMachine
  55.      */
  56.     protected $orderStateMachine;
  57.     /**
  58.      * @var OrderStatusRepository
  59.      */
  60.     protected $orderStatusRepository;
  61.     /**
  62.      * OrderType constructor.
  63.      *
  64.      * @param EntityManagerInterface $entityManager
  65.      * @param EccubeConfig $eccubeConfig
  66.      * @param OrderStateMachine $orderStateMachine
  67.      */
  68.     public function __construct(
  69.         EntityManagerInterface $entityManager,
  70.         EccubeConfig $eccubeConfig,
  71.         OrderStateMachine $orderStateMachine,
  72.         OrderStatusRepository $orderStatusRepository
  73.     ) {
  74.         $this->entityManager $entityManager;
  75.         $this->eccubeConfig $eccubeConfig;
  76.         $this->orderStateMachine $orderStateMachine;
  77.         $this->orderStatusRepository $orderStatusRepository;
  78.     }
  79.     /**
  80.      * {@inheritdoc}
  81.      */
  82.     public function buildForm(FormBuilderInterface $builder, array $options)
  83.     {
  84.         $builder
  85.             ->add('name'NameType::class, [
  86.                 'required' => false,
  87.                 'options' => [
  88.                     'constraints' => [
  89.                         new Assert\NotBlank(),
  90.                     ],
  91.                 ],
  92.             ])
  93.             ->add('kana'KanaType::class, [
  94.                 'required' => false,
  95.                 'options' => [
  96.                     'constraints' => [
  97.                         new Assert\NotBlank(),
  98.                     ],
  99.                 ],
  100.             ])
  101.             ->add('company_name'TextType::class, [
  102.                 'required' => false,
  103.                 'constraints' => [
  104.                     new Assert\Length([
  105.                         'max' => $this->eccubeConfig['eccube_stext_len'],
  106.                     ]),
  107.                 ],
  108.             ])
  109.             ->add('postal_code'PostalType::class, [
  110.                 'required' => false,
  111.                 'constraints' => [
  112.                     new Assert\NotBlank(),
  113.                 ],
  114.                 'options' => [
  115.                     'attr' => ['class' => 'p-postal-code'],
  116.                 ],
  117.             ])
  118.             ->add('address'AddressType::class, [
  119.                 'required' => false,
  120.                 'pref_options' => [
  121.                     'constraints' => [
  122.                         new Assert\NotBlank(),
  123.                     ],
  124.                     'attr' => ['class' => 'p-region-id'],
  125.                 ],
  126.                 'addr01_options' => [
  127.                     'constraints' => [
  128.                         new Assert\NotBlank(),
  129.                         new Assert\Length([
  130.                             'max' => $this->eccubeConfig['eccube_mtext_len'],
  131.                         ]),
  132.                     ],
  133.                     'attr' => ['class' => 'p-locality p-street-address'],
  134.                 ],
  135.                 'addr02_options' => [
  136.                     'required' => false,
  137.                     'constraints' => [
  138.                         new Assert\NotBlank(),
  139.                         new Assert\Length([
  140.                             'max' => $this->eccubeConfig['eccube_mtext_len'],
  141.                         ]),
  142.                     ],
  143.                     'attr' => ['class' => 'p-extended-address'],
  144.                 ],
  145.             ])
  146.             ->add('email'EmailType::class, [
  147.                 'required' => false,
  148.                 'constraints' => [
  149.                     new Assert\NotBlank(),
  150.                     new Email(['strict' => $this->eccubeConfig['eccube_rfc_email_check']]),
  151.                 ],
  152.             ])
  153.             ->add('phone_number'PhoneNumberType::class, [
  154.                 'required' => false,
  155.                 'constraints' => [
  156.                     new Assert\NotBlank(),
  157.                 ],
  158.             ])
  159.             ->add('company_name'TextType::class, [
  160.                 'required' => false,
  161.                 'constraints' => [
  162.                     new Assert\Length([
  163.                         'max' => $this->eccubeConfig['eccube_stext_len'],
  164.                     ]),
  165.                 ],
  166.             ])
  167.             ->add('message'TextareaType::class, [
  168.                 'required' => false,
  169.                 'constraints' => [
  170.                     new Assert\Length([
  171.                         'max' => $this->eccubeConfig['eccube_ltext_len'],
  172.                     ]),
  173.                 ],
  174.             ])
  175.             ->add('discount'PriceType::class, [
  176.                 'required' => false,
  177.             ])
  178.             ->add('delivery_fee_total'PriceType::class, [
  179.                 'required' => false,
  180.             ])
  181.             ->add('charge'PriceType::class, [
  182.                 'required' => false,
  183.             ])
  184.             ->add('use_point'NumberType::class, [
  185.                 'required' => false,
  186.                 'constraints' => [
  187.                     new Assert\Regex([
  188.                         'pattern' => "/^\d+$/u",
  189.                         'message' => 'form_error.numeric_only',
  190.                     ]),
  191.                 ],
  192.             ])
  193.             ->add('note'TextareaType::class, [
  194.                 'required' => false,
  195.                 'constraints' => [
  196.                     new Assert\Length([
  197.                         'max' => $this->eccubeConfig['eccube_ltext_len'],
  198.                     ]),
  199.                 ],
  200.             ])
  201.             ->add('Payment'EntityType::class, [
  202.                 'required' => false,
  203.                 'class' => Payment::class,
  204.                 'choice_label' => function (Payment $Payment) {
  205.                     return $Payment->isVisible()
  206.                         ? $Payment->getMethod()
  207.                         : $Payment->getMethod().trans('admin.common.hidden_label');
  208.                 },
  209.                 'placeholder' => false,
  210.                 'query_builder' => function ($er) {
  211.                     return $er->createQueryBuilder('p')
  212.                         ->orderBy('p.visible''DESC')  // 非表示は下に配置
  213.                         ->addOrderBy('p.sort_no''ASC');
  214.                 },
  215.                 'constraints' => [
  216.                     new Assert\NotBlank(),
  217.                 ],
  218.             ])
  219.             ->add('OrderItems'CollectionType::class, [
  220.                 'entry_type' => OrderItemType::class,
  221.                 'allow_add' => true,
  222.                 'allow_delete' => true,
  223.                 'prototype' => true,
  224.             ])
  225.             ->add('OrderItemsErrors'TextType::class, [
  226.                 'mapped' => false,
  227.             ])
  228.             ->add('return_link'HiddenType::class, [
  229.                 'mapped' => false,
  230.             ]);
  231.         $builder
  232.             ->add($builder->create('Customer'HiddenType::class)
  233.                 ->addModelTransformer(new DataTransformer\EntityToIdTransformer(
  234.                     $this->entityManager,
  235.                     '\Eccube\Entity\Customer'
  236.                 )));
  237.         $builder->addEventListener(FormEvents::POST_SET_DATA, [$this'sortOrderItems']);
  238.         $builder->addEventListener(FormEvents::POST_SET_DATA, [$this'addOrderStatusForm']);
  239.         $builder->addEventListener(FormEvents::POST_SET_DATA, [$this'addShippingForm']);
  240.         $builder->addEventListener(FormEvents::POST_SUBMIT, [$this'copyFields']);
  241.         $builder->addEventListener(FormEvents::POST_SUBMIT, [$this'validateOrderStatus']);
  242.         $builder->addEventListener(FormEvents::POST_SUBMIT, [$this'validateOrderItems']);
  243.         $builder->addEventListener(FormEvents::POST_SUBMIT, [$this'associateOrderAndShipping']);
  244.     }
  245.     /**
  246.      * {@inheritdoc}
  247.      */
  248.     public function configureOptions(OptionsResolver $resolver)
  249.     {
  250.         $resolver->setDefaults([
  251.             'data_class' => Order::class,
  252.         ]);
  253.     }
  254.     /**
  255.      * {@inheritdoc}
  256.      */
  257.     public function getBlockPrefix()
  258.     {
  259.         return 'order';
  260.     }
  261.     /**
  262.      * 受注明細をソートする.
  263.      *
  264.      * @param FormEvent $event
  265.      */
  266.     public function sortOrderItems(FormEvent $event)
  267.     {
  268.         /** @var Order $Order */
  269.         $Order $event->getData();
  270.         if (null === $Order) {
  271.             return;
  272.         }
  273.         $OrderItems $Order->getItems();
  274.         $form $event->getForm();
  275.         $form['OrderItems']->setData($OrderItems);
  276.     }
  277.     /**
  278.      * 受注ステータスのフォームを追加する
  279.      * 新規登録の際は, ユーザ編集不可のため追加しない.
  280.      *
  281.      * ステータスのプルダウンは, ステートマシンで遷移可能なステータスのみ表示する.
  282.      *
  283.      * @param FormEvent $event
  284.      */
  285.     public function addOrderStatusForm(FormEvent $event)
  286.     {
  287.         /** @var Order $Order */
  288.         $Order $event->getData();
  289.         if (null === $Order || ($Order && !$Order->getId())) {
  290.             return;
  291.         }
  292.         /** @var ArrayCollection|OrderStatus[] $OrderStatuses */
  293.         $OrderStatuses $this->orderStatusRepository->findBy([], ['sort_no' => 'ASC']);
  294.         $OrderStatuses = new ArrayCollection($OrderStatuses);
  295.         foreach ($OrderStatuses as $Status) {
  296.             // 同一ステータスはスキップ
  297.             if ($Order->getOrderStatus()->getId() == $Status->getId()) {
  298.                 continue;
  299.             }
  300.             // 遷移できないステータスはリストから除外する.
  301.             if (!$this->orderStateMachine->can($Order$Status)) {
  302.                 $OrderStatuses->removeElement($Status);
  303.             }
  304.         }
  305.         $form $event->getForm();
  306.         $form->add('OrderStatus'EntityType::class, [
  307.             'class' => OrderStatus::class,
  308.             'choices' => $OrderStatuses,
  309.             'choice_label' => 'name',
  310.             'constraints' => [
  311.                 new Assert\NotBlank(),
  312.             ],
  313.             // 変更前後のステータスチェックが必要なのでmapped => false で定義する.
  314.             'mapped' => false,
  315.             'data' => $Order->getOrderStatus(),
  316.         ]);
  317.     }
  318.     /**
  319.      * 単一配送時に, Shippingのフォームを追加する.
  320.      * 複数配送時はShippingの編集は行わない.
  321.      *
  322.      * @param FormEvent $event
  323.      */
  324.     public function addShippingForm(FormEvent $event)
  325.     {
  326.         /** @var Order $Order */
  327.         $Order $event->getData();
  328.         // 複数配送時はShippingの編集は行わない
  329.         if ($Order && $Order->isMultiple()) {
  330.             return;
  331.         }
  332.         $data $Order $Order->getShippings()->first() : null;
  333.         $form $event->getForm();
  334.         $form->add('Shipping'ShippingType::class, [
  335.             'mapped' => false,
  336.             'data' => $data,
  337.         ]);
  338.     }
  339.     /**
  340.      * フォームからPOSTされない情報をコピーする.
  341.      *
  342.      * - 支払方法の名称
  343.      * - 会員の性別/職業/誕生日
  344.      * - 受注ステータス(新規登録時)
  345.      *
  346.      * @param FormEvent $event
  347.      */
  348.     public function copyFields(FormEvent $event)
  349.     {
  350.         /** @var Order $Order */
  351.         $Order $event->getData();
  352.         // 支払方法の名称をコピーする.
  353.         if ($Payment $Order->getPayment()) {
  354.             $Order->setPaymentMethod($Payment->getMethod());
  355.         }
  356.         // 会員受注の場合、会員の性別/職業/誕生日をエンティティにコピーする
  357.         if ($Customer $Order->getCustomer()) {
  358.             $Order->setSex($Customer->getSex());
  359.             $Order->setJob($Customer->getJob());
  360.             $Order->setBirth($Customer->getBirth());
  361.         }
  362.         // 新規登録時は, 新規受付ステータスで登録する.
  363.         if (null === $Order->getOrderStatus()) {
  364.             $Order->setOrderStatus($this->orderStatusRepository->find(OrderStatus::NEW));
  365.         } else {
  366.             // 編集時は, mapped => falseで定義しているため, フォームから変更後データを取得する.
  367.             $form $event->getForm();
  368.             $Order->setOrderStatus($form['OrderStatus']->getData());
  369.         }
  370.         // 新規登録時は受注日を登録する.
  371.         if (null === $Order->getOrderDate()) {
  372.             $Order->setOrderDate(new \DateTime());
  373.         }
  374.     }
  375.     /**
  376.      * 受注ステータスのバリデーションを行う.
  377.      *
  378.      * @param FormEvent $event
  379.      */
  380.     public function validateOrderStatus(FormEvent $event)
  381.     {
  382.         /** @var Order $Order */
  383.         $Order $event->getData();
  384.         if (!$Order->getId()) {
  385.             return;
  386.         }
  387.         $form $event->getForm();
  388.         if (!$form['OrderStatus']->isValid()) {
  389.             return;
  390.         }
  391.         // mapped => falseで定義しているため, Orderのステータスは変更されない
  392.         $oldStatus $Order->getOrderStatus();
  393.         // 変更後のステータスはFormから直接取得する.
  394.         $newStatus $form['OrderStatus']->getData();
  395.         // ステータスに変更があった場合のみチェックする.
  396.         if ($oldStatus->getId() != $newStatus->getId()) {
  397.             if (!$this->orderStateMachine->can($Order$newStatus)) {
  398.                 $form['OrderStatus']->addError(
  399.                     new FormError(trans('admin.order.failed_to_change_status__short'$oldStatus->getName(), $newStatus->getName())));
  400.             }
  401.         }
  402.     }
  403.     /**
  404.      * 受注明細のバリデーションを行う.
  405.      * 商品明細が1件も登録されていない場合はエラーとする.
  406.      *
  407.      * @param FormEvent $event
  408.      */
  409.     public function validateOrderItems(FormEvent $event)
  410.     {
  411.         /** @var Order $Order */
  412.         $Order $event->getData();
  413.         $OrderItems $Order->getOrderItems();
  414.         $count 0;
  415.         foreach ($OrderItems as $OrderItem) {
  416.             if ($OrderItem->isProduct()) {
  417.                 $count++;
  418.             }
  419.         }
  420.         // 商品明細が1件もない場合はエラーとする.
  421.         if ($count 1) {
  422.             // 画面下部にエラーメッセージを表示させる
  423.             $form $event->getForm();
  424.             $form['OrderItemsErrors']->addError(new FormError(trans('admin.order.product_item_not_found')));
  425.         }
  426.     }
  427.     /**
  428.      * 受注明細と, Order/Shippingの紐付けを行う.
  429.      *
  430.      * @param FormEvent $event
  431.      */
  432.     public function associateOrderAndShipping(FormEvent $event)
  433.     {
  434.         /** @var Order $Order */
  435.         $Order $event->getData();
  436.         $OrderItems $Order->getOrderItems();
  437.         // 明細とOrder, Shippingを紐付ける.
  438.         // 新規の明細のみが対象, 更新時はスキップする.
  439.         foreach ($OrderItems as $OrderItem) {
  440.             // 更新時はスキップ
  441.             if ($OrderItem->getId()) {
  442.                 continue;
  443.             }
  444.             $OrderItem->setOrder($Order);
  445.             // 送料明細の紐付けを行う.
  446.             // 複数配送の場合は, 常に最初のShippingと紐付ける.
  447.             // Order::getShippingsは氏名でソートされている.
  448.             if ($OrderItem->isDeliveryFee()) {
  449.                 $OrderItem->setShipping($Order->getShippings()->first());
  450.             }
  451.             // 商品明細の紐付けを行う.
  452.             // 複数配送時は, 明細の追加は行われないためスキップする.
  453.             if ($OrderItem->isProduct() && !$Order->isMultiple()) {
  454.                 $OrderItem->setShipping($Order->getShippings()->first());
  455.             }
  456.         }
  457.     }
  458. }