src/AppBundle/Component/PageBuilder/PageBuilder.php line 48

Open in your IDE?
  1. <?php
  2. namespace AppBundle\Component\PageBuilder;
  3. use AppBundle\Component\PageBuilder\Event\BlockLoadedEvent;
  4. use AppBundle\Component\PageBuilder\Event\BlockLoadingEvent;
  5. use AppBundle\Component\PageBuilder\Event\FormInitEvent;
  6. use AppBundle\Component\PageBuilder\Event\FormLoadedEvent;
  7. use AppBundle\Component\PageBuilder\Event\FormLoadingEvent;
  8. use AppBundle\Component\PageBuilder\Event\OnCommitEvent;
  9. use AppBundle\Component\PageBuilder\Event\PreValidateEvent;
  10. use AppBundle\Component\PageBuilder\Type\BlockInterface;
  11. use Symfony\Component\DependencyInjection\ContainerInterface;
  12. use Symfony\Component\EventDispatcher\EventDispatcher;
  13. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\Validator\ConstraintViolationList;
  16. class PageBuilder
  17. {
  18.     public $container;
  19.     protected $entity null;
  20.     protected $blocks = [];
  21.     protected $builder;
  22.     protected $engine;
  23.     protected $autoincrement 0;
  24.     protected $blockTypes = [];
  25.     protected $data = [];
  26.     protected $errors = [];
  27.     public $translator;
  28.     protected $oldInputs = [];
  29.     protected $form null;
  30.     public $themeDirectory '::page-builder/themes';
  31.     public $theme 'default';
  32.     public $mediaUsageTracker null;
  33.     public $dispatcher null;
  34.     public $sharedParams = [];
  35.     public $validationCallback null;
  36.     protected $validationData = [];
  37.     public function __construct(ContainerInterface $container)
  38.     {
  39.         $this->container $container;
  40.         $this->engine $this->container->get('templating');
  41.         $this->dispatcher = new EventDispatcher();
  42.         $router $container->get('router');
  43.         $mediaLibraryExists = (null !== $router->getRouteCollection()->get('admin_media_library_item'));
  44.         if($mediaLibraryExists){
  45.             $this->setSharedParam('media_library_enabled'true);
  46.         } else {
  47.             $this->setSharedParam('media_library_enabled'false);
  48.         }
  49.     }
  50.     public function duplicate()
  51.     {
  52.         $clone = clone $this;
  53.         $blocks1 $this->getBlocks();
  54.         $blocks2 = [];
  55.         foreach($blocks1 as $block){
  56.             $blocks2[] = clone $block;
  57.         }
  58.         $clone->setBlocks($blocks2);
  59.         return $clone;
  60.     }
  61.     public function initOldInputs()
  62.     {
  63.         if(is_null($this->oldInputs)) {
  64.             $request $this->container->get('request_stack')->getCurrentRequest();
  65.             $session $request->getSession();
  66.             $this->oldInputs $session->getFlashBag()->get('_form_inputs', []);
  67.         }
  68.     }
  69.     public function loadForm($class$params = [])
  70.     {
  71.         $form = new $class($params);
  72.         $this->dispatcher
  73.             ->dispatch(FormLoadingEvent::NAME, new FormInitEvent($form));
  74.         $initService $this->getSharedParam('form_initializer'null);
  75.         $container $this->getContainer();
  76.         if($container->has($initService)){
  77.             $initializer $container->get($initService);
  78.             $initializer->init($form$params);
  79.         }
  80.         $form->registerBlockData($this$params);
  81.         $this->dispatcher
  82.             ->dispatch(FormLoadingEvent::NAME, new FormLoadingEvent($form));
  83.         foreach($form->getRegisteredBlockDataList() as $name => $blockData){
  84.             $block $this->createBlock($blockData['class'], $name$blockData['options']);
  85.             $this->dispatcher->dispatch(BlockLoadingEvent::NAME, new BlockLoadingEvent($block));
  86.             $this->addBlock($block);
  87.             $this->dispatcher->dispatch(BlockLoadedEvent::NAME, new BlockLoadedEvent($block));
  88.         }
  89.         $this->form $form;
  90.         $this->dispatcher
  91.             ->dispatch(FormLoadedEvent::NAME, new FormLoadedEvent($form));
  92.         return $this;
  93.     }
  94.     public function getForm()
  95.     {
  96.         return $this->form;
  97.     }
  98.     public function getBlockType($name)
  99.     {
  100.         if(isset($this->blockTypes[$name])){
  101.             return $this->blockTypes[$name];
  102.         } else {
  103.             return null;
  104.         }
  105.     }
  106.     public function createBlock($class$name$options=[]){
  107.         /** @var BlockInterface $block */
  108.         $block = new $class($name$options);
  109.         $block->setBuilder($this);
  110.         $block->setEngine($this->engine);
  111.         $block->init();
  112.         return $block;
  113.     }
  114.     // 初期表示するブロックを追加
  115.     public function addBlock($block){
  116.         $block->setBuilder($this);
  117.         $block->setEngine($this->engine);
  118.         $this->autoincrement++;
  119.         $block->setId($this->autoincrement);
  120.         $this->blocks[$block->getId()] = $block;
  121.         return $this;
  122.     }
  123.     /**
  124.      * 条件に一致するブロックを取得する
  125.      *
  126.      * @param array $conditions
  127.      * @return array
  128.      */
  129.     public function getBlocks($conditions = [])
  130.     {
  131.         $blocks $this->blocks;
  132.         $list = [];
  133.         foreach($blocks as $block){
  134.             $show true;
  135.             foreach($conditions as $key => $value){
  136.                 if($key === 'form_display_section' && $block->getFormDisplaySection() !== $value){
  137.                     $show false;
  138.                     break;
  139.                 }
  140.                 if($key === 'content_display_section' && $block->getContentDisplaySection() !== $value){
  141.                     $show false;
  142.                     break;
  143.                 }
  144.             }
  145.             if($show){
  146.                 $list[$block->getName()] = $block;
  147.             }
  148.         }
  149.         return $list;
  150.     }
  151.     /**
  152.      * ブロックをセットする
  153.      *
  154.      * @param $blocks
  155.      */
  156.     public function setBlocks($blocks)
  157.     {
  158.         $this->blocks $blocks;
  159.     }
  160.     /**
  161.      * 名前からブロックを探す
  162.      *
  163.      * @param $name
  164.      * @return null
  165.      */
  166.     public function findBlockByName($name)
  167.     {
  168.         $blocks $this->getBlocks();
  169.         foreach($blocks as $block){
  170.             if($name === $block->getName()){
  171.                 return $block;
  172.             }
  173.             // グループであれば子も走査する
  174.             if($block->isGroup){
  175.                 foreach($block->getChildren() as $child){
  176.                     if($name === $child->getName()){
  177.                         return $child;
  178.                     }
  179.                 }
  180.             }
  181.         }
  182.         return null;
  183.     }
  184.     public function findBlocksByFormDisplaySection($section)
  185.     {
  186.     }
  187.     public function getExtraBlocks(){
  188.         return $this->extraBlocks;
  189.     }
  190.     public function getBlockList()
  191.     {
  192.         $list = [];
  193.         foreach($this->getBlocks() as $block){
  194.             $list[$block->getName()] = $block;
  195.         }
  196.         return $list;
  197.     }
  198.     public function getEngine()
  199.     {
  200.         return $this->engine;
  201.     }
  202.     public function renderForm()
  203.     {
  204.         $content '';
  205.         foreach($this->blocks as $block){
  206.             $content .= $block->renderForm();
  207.         }
  208.         return $content;
  209.     }
  210.     public function renderContent()
  211.     {
  212.         $content '';
  213.         foreach($this->blocks as $block){
  214.             $content .= $block->renderContent();
  215.         }
  216.         return $content;
  217.     }
  218.     public function validate($inputs)
  219.     {
  220.         $validator $this->container->get('validator');
  221.         $inputs $this->optimizeInputs($inputs);
  222.         $form $this->getForm();
  223.         $this->dispatcher->dispatch(PreValidateEvent::NAME, new PreValidateEvent($form$inputs));
  224.         $blockList $this->getBlockList();
  225.         $this->errors = [];
  226.         $violations = [];
  227.         $blockValues = [];
  228.         $blocks = [];
  229.         foreach ($blockList as $name => $block) {
  230.             $blocks[$block->getNamePrefix() . $name] = $block;
  231.         }
  232.         $blockList $blocks;
  233.         foreach($blockList as $name => $block){
  234.             $blockValues[$name] = [];
  235.         }
  236.         foreach($inputs['blocks'] as $name => $values){
  237.             $parts preg_split('/__/'$name, -1, \PREG_SPLIT_NO_EMPTY);
  238.             if(count($parts) === 2){
  239.                 $blockName $parts[0];
  240.             } else {
  241.                 $blockName $name;
  242.             }
  243.             if(!isset($blockList[$blockName])){
  244.                 continue;
  245.             }
  246.             $blockValues[$name] = $values;
  247.         }
  248.         $this->setValidationData($inputs);
  249.         foreach($blockValues as $name => $values)
  250.         {
  251.             if(isset($blockList[$name])) {
  252.                 $block $blockList[$name];
  253.             } else {
  254.                 continue;
  255.             }
  256.             $result $block->validate($values);
  257.             if($result){
  258.                 $violations$name ] = $result;
  259.             }
  260.         }
  261.         if(!is_null($this->validationCallback)){
  262.             $violations2 $this->validationCallback->call($this$blockValues);
  263.             if(is_array($violations2)){
  264.                 $violations array_merge($violations$violations2);
  265.             }
  266.         }
  267.         $this->setValidationData([]);
  268.         $this->errors $violations;
  269.         return $violations;
  270.     }
  271.     /**
  272.      * 送信された値を扱いやすい形に整形する
  273.      *
  274.      * @param $inputs
  275.      * @return array
  276.      */
  277.     public function optimizeInputs($inputs){
  278.         $result = [
  279.             'fields' => [],
  280.             'blocks' => [],
  281.         ];
  282.         foreach($inputs as $key => $value){
  283.             if(preg_match('/^__block_(.+?)$/'$key$matches)){
  284.                 $parts preg_split('/__/'$matches[1], -1, \PREG_SPLIT_NO_EMPTY);
  285.                 if(count($parts) === 3){
  286.                     $section $parts[0];
  287.                     $name $parts[1];
  288.                     $uniqueCode $parts[2];
  289.                     if(!isset($result['blocks'][$section])) {
  290.                         $result['blocks'][$section] = [];
  291.                     }
  292.                     $result['blocks'][$section][$section '__' $name '__' $uniqueCode] = $value;
  293.                 } else {
  294.                     $name $matches[1];
  295.                     $result['blocks'][$name] = $value;
  296.                 }
  297.             } else {
  298.                 $result['fields'][$key] = $value;
  299.             }
  300.         }
  301.         return $result;
  302.     }
  303.     public function hasErrors($field){
  304.         $this->initOldInputs();
  305.         return (!empty($this->errors[$field]));
  306.     }
  307.     public function getErrors($field=null)
  308.     {
  309.         if(is_null($field)){
  310.             return $this->errors;
  311.         } else if( isset($this->errors[$field]) ){
  312.             return $this->errors[$field];
  313.         } else {
  314.             return [];
  315.         }
  316.     }
  317.     /**
  318.      * @return int
  319.      */
  320.     public function countAllErrors()
  321.     {
  322.         $errors $this->errors;
  323.         $func = function($errors$count 0) use(&$func){
  324.             foreach($errors as $value){
  325.                 if(is_array($value)){
  326.                     $count $func($value$count);
  327.                 } elseif($value instanceof ConstraintViolationList) {
  328.                     $count += $value->count();
  329.                 }
  330.             }
  331.             return $count;
  332.         };
  333.         $count $func($errors);
  334.         return $count;
  335.     }
  336.     // 入力値を処理する
  337.     public function handleRequest(Request $request)
  338.     {
  339.         $inputs $request->request->all();
  340.         $files $this->optimizeInputs($request->files->all());
  341.         $session $request->getSession();
  342.         $this->oldInputs $inputs;
  343.         $flashBag $session->getFlashBag();
  344.         $flashBag->set('_form_inputs'$inputs);
  345.         $oldInputs $this->optimizeInputs($this->oldInputs);
  346.         foreach($this->blocks as $block){
  347.             // 変更前の値を退避
  348.             $block->stashValues();
  349.             $blockName $block->getNamePrefix() . $block->getName();
  350.             if(isset($oldInputs['blocks'][$blockName])){
  351.                 $block->applySubmittedValues($oldInputs['blocks'][$blockName]);
  352.             } else {
  353.                 $block->resetValues();
  354.             }
  355.             if(isset($files['blocks'][$blockName])){
  356.                 $block->applySubmittedFiles($files['blocks'][$blockName]);
  357.             } else {
  358.                 $block->resetFiles();
  359.             }
  360.         }
  361.         // 入力値を検証
  362.         $violations $this->validate($inputs);
  363.         return $violations;
  364.     }
  365.     /**
  366.      * 内容を確定させる
  367.      * @param string $mode
  368.      */
  369.     public function commit($mode OnCommitEvent::MODE_DEFAULT)
  370.     {
  371.         $this->dispatcher->dispatch(OnCommitEvent::NAME, new OnCommitEvent($this$mode));
  372.     }
  373.     /**
  374.      * @return array
  375.      */
  376.     public function getData()
  377.     {
  378.         $data = [];
  379.         foreach($this->blocks as $block){
  380.             $blockName $block->getNamePrefix() . $block->getName();
  381.             if(!isset($data[$blockName])) $data[$blockName] = array();
  382.             // 返却値を展開する場合
  383.             if($block->isGroup){
  384.                 $dataList $block->getData();
  385.                 if(!empty($dataList)){
  386.                     foreach($dataList as $fieldName => $value){
  387.                         $data[$fieldName] = $value;
  388.                     }
  389.                 }
  390.             } else {
  391.                 $data[$blockName] = $block->getData();
  392.             }
  393.         }
  394.         return $data;
  395.     }
  396.     // データを各ブロックに割り当てる
  397.     public function applyData($values)
  398.     {
  399.         $blockValues = [];
  400.         $blocks $this->getBlocks();
  401.         foreach($values as $field => $value){
  402.             foreach($blocks as $block){
  403.                 $boundFields $block->getBoundFields();
  404.                 if(in_array($field$boundFields)){
  405.                     if($block->isGroup){
  406.                         $blockValues[$block->getName()][$field] = $value;
  407.                     } else {
  408.                         $blockValues[$block->getName()] = $value;
  409.                     }
  410.                 }
  411.             }
  412.         }
  413.         foreach($blockValues as $blockName => $values){
  414.             if(isset($blocks[$blockName])){
  415.                 $blocks[$blockName]->applyValues($values);
  416.             }
  417.         }
  418.         return $this;
  419.     }
  420.     public function readEntityData($entity){
  421.         if(!$entity){
  422.             return $this;
  423.         }
  424.         $class get_class($entity);
  425.         $doctrine $this->container->get('doctrine');
  426.         $em $doctrine->getManager();
  427.         $metadata $em->getClassMetadata($class);
  428.         $data = array();
  429.         foreach($metadata->fieldNames as $field => $property){
  430.             if(is_null($entity->{$property})){
  431.                 $data[$property] = null;
  432.             } else {
  433.                 $data[$property] = $entity->{$property};
  434.             }
  435.         }
  436.         // アソシエーション
  437.         foreach($metadata->associationMappings as $property => $mapping){
  438.             $data[$property] = $entity->{$property};
  439.         }
  440.         $this->applyData($data);
  441.         $this->entity $entity;
  442.         return $this;
  443.     }
  444.     public function generateUniqueCode()
  445.     {
  446.         return sha1(uniqid(mt_rand(), true));
  447.     }
  448.     /**
  449.      * @return string
  450.      */
  451.     public function getThemeDirectory()
  452.     {
  453.         return $this->themeDirectory;
  454.     }
  455.     /**
  456.      * @param string $themeDirectory
  457.      * @return $this
  458.      */
  459.     public function setThemeDirectory($themeDirectory)
  460.     {
  461.         $this->themeDirectory $themeDirectory;
  462.         return $this;
  463.     }
  464.     public function getTheme()
  465.     {
  466.         return $this->theme;
  467.     }
  468.     public function setTheme($theme)
  469.     {
  470.         $this->theme $theme;
  471.     }
  472.     public function initMediaUsageTracker()
  473.     {
  474.         if($this->container->has('app.media_usage_tracker')){
  475.             $this->mediaUsageTracker $this->container->get('app.media_usage_tracker')->create();
  476.         } else {
  477.             $this->mediaUsageTracker null;
  478.         }
  479.         return $this;
  480.     }
  481.     public function getMediaUsageTracker()
  482.     {
  483.         return $this->mediaUsageTracker;
  484.     }
  485.     public function saveMediaUsages($entityName$contentId)
  486.     {
  487.         $tracker $this->getMediaUsageTracker();
  488.         if($tracker){
  489.             $tracker->save($entityName$contentId);
  490.         }
  491.         return $this;
  492.     }
  493.     /**
  494.      * @param $eventName
  495.      * @param $listener
  496.      * @param int $priority
  497.      * @return $this
  498.      */
  499.     public function addEventListener($eventName$listener$priority 0)
  500.     {
  501.         $this->dispatcher->addListener($eventName$listener$priority);
  502.         return $this;
  503.     }
  504.     /**
  505.      * @param $eventName
  506.      * @param $listener
  507.      */
  508.     public function removeEventListener($eventName$listener)
  509.     {
  510.         $this->dispatcher->removeListener($eventName$listener);
  511.     }
  512.     /**
  513.      * @param EventSubscriberInterface $subscriber
  514.      * @return $this
  515.      */
  516.     public function addEventSubscriber(EventSubscriberInterface $subscriber)
  517.     {
  518.         $this->dispatcher->addSubscriber($subscriber);
  519.         return $this;
  520.     }
  521.     /**
  522.      * @return array
  523.      */
  524.     public function getSharedParams(){
  525.         return $this->sharedParams;
  526.     }
  527.     /**
  528.      * @param $params
  529.      * @return $this
  530.      */
  531.     public function setSharedParams($params){
  532.         $this->sharedParams $params;
  533.         return $this;
  534.     }
  535.     /**
  536.      * @param $name
  537.      * @return bool
  538.      */
  539.     public function hasSharedParam($name)
  540.     {
  541.         return isset($this->sharedParams[$name]);
  542.     }
  543.     /**
  544.      * @param $name
  545.      * @param null $default
  546.      * @return |null
  547.      */
  548.     public function getSharedParam($name$default=null)
  549.     {
  550.         if($this->hasSharedParam($name)){
  551.             return $this->sharedParams[$name];
  552.         } else {
  553.             return $default;
  554.         }
  555.     }
  556.     /**
  557.      * @param $name
  558.      * @param $value
  559.      * @return $this
  560.      */
  561.     public function setSharedParam($name$value)
  562.     {
  563.         $this->sharedParams[$name] = $value;
  564.         return $this;
  565.     }
  566.     /**
  567.      * @return ContainerInterface
  568.      */
  569.     public function getContainer()
  570.     {
  571.         return $this->container;
  572.     }
  573.     /**
  574.      * @return null
  575.      */
  576.     public function getEntity()
  577.     {
  578.         return $this->entity;
  579.     }
  580.     /**
  581.      * 他フィールドを参照するバリデーションのためのデータを取得
  582.      *
  583.      * @return array
  584.      */
  585.     public function getValidationData()
  586.     {
  587.         return $this->validationData;
  588.     }
  589.     /**
  590.      * 他フィールドを参照するバリデーションのためのデータをセット
  591.      *
  592.      * @param $values
  593.      */
  594.     public function setValidationData($values)
  595.     {
  596.         $this->validationData $values;
  597.     }
  598. }