HosipLan

Untitled

Jan 5th, 2012
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 8.49 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4.  * This file is part of the Kdyby (http://www.kdyby.org)
  5.  *
  6.  * Copyright (c) 2008, 2011 Filip Procházka ([email protected])
  7.  *
  8.  * @license http://www.kdyby.org/license
  9.  */
  10.  
  11. namespace Kdyby\Forms\Containers;
  12.  
  13. use Nette;
  14. use Nette\Forms\Container;
  15.  
  16.  
  17.  
  18. /**
  19.  * @author Filip Procházka <[email protected]>
  20.  * @author Jan Tvrdík
  21.  */
  22. class Replicator extends Container
  23. {
  24.  
  25.     /** @var bool */
  26.     public $forceDefault;
  27.  
  28.     /** @var int */
  29.     public $createDefault;
  30.  
  31.     /** @var callback */
  32.     protected $factoryCallback;
  33.  
  34.     /** @var boolean */
  35.     private $submittedBy = FALSE;
  36.  
  37.     /** @var array */
  38.     private $created = array();
  39.  
  40.     /** @var \Nette\Http\IRequest */
  41.     private $httpRequest;
  42.  
  43.  
  44.  
  45.     /**
  46.      * @param callable $factory
  47.      * @param int $createDefault
  48.      * @param bool $forceDefault
  49.      */
  50.     public function __construct($factory, $createDefault = 0, $forceDefault = FALSE)
  51.     {
  52.         parent::__construct();
  53.  
  54.         $this->monitor('Nette\Application\UI\Presenter');
  55.         $this->factoryCallback = callback($factory);
  56.         $this->createDefault = (int)$createDefault;
  57.         $this->forceDefault = $forceDefault;
  58.     }
  59.  
  60.  
  61.  
  62.     /**
  63.      * Magical component factory
  64.      *
  65.      * @param \Nette\ComponentModel\IContainer
  66.      */
  67.     protected function attached($obj)
  68.     {
  69.         parent::attached($obj);
  70.  
  71.         if (!$obj instanceof Nette\Application\UI\Presenter) {
  72.             return;
  73.         }
  74.  
  75.         $this->loadHttpData();
  76.         if ($this->createDefault > 0) {
  77.             if (!$this->getForm()->isSubmitted()) {
  78.                 foreach (range(0, $this->createDefault - 1) as $key) {
  79.                     $this->createComponent($key);
  80.                 }
  81.  
  82.             } elseif ($this->forceDefault) {
  83.                 while ($this->getContainers()->count() < $this->createDefault) {
  84.                     $this->createOne();
  85.                 }
  86.             }
  87.         }
  88.     }
  89.  
  90.  
  91.  
  92.     /**
  93.      * @param boolean $recursive
  94.      * @return \ArrayIterator
  95.      */
  96.     public function getContainers($recursive = FALSE)
  97.     {
  98.         return $this->getComponents($recursive, 'Nette\Forms\Container');
  99.     }
  100.  
  101.  
  102.  
  103.     /**
  104.      * @param boolean $recursive
  105.      * @return \ArrayIterator
  106.      */
  107.     public function getButtons($recursive = FALSE)
  108.     {
  109.         return $this->getComponents($recursive, 'Nette\Forms\ISubmitterControl');
  110.     }
  111.  
  112.  
  113.  
  114.     /**
  115.      * Magical component factory
  116.      *
  117.      * @param string $name
  118.      * @return \Nette\Forms\Container
  119.      */
  120.     protected function createComponent($name)
  121.     {
  122.         $container = $this->createContainer();
  123.         $container->currentGroup = $this->currentGroup;
  124.         $this->addComponent($container, $name, $this->getFirstControlName());
  125.  
  126.         $this->factoryCallback->invoke($container);
  127.  
  128.         return $this->created[$container->name] = $container;
  129.     }
  130.  
  131.  
  132.  
  133.     /**
  134.      * @return string
  135.      */
  136.     private function getFirstControlName()
  137.     {
  138.         $controls = iterator_to_array($this->getComponents(FALSE, 'Nette\Forms\IControl'));
  139.         $firstControl = reset($controls);
  140.         return $firstControl ? $firstControl->name : NULL;
  141.     }
  142.  
  143.  
  144.  
  145.     /**
  146.      * @return \Nette\Forms\Container
  147.      */
  148.     protected function createContainer()
  149.     {
  150.         return new Container;
  151.     }
  152.  
  153.  
  154.  
  155.     /**
  156.      * @return boolean
  157.      */
  158.     public function isSubmittedBy()
  159.     {
  160.         if ($this->submittedBy) {
  161.             return TRUE;
  162.         }
  163.  
  164.         foreach ($this->getButtons(TRUE) as $button) {
  165.             if ($button->isSubmittedBy()) {
  166.                 return $this->submittedBy = TRUE;
  167.             }
  168.         }
  169.  
  170.         return FALSE;
  171.     }
  172.  
  173.  
  174.  
  175.     /**
  176.      * Create new container
  177.      *
  178.      * @param string|int $name
  179.      *
  180.      * @return \Nette\Forms\Container
  181.      */
  182.     public function createOne($name = NULL)
  183.     {
  184.         if ($name === NULL) {
  185.             $names = array_keys($this->getContainers()->getArrayCopy());
  186.             $name = $names ? max($names) + 1 : 0;
  187.         }
  188.  
  189.         // Container is overriden, therefore every request for getComponent($name, FALSE) would return container
  190.         if (isset($this->created[$name])) {
  191.             throw new Kdyby\InvalidArgumentException("Container with name '$name' already exists.");
  192.         }
  193.  
  194.         return $this[$name];
  195.     }
  196.  
  197.  
  198.  
  199.     /**
  200.      * @param \Nette\Forms\Container $container
  201.      * @param boolean $cleanUpGroups
  202.      *
  203.      * @throws \Kdyby\InvalidArgumentException
  204.      */
  205.     public function remove(Container $container, $cleanUpGroups = FALSE)
  206.     {
  207.         if (!$container->parent === $this) {
  208.             throw new Kdyby\InvalidArgumentException('Given component ' . $container->name . ' is not children of ' . $this->name . '.');
  209.         }
  210.  
  211.         // to check if form was submitted by this one
  212.         foreach ($container->getComponents(TRUE, 'Nette\Forms\ISubmitterControl') as $button) {
  213.             if ($button->isSubmittedBy()) {
  214.                 $this->submittedBy = TRUE;
  215.                 break;
  216.             }
  217.         }
  218.  
  219.         // get components
  220.         $components = $container->getComponents(TRUE);
  221.         $this->removeComponent($container);
  222.  
  223.         // reflection is required to hack form groups
  224.         $groupRefl = Nette\Reflection\ClassType::from('Nette\Forms\ControlGroup');
  225.         $controlsProperty = $groupRefl->getProperty('controls');
  226.         $controlsProperty->setAccessible(TRUE);
  227.  
  228.         // walk groups and clean then from removed components
  229.         $affected = array();
  230.         foreach ($this->getForm()->getGroups() as $group) {
  231.             $groupControls = $controlsProperty->getValue($group);
  232.  
  233.             foreach ($components as $control) {
  234.                 if ($groupControls->contains($control)) {
  235.                     $groupControls->detach($control);
  236.  
  237.                     if (!in_array($group, $affected, TRUE)) {
  238.                         $affected[] = $group;
  239.                     }
  240.                 }
  241.             }
  242.         }
  243.  
  244.         // remove affected & empty groups
  245.         if ($cleanUpGroups && $affected) {
  246.             foreach ($this->getForm()->getComponents(FALSE, 'Nette\Forms\Container') as $container) {
  247.                 if ($index = array_search($container->currentGroup, $affected, TRUE)) {
  248.                     unset($affected[$index]);
  249.                 }
  250.             }
  251.  
  252.             foreach ($affected as $group) {
  253.                 if (!$group->getControls() && in_array($group, $this->getForm()->getGroups(), TRUE)) {
  254.                     $this->getForm()->removeGroup($group);
  255.                 }
  256.             }
  257.         }
  258.     }
  259.  
  260.  
  261.  
  262.     /**
  263.      * Loads data received from POST
  264.      */
  265.     protected function loadHttpData()
  266.     {
  267.         if ($this->getHttpRequest()->isPost()) {
  268.             $values = (array)$this->getHttpData();
  269.             foreach ($values as $key => $value) {
  270.                 if (is_array($value) && !$this->getComponent($key, FALSE)) {
  271.                     $this->createComponent($key);
  272.                 }
  273.             }
  274.  
  275.             $this->setValues($values);
  276.         }
  277.     }
  278.  
  279.  
  280.  
  281.     /**
  282.      * Counts filled values, filtered by given names
  283.      *
  284.      * @param array $components
  285.      * @param array $subComponents
  286.      * @return int
  287.      */
  288.     public function countFilledWithout(array $components = array(), array $subComponents = array())
  289.     {
  290.         $httpData = array_diff_key((array)$this->getHttpData(), array_flip($components));
  291.  
  292.         if (!$httpData) {
  293.             return 0;
  294.         }
  295.  
  296.         $rows = array();
  297.         $subComponents = array_flip($subComponents);
  298.         foreach ($httpData as $item) {
  299.             $rows[] = array_filter(array_diff_key($item, $subComponents)) ?: FALSE;
  300.         }
  301.  
  302.         return count(array_filter($rows));
  303.     }
  304.  
  305.  
  306.  
  307.     /**
  308.      * @param array $exceptChildren
  309.      * @return bool
  310.      */
  311.     public function isAllFilled(array $exceptChildren = array())
  312.     {
  313.         $components = array();
  314.         foreach ($this->getComponents(FALSE, 'Nette\Forms\IControl') as $control) {
  315.             $components[] = $control->getName();
  316.         }
  317.  
  318.         foreach ($this->getContainers() as $container) {
  319.             foreach ($container->getComponents(TRUE, 'Nette\Forms\ISubmitterControl') as $button) {
  320.                 $exceptChildren[] = $button->getName();
  321.             }
  322.         }
  323.  
  324.         $filled = $this->countFilledWithout($components, array_unique($exceptChildren));
  325.         return $filled === count($this->getContainers());
  326.     }
  327.  
  328.  
  329.  
  330.     /**
  331.      * @return mixed|NULL
  332.      */
  333.     private function getHttpData()
  334.     {
  335.         $httpRequest = $this->getHttpRequest();
  336.  
  337.         if ($httpRequest->isPost()) {
  338.             $post = (array)$httpRequest->getPost();
  339.  
  340.             $chain = array();
  341.             $parent = $this;
  342.  
  343.             while (!$parent instanceof Nette\Forms\Form) {
  344.                 $chain[] = $parent->getName();
  345.                 $parent = $parent->getParent();
  346.             };
  347.  
  348.             while ($chain) {
  349.                 $post = &$post[array_pop($chain)];
  350.             }
  351.  
  352.             return $post;
  353.         }
  354.  
  355.         return NULL;
  356.     }
  357.  
  358.  
  359.  
  360.     /**
  361.      * @param \Nette\Http\IRequest $request
  362.      *
  363.      * @return \Kdyby\Forms\Containers\Replicator
  364.      */
  365.     public function setHttpRequest(Nette\Http\IRequest $request)
  366.     {
  367.         $this->httpRequest = $request;
  368.         return $this;
  369.     }
  370.  
  371.  
  372.  
  373.     /**
  374.      * @return \Nette\Http\Request
  375.      */
  376.     private function getHttpRequest()
  377.     {
  378.         if ($this->httpRequest !== NULL) {
  379.             return $this->httpRequest;
  380.         }
  381.  
  382.         return $this->httpRequest = $this->getForm()->getPresenter()
  383.             ->getContext()->httpRequest;
  384.     }
  385.  
  386.  
  387.  
  388.     /**
  389.      * @param string $methodName
  390.      */
  391.     public static function register($methodName = 'addDynamic')
  392.     {
  393.         Container::extensionMethod($methodName, function ($_this, $name, $factory, $createDefault = 0) {
  394.             return $_this[$name] = new Replicator($factory, $createDefault);
  395.         });
  396.     }
  397.  
  398. }
Advertisement
Add Comment
Please, Sign In to add comment