src/Juki/Bundle/AppBundle/EventListener/ProductSubscriber.php line 643

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Juki\Bundle\AppBundle\EventListener;
  4. use Doctrine\Common\Util\Debug;
  5. use Doctrine\ORM\EntityManagerInterface;
  6. use Faker\Factory;
  7. use Hitso\Bundle\CatalogBundle\Entity\Category;
  8. use Hitso\Bundle\CatalogBundle\Entity\Product;
  9. use Hitso\Bundle\CommonBundle\Doctrine\Event\EntityEvent;
  10. use Hitso\Bundle\CommonBundle\Doctrine\Repository\EntityRepository;
  11. use Hitso\Bundle\CommonBundle\Form\Type\LinkType;
  12. use Juki\Bundle\AppBundle\Entity\Dictionary\FeatureHsm;
  13. use Juki\Bundle\AppBundle\Form\Type\AccessoryType;
  14. use Hitso\Bundle\CommonBundle\Form\Type\KeyValueUnitType;
  15. use Hitso\Bundle\CommonBundle\Form\Type\RepeatableTextCollectionType;
  16. use Hitso\Bundle\CommonBundle\Form\Type\RepeatableTextType;
  17. use Hitso\Bundle\CommonBundle\Form\Type\TableCollectionType;
  18. use Hitso\Bundle\CommonBundle\Helper\Request\RequestHelper;
  19. use Hitso\Bundle\ContentBundle\Entity\Article;
  20. use Hitso\Bundle\FormBundle\Event\FormEvent;
  21. use Hitso\Bundle\FormBundle\Form\Conditions\Equals;
  22. use Hitso\Bundle\FormBundle\Form\Dependencies\Show;
  23. use Hitso\Bundle\FormBundle\Form\Type\ImageType;
  24. use Juki\Bundle\AppBundle\Entity\Dictionary\Device;
  25. use Juki\Bundle\AppBundle\Entity\Dictionary\Feature;
  26. use Juki\Bundle\AppBundle\Entity\Dictionary\ProductAccessory;
  27. use Juki\Bundle\AppBundle\Entity\Dictionary\Shop;
  28. use Juki\Bundle\AppBundle\Event\ProductDisplayEvent;
  29. use Symfony\Bridge\Doctrine\Form\Type\EntityType;
  30. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  31. use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  32. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  33. use Symfony\Component\Form\Extension\Core\Type\TextareaType;
  34. use Symfony\Component\Form\Extension\Core\Type\TextType;
  35. use Symfony\Component\Form\FormEvents;
  36. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  37. use Symfony\Component\Validator\Constraints\Callback;
  38. use Symfony\Component\Validator\Constraints\Count;
  39. use Symfony\Component\Validator\Constraints\Length;
  40. use Symfony\Component\Validator\Constraints\NotNull;
  41. use Symfony\Component\Validator\Constraints\Url;
  42. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  43. final class ProductSubscriber implements EventSubscriberInterface
  44. {
  45.     /**
  46.      * @var EntityManagerInterface
  47.      */
  48.     private $entityManager;
  49.     /**
  50.      * @var RequestHelper
  51.      */
  52.     private $requestHelper;
  53.     /**
  54.      * ProductSubscriber constructor.
  55.      *
  56.      * @param EntityManagerInterface $entityManager
  57.      * @param RequestHelper $requestHelper
  58.      */
  59.     public function __construct(EntityManagerInterface $entityManagerRequestHelper $requestHelper)
  60.     {
  61.         $this->entityManager $entityManager;
  62.         $this->requestHelper $requestHelper;
  63.     }
  64.     public static function getSubscribedEvents()
  65.     {
  66.         return [
  67.             'product.pre_create' => 'onPreCreate',
  68.             'product_type.form_init' => 'onProductFormInit',
  69.             'product.display' => 'onProductDisplay',
  70.             FormEvents::PRE_SUBMIT => 'onProductFormSubmit',
  71.         ];
  72.     }
  73.     public function onPreCreate(EntityEvent $event): void
  74.     {
  75.         $product $event->getEntity();
  76.         if ($product instanceof Product) {
  77.             $categories $product->getCategories();
  78.             if ($categories) {
  79.                 $category $categories[0];
  80.                 if ($category) {
  81.                     $position 0;
  82.                     $result $this->entityManager->getRepository(Product::class)
  83.                         ->createQueryBuilder('p')
  84.                         ->select('MAX(p.position) AS max_position')
  85.                         ->innerJoin('p.categories''c')
  86.                         ->where('c.id = :category')
  87.                         ->setParameter('category'$category->getId())
  88.                         ->orderBy('p.id''asc')
  89.                         ->getQuery()
  90.                         ->getSingleResult();
  91.                     $product->setPosition((int)$result['max_position'] + 1);
  92.                 }
  93.             }
  94.         }
  95.     }
  96.     public function onProductFormSubmit(FormEvent $event)
  97.     {
  98.         /** @var Product $product */
  99.         $product $event->getBuilder()->getData();
  100.         $form $event->getBuilder()->getForm();
  101.         //if ($product->getShopType() === Shop::TYPE_ISM) {
  102.         //    $product->setShops([Shop::ISM_SYMBOL]);
  103.         //    $event->getBuilder()->get('shops')->setData([Shop::ISM_SYMBOL]);
  104.         //}
  105.     }
  106.     public function onProductFormInit(FormEvent $event)
  107.     {
  108.         $builder $event->getBuilder();
  109.         $options $builder->getOptions();
  110.         $product $builder->getData();
  111.         $builder->add(
  112.             'shopType',
  113.             ChoiceType::class,
  114.             [
  115.                 'label' => 'Typ',
  116.                 'required' => true,
  117.                 'choices' => [
  118.                     'ISM' => Shop::TYPE_ISM,
  119.                     'HSM' => Shop::TYPE_HSM,
  120.                 ],
  121.                 'position' => ['before' => 'name'],
  122. //                'source'       => $builder->get('series')->getName(),
  123. //                'form'         => $builder->getForm()->getName(),
  124. //                'condition'    => new Equals(Shop::TYPE_HSM),
  125.             ]
  126.         );
  127.         $choices = [];
  128.         $result $this->entityManager->getRepository(Shop::class)->findBy([], ['name' => 'ASC']);
  129.         foreach ($result as $shop) {
  130.             $choices[$shop->getName() . ' - ' $shop->getShopNameByType()] = $shop->getSymbol();
  131.         }
  132.         //if ($builder->get('shopType')->getData() === Shop::TYPE_ISM) {
  133.         //  $product->setShops([Shop::ISM_SYMBOL]);
  134.         //}
  135.         $builder->add(
  136.             'shops',
  137.             ChoiceType::class,
  138.             [
  139.                 'label' => 'Sklepy',
  140.                 'required' => true,
  141.                 'translation_domain' => 'admin',
  142.                 'multiple' => true,
  143.                 'expanded' => true,
  144.                 'choices' => $choices,
  145.                 'empty_data' => Shop::ISM_SYMBOL,
  146.                 'position' => ['after' => 'shopType'],
  147.                 'constraints' => [
  148.                     new Count([
  149.                         'min' => 1,
  150.                         'minMessage' => 'Musisz wybrać przynajmniej 1 sklep',
  151.                     ]),
  152.                 ],
  153.                 /*
  154.                 'dependencies' => [
  155.                     new Show([
  156.                         'source' => $builder->get('shopType')->getName(),
  157.                         'form' => $builder->getForm()->getName(),
  158.                         'condition' => new Equals(Shop::TYPE_HSM),
  159.                     ]),
  160.                 ],
  161.                 */
  162.             ]
  163.         );
  164. //        if ($builder->get('shopType')->getData() === Shop::TYPE_ISM) {
  165. //            $builder->get('shops')->setData([Shop::ISM_SYMBOL]);
  166. //        }
  167.         $builder->add(
  168.             'excerpt',
  169.             TextareaType::class,
  170.             [
  171.                 'label' => 'Zajawka',
  172.                 'required' => false,
  173.                 'empty_data' => '',
  174.                 'position' => ['before' => 'shortDescription'],
  175.                 'constraints' => [new Length(['max' => 255])],
  176.             ]
  177.         );
  178.         $builder->add(
  179.             'series',
  180.             CheckboxType::class,
  181.             [
  182.                 'label' => 'Produkt jest serią',
  183.                 'required' => false,
  184.                 'position' => ['after' => 'standardAccessories'],
  185.                 'dependencies' => [],
  186.             ]
  187.         );
  188.         $builder->add(
  189.             'seriesProducts',
  190.             EntityType::class,
  191.             [
  192.                 'class' => Product::class,
  193.                 'label' => 'Produkty w serii',
  194.                 'choice_label' => 'name',
  195.                 'multiple' => true,
  196.                 'expanded' => false,
  197.                 'required' => false,
  198.                 'attr' => ['class' => 'select2-custom'],
  199.                 'position' => ['after' => 'series'],
  200.                 'dependencies' => [
  201.                     new Show([
  202.                         'source' => $builder->get('series')->getName(),
  203.                         'form' => $builder->getForm()->getName(),
  204.                         'condition' => new Equals(1),
  205.                     ]),
  206.                 ],
  207.                 'constraints' => [
  208.                     new Callback([
  209.                         'callback' => function ($objectExecutionContextInterface $context) {
  210.                             $series $context->getRoot()->get('series')->getData();
  211.                             if ((bool)$series === true && $object->count() === 0) {
  212.                                 $context->buildViolation('Musisz wybrać minimum 1 produkt w serii.')
  213.                                     ->atPath('seriesProducts')
  214.                                     ->addViolation();
  215.                             }
  216.                         },
  217.                     ]),
  218.                 ],
  219.             ]
  220.         );
  221.         $builder->add(
  222.             'devices',
  223.             EntityType::class,
  224.             [
  225.                 'class' => Device::class,
  226.                 'label' => 'Powiązane dodatki (ISM only)',
  227.                 'choice_label' => 'model',
  228.                 'multiple' => true,
  229.                 'expanded' => false,
  230.                 'required' => false,
  231.                 'attr' => ['class' => 'select2-custom'],
  232.                 'position' => ['after' => 'photos'],
  233.                 'dependencies' => [
  234.                     new Show([
  235.                         'source' => $builder->get('shopType')->getName(),
  236.                         'form' => $builder->getForm()->getName(),
  237.                         'condition' => new Equals(Shop::TYPE_ISM),
  238.                     ]),
  239.                 ],
  240.             ]
  241.         );
  242.         $builder->add(
  243.             'features',
  244.             EntityType::class,
  245.             [
  246.                 'class' => Feature::class,
  247.                 'label' => 'Powiązane funkcje',
  248.                 'choice_label' => 'cmsName',
  249.                 'multiple' => true,
  250.                 'expanded' => false,
  251.                 'required' => false,
  252.                 'attr' => ['class' => 'select2-custom'],
  253.                 'position' => ['after' => 'devices'],
  254.             ]
  255.         );
  256.         $builder->add(
  257.             'relatedProducts',
  258.             EntityType::class,
  259.             [
  260.                 'class' => Product::class,
  261.                 'label' => 'Powiązane produkty',
  262.                 'choice_label' => 'name',
  263.                 'multiple' => true,
  264.                 'expanded' => false,
  265.                 'required' => false,
  266.                 'attr' => ['class' => 'select2-custom'],
  267.                 'position' => ['after' => 'photos'],
  268.             ]
  269.         );
  270.         $builder->add(
  271.             'relatedArticles',
  272.             EntityType::class,
  273.             [
  274.                 'class' => Article::class,
  275.                 'label' => 'Powiązane artykuły',
  276.                 'choice_label' => 'title',
  277.                 'multiple' => true,
  278.                 'expanded' => false,
  279.                 'required' => false,
  280.                 'attr' => ['class' => 'select2-custom'],
  281.                 'position' => ['after' => 'relatedProducts'],
  282.             ]
  283.         );
  284.         $builder->add(
  285.             'subTitle',
  286.             TextType::class,
  287.             [
  288.                 'label' => 'Podtytuł (ISM only)',
  289.                 'required' => false,
  290.                 'empty_data' => '',
  291.                 'position' => ['after' => 'name'],
  292.                 'constraints' => [new Length(['max' => 255])],
  293.                 'dependencies' => [
  294.                     new Show([
  295.                         'source' => $builder->get('shopType')->getName(),
  296.                         'form' => $builder->getForm()->getName(),
  297.                         'condition' => new Equals(Shop::TYPE_ISM),
  298.                     ]),
  299.                 ],
  300.             ]
  301.         );
  302.         $builder->add(
  303.             'label',
  304.             ChoiceType::class,
  305.             [
  306.                 'label' => 'Plakietka (HSM only)',
  307.                 'required' => false,
  308.                 'empty_data' => '',
  309.                 'choices' => [
  310.                     '---' => '',
  311.                     'best for start (zielona)' => 'best_for_start',
  312.                     'advanced (żółta)' => 'advanced',
  313.                     'best deal (niebieska)' => 'best_deal',
  314.                 ],
  315.                 'position' => ['after' => 'subTitle'],
  316.                 'dependencies' => [
  317.                     new Show([
  318.                         'source' => $builder->get('shopType')->getName(),
  319.                         'form' => $builder->getForm()->getName(),
  320.                         'condition' => new Equals(Shop::TYPE_HSM),
  321.                     ]),
  322.                 ],
  323.             ]
  324.         );
  325.         $builder->add(
  326.             'layoutColour',
  327.             ChoiceType::class,
  328.             [
  329.                 'label' => 'Wariant kolorystyczny (HSM only)',
  330.                 'required' => false,
  331.                 'empty_data' => '',
  332.                 'choices' => [
  333.                     '---' => '',
  334.                     'błękitny' => 'blue',
  335.                     'żółty' => 'yellow',
  336.                     'różowy' => 'pink',
  337.                 ],
  338.                 'position' => ['after' => 'label'],
  339.                 'dependencies' => [
  340.                     new Show([
  341.                         'source' => $builder->get('shopType')->getName(),
  342.                         'form' => $builder->getForm()->getName(),
  343.                         'condition' => new Equals(Shop::TYPE_HSM),
  344.                     ]),
  345.                 ],
  346.             ]
  347.         );
  348.         $builder->add(
  349.             'accessories',
  350.             EntityType::class,
  351.             [
  352.                 'class' => ProductAccessory::class,
  353.                 'label' => 'Powiązane akcesoria (HSM only)',
  354.                 'choice_label' => 'cmsName',
  355.                 'multiple' => true,
  356.                 'expanded' => false,
  357.                 'required' => false,
  358.                 'attr' => ['class' => 'select2-custom'],
  359.                 'position' => ['after' => 'devices'],
  360.                 'dependencies' => [
  361.                     new Show([
  362.                         'source' => $builder->get('shopType')->getName(),
  363.                         'form' => $builder->getForm()->getName(),
  364.                         'condition' => new Equals(Shop::TYPE_HSM),
  365.                     ]),
  366.                 ],
  367.             ]
  368.         );
  369.         $builder->add(
  370.             'featuresHsm',
  371.             EntityType::class,
  372.             [
  373.                 'class' => FeatureHsm::class,
  374.                 'label' => 'Powiązane cechy (HSM only)',
  375.                 'choice_label' => 'name',
  376.                 'multiple' => true,
  377.                 'expanded' => false,
  378.                 'required' => false,
  379.                 'attr' => ['class' => 'select2-custom'],
  380.                 'position' => ['after' => 'devices'],
  381.                 'dependencies' => [
  382.                     new Show([
  383.                         'source' => $builder->get('shopType')->getName(),
  384.                         'form' => $builder->getForm()->getName(),
  385.                         'condition' => new Equals(Shop::TYPE_HSM),
  386.                     ]),
  387.                 ],
  388.             ]
  389.         );
  390.         $builder->add(
  391.             'slogan',
  392.             TextType::class,
  393.             [
  394.                 'label' => 'Pogrubiony slogan (ISM only)',
  395.                 'required' => false,
  396.                 'empty_data' => '',
  397.                 'position' => ['after' => 'subTitle'],
  398.                 'dependencies' => [
  399.                     new Show([
  400.                         'source' => $builder->get('shopType')->getName(),
  401.                         'form' => $builder->getForm()->getName(),
  402.                         'condition' => new Equals(Shop::TYPE_ISM),
  403.                     ]),
  404.                 ],
  405.             ]
  406.         );
  407.         $builder->add(
  408.             'extraProperties',
  409.             TableCollectionType::class, [
  410.             'label' => 'Cechy produktu (ISM only)',
  411.             'entry_type' => KeyValueUnitType::class,
  412.             'required' => false,
  413.             'allow_add' => true,
  414.             'allow_delete' => true,
  415.             'row_attr' => [
  416.                 'class' => 'fb-table',
  417.             ],
  418.             'position' => ['before' => 'seoMetadata'],
  419.             'disabled' => $options['disabled'],
  420.             'dependencies' => [
  421.                 new Show([
  422.                     'source' => $builder->get('shopType')->getName(),
  423.                     'form' => $builder->getForm()->getName(),
  424.                     'condition' => new Equals(Shop::TYPE_ISM),
  425.                 ]),
  426.             ],
  427.         ]);
  428.         $builder->add(
  429.             'videos',
  430.             RepeatableTextCollectionType::class,
  431.             [
  432.                 'label' => 'Linki video',
  433.                 'entry_type' => RepeatableTextType::class,
  434.                 'allow_add' => true,
  435.                 'allow_delete' => true,
  436.                 'empty_data' => [''],
  437.                 'entry_options' => [
  438.                     'constraints' => [
  439.                         new Url(['relativeProtocol' => false]),
  440.                     ],
  441.                 ],
  442.                 'position' => ['after' => 'extraProperties'],
  443.             ]
  444.         );
  445.         $builder->add(
  446.             'standardAccessories',
  447.             RepeatableTextCollectionType::class,
  448.             [
  449.                 'label' => 'Standardowe akcesoria (HSM only)',
  450.                 'entry_type' => RepeatableTextType::class,
  451.                 'allow_add' => true,
  452.                 'allow_delete' => true,
  453.                 'empty_data' => [''],
  454.                 'entry_options' => [
  455.                     'constraints' => [
  456.                         new NotNull(),
  457.                     ],
  458.                 ],
  459.                 'position' => ['after' => 'description'],
  460.                 'dependencies' => [
  461.                     new Show([
  462.                         'source' => $builder->get('shopType')->getName(),
  463.                         'form' => $builder->getForm()->getName(),
  464.                         'condition' => new Equals(Shop::TYPE_HSM),
  465.                     ]),
  466.                 ],
  467.             ]
  468.         );
  469.         $builder->add(
  470.             'published',
  471.             CheckboxType::class,
  472.             [
  473.                 'label' => 'Opublikowany',
  474.                 'required' => false,
  475.                 'position' => ['after' => 'slogan'],
  476.                 'disabled' => $options['disabled'],
  477.             ]
  478.         );
  479.         $builder->remove('brand');
  480.         $builder->remove('taxRate');
  481.         $builder->remove('netAmount');
  482.         $builder->remove('currencyCode');
  483.         $builder->remove('enabled');
  484.         $builder->setRequired(false);
  485.         $builder->remove('photo');
  486.         $builder->add(
  487.             'photo',
  488.             ImageType::class,
  489.             [
  490.                 'label' => 'Zdjęcie',
  491.                 'config_name' => 'product_image',
  492.                 'required' => false,
  493.                 'hotspot_enabled' => $product->getId() > 0,
  494.                 'hotspot_resource_name' => 'product',
  495.                 'hotspot_resource_id' => $product->getId(),
  496.                 'position' => ['after' => 'seriesProducts'],
  497.                 'constraints' => [
  498.                     new Callback([
  499.                         'callback' => function ($objectExecutionContextInterface $context) {
  500.                             $series $context->getRoot()->get('series')->getData();
  501.                             if ((bool)$series === true && is_null($object)) {
  502.                                 $context->buildViolation('Zdjęcie jest wymagane, gdyż produkt jest serią')
  503.                                     ->addViolation();
  504.                             }
  505.                         },
  506.                     ]),
  507.                 ],
  508.             ]
  509.         );
  510.         $builder
  511.             ->remove('photos')
  512.             ->add(
  513.                 'photos',
  514.                 ImageType::class,
  515.                 [
  516.                     'label' => 'Zdjęcia dodatkowe (ISM only)',
  517.                     'config_name' => 'product_images',
  518.                     'multiple' => true,
  519.                     'disabled' => $options['disabled'],
  520.                     'hotspot_enabled' => $product->getId() > 0,
  521.                     'hotspot_resource_name' => 'product',
  522.                     'hotspot_resource_id' => $product->getId(),
  523.                     'position' => ['after' => 'photo'],
  524.                     'dependencies' => [
  525.                         new Show([
  526.                             'source' => $builder->get('shopType')->getName(),
  527.                             'form' => $builder->getForm()->getName(),
  528.                             'condition' => new Equals(Shop::TYPE_ISM),
  529.                         ]),
  530.                     ],
  531.                 ]
  532.             );
  533.         $builder
  534.             ->remove('category')
  535.             ->add(
  536.                 'category',
  537.                 EntityType::class,
  538.                 [
  539.                     'class' => Category::class,
  540.                     'label' => 'Kategoria główna',
  541.                     'choice_label' => 'pathAsString',
  542.                     'multiple' => false,
  543.                     'expanded' => false,
  544.                     'attr' => ['class' => 'select2-custom'],
  545.                     'query_builder' => function (EntityRepository $repo) {
  546.                         return $repo->createQueryBuilder('c')->andWhere('c.parent IS NULL');
  547.                     },
  548.                     'position' => ['before' => 'categories'],
  549.                 ]
  550.             );
  551.         $builder
  552.             ->remove('categories')
  553.             ->add(
  554.                 'categories',
  555.                 EntityType::class,
  556.                 [
  557.                     'class' => Category::class,
  558.                     'label' => 'Kategorie dodatkowe (ISM only)',
  559.                     'choice_label' => 'name',
  560.                     'required' => false,
  561.                     'multiple' => true,
  562.                     'expanded' => false,
  563.                     'attr' => ['class' => 'select2-custom'],
  564. //                    'choices'      => $product->getCategory()->getChildren(),
  565.                     'query_builder' => function (\Doctrine\ORM\EntityRepository $repo) {
  566.                         return $repo->createQueryBuilder('c')->andWhere('c.parent IS NOT NULL');
  567.                     },
  568.                     'position' => ['before' => 'extraProperties'],
  569.                     'dependencies' => [
  570.                         new Show([
  571.                             'source' => $builder->get('shopType')->getName(),
  572.                             'form' => $builder->getForm()->getName(),
  573.                             'condition' => new Equals(Shop::TYPE_ISM),
  574.                         ]),
  575.                     ],
  576.                 ]
  577.             );
  578.         if ($product->getId()) {
  579.             $builder->get('actions')->add('edit_files'LinkType::class, [
  580.                 'label' => 'Edytuj pliki',
  581.                 'position' => ['after' => 'edit_attributes'],
  582.                 'route' => 'juki_product_attachment_admin_index',
  583.                 'route_params' => ['id' => $product->getId()],
  584.                 'target' => '_blank',
  585.                 'attr' => [
  586.                     'class' => 'btn-right',
  587.                 ],
  588.             ]);
  589.         }
  590.         if ($builder->get('shopType')->getData() === Shop::TYPE_ISM) {
  591.             $product->setShops([Shop::ISM_SYMBOL]);
  592.         }
  593.     }
  594.     public function onProductDisplay(ProductDisplayEvent $event)
  595.     {
  596.         $product $event->getProduct();
  597.         if (false === $this->requestHelper->getSessionAttribute('product_display_' $product->getId(), false)) {
  598.             $product->setDisplayCounter($product->getDisplayCounter() + 1);
  599.             $this->entityManager->persist($product);
  600.             $this->entityManager->flush();
  601.             $this->requestHelper->setSessionAttribute('product_display_' $product->getId(), true);
  602.         }
  603.         // if ($product->getShopType() === Shop::TYPE_ISM) {
  604.         //     $product->setShops([Shop::ISM_SYMBOL]);
  605.         // }
  606.     }
  607. }