diff --git a/core/lib/Thelia/Core/Template/Element/BaseLoop.php b/core/lib/Thelia/Core/Template/Element/BaseLoop.php index c2399a3d3..f7a958cad 100755 --- a/core/lib/Thelia/Core/Template/Element/BaseLoop.php +++ b/core/lib/Thelia/Core/Template/Element/BaseLoop.php @@ -38,16 +38,14 @@ abstract class BaseLoop /** * @var \Symfony\Component\HttpFoundation\Request */ - public $request; + protected $request; /** * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ - public $dispatcher; + protected $dispatcher; - public $limit; - public $page; - public $offset; + private $args; protected function getDefaultArgs() { @@ -59,6 +57,8 @@ abstract class BaseLoop } /** + * Create a new Loop + * * @param \Symfony\Component\HttpFoundation\Request $request * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ @@ -66,11 +66,94 @@ abstract class BaseLoop { $this->request = $request; $this->dispatcher = $dispatcher; + + $this->args = $this->getArgDefinitions()->addArguments($this->getDefaultArgs()); } - public function getArgs() - { - return $this->defineArgs()->addArguments($this->getDefaultArgs()); + /** + * Initialize the loop arguments. + * + * @param array $nameValuePairs a array of name => value pairs. The name is the name of the argument. + * + * @throws \InvalidArgumentException if somùe argument values are missing, or invalid + */ + public function initializeArgs(array $nameValuePairs) { + + $faultActor = array(); + $faultDetails = array(); + + while (($argument = $this->args->current()) !== false) { + + $value = isset($nameValuePairs[$argument->name]) ? $nameValuePairs[$argument->name] : null; + + /* check if mandatory */ + if($value === null && $argument->mandatory) { + $faultActor[] = $argument->name; + $faultDetails[] = sprintf('"%s" parameter is missing', $argument->name); + continue; + } + + /* check if empty */ + if($value === '' && !$argument->empty) { + $faultActor[] = $argument->name; + $faultDetails[] = sprintf('"%s" parameter cannot be empty', $argument->name); + continue; + } + + /* check type */ + if($value !== null && !$argument->type->isValid($value)) { + $faultActor[] = $argument->name; + $faultDetails[] = sprintf('Invalid value for "%s" argument', $argument->name); + continue; + } + + /* set default */ + /* did it as last checking for we consider default value is acceptable no matter type or empty restriction */ + if($value === null) { + $value = $argument->default; + } + + $argument->setValue($value); + + $this->args->next(); + } + + if (!empty($faultActor)) { + + $complement = sprintf('[%s]', implode(', ', $faultDetails)); + throw new \InvalidArgumentException($complement); + } + } + + /** + * Return a loop argument + * + * @param string $argumentName the argument name + * + * @throws \InvalidArgumentException if argument is not found in loop argument list + * @return Argument the loop argument. + */ + public function getArg($argumentName) { + + $arg = $this->args->get($argumentName); + + if ($arg === null) + throw new \InvalidArgumentException("Undefined loop argument '$argumentName'"); + + return $arg; + } + + /** + * Return a loop argument value + * + * @param string $argumentName the argument name + * + * @throws \InvalidArgumentException if argument is not found in loop argument list + * @return Argument the loop argument. + */ + public function getArgValue($argumentName) { + + return $this->getArg($argumentName)->getValue(); } /** @@ -81,7 +164,7 @@ abstract class BaseLoop */ public function search(ModelCriteria $search, &$pagination = null) { - if($this->page !== null) { + if($this->getArgValue('page') !== null) { return $this->searchWithPagination($search, $pagination); } else { return $this->searchWithOffset($search); @@ -95,10 +178,10 @@ abstract class BaseLoop */ public function searchWithOffset(ModelCriteria $search) { - if($this->limit >= 0) { - $search->limit($this->limit); + if($this->getArgValue('limit') >= 0) { + $search->limit($this->getArgValue('limit')); } - $search->offset($this->offset); + $search->offset($this->getArgValue('offset')); return $search->find(); } @@ -111,9 +194,9 @@ abstract class BaseLoop */ public function searchWithPagination(ModelCriteria $search, &$pagination) { - $pagination = $search->paginate($this->page, $this->limit); + $pagination = $search->paginate($this->getArgValue('page'), $this->getArgValue('limit')); - if($this->page > $pagination->getLastPage()) { + if($this->getArgValue('page') > $pagination->getLastPage()) { return array(); } else { return $pagination; @@ -145,7 +228,8 @@ abstract class BaseLoop * @param $pagination * * @return mixed - */abstract public function exec(&$pagination); + */ + abstract public function exec(&$pagination); /** * @@ -166,6 +250,6 @@ abstract class BaseLoop * * @return \Thelia\Core\Template\Loop\Argument\ArgumentCollection */ - abstract protected function defineArgs(); + abstract protected function getArgDefinitions(); } diff --git a/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php b/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php index 9fb72bff0..986cb1afd 100755 --- a/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php +++ b/core/lib/Thelia/Core/Template/Loop/Argument/Argument.php @@ -38,12 +38,25 @@ class Argument public $mandatory; public $empty; - public function __construct($name, \Thelia\Type\TypeCollection $type, $default = null, $mandatory = false, $empty = true) + private $value; + + public function __construct($name, \Thelia\Type\TypeCollection $type, $default = null, $mandatory = false, $empty = true, $value = null) { $this->name = $name; $this->type = $type; $this->mandatory = $mandatory ? true : false; $this->default = $default; + $this->empty = $empty; + + $this->setValue($value); + } + + public function getValue() { + return $this->value; + } + + public function setValue($value) { + $this->value = $value; } public static function createAnyTypeArgument($name, $default=null, $mandatory=false, $empty=true) diff --git a/core/lib/Thelia/Core/Template/Loop/Argument/ArgumentCollection.php b/core/lib/Thelia/Core/Template/Loop/Argument/ArgumentCollection.php index 6aec1fd9b..b4b680e08 100755 --- a/core/lib/Thelia/Core/Template/Loop/Argument/ArgumentCollection.php +++ b/core/lib/Thelia/Core/Template/Loop/Argument/ArgumentCollection.php @@ -30,14 +30,21 @@ namespace Thelia\Core\Template\Loop\Argument; class ArgumentCollection implements \Iterator { - private $position; - protected $arguments = array(); + private $arguments = array(); public function __construct() { $this->addArguments(func_get_args()); } + public function hasKey($key) { + return isset($this->arguments[$key]); + } + + public function get($key) { + return $this->hasKey($key) ? $this->arguments[$key] : null; + } + public function isEmpty() { return count($this->arguments) == 0; @@ -64,7 +71,8 @@ class ArgumentCollection implements \Iterator */ public function addArgument(Argument $argument) { - $this->arguments[] = $argument; + $this->arguments[$argument->name] = $argument; + return $this; } @@ -81,7 +89,7 @@ class ArgumentCollection implements \Iterator */ public function current() { - return $this->arguments[$this->position]; + return current($this->arguments); } /** @@ -92,7 +100,7 @@ class ArgumentCollection implements \Iterator */ public function next() { - $this->position++; + next($this->arguments); } /** @@ -103,7 +111,7 @@ class ArgumentCollection implements \Iterator */ public function key() { - return $this->position; + return key($this->arguments); } /** @@ -115,7 +123,7 @@ class ArgumentCollection implements \Iterator */ public function valid() { - return isset($this->arguments[$this->position]); + return $this->key() !== null; } /** @@ -126,6 +134,6 @@ class ArgumentCollection implements \Iterator */ public function rewind() { - $this->position = 0; + reset($this->arguments); } } diff --git a/core/lib/Thelia/Core/Template/Loop/Category.php b/core/lib/Thelia/Core/Template/Loop/Category.php index 7240b688c..e8b1f22e7 100755 --- a/core/lib/Thelia/Core/Template/Loop/Category.php +++ b/core/lib/Thelia/Core/Template/Loop/Category.php @@ -80,7 +80,7 @@ class Category extends BaseLoop /** * @return ArgumentCollection */ - protected function defineArgs() + protected function getArgDefinitions() { return new ArgumentCollection( Argument::createIntListTypeArgument('id'), @@ -109,31 +109,41 @@ class Category extends BaseLoop { $search = CategoryQuery::create(); - if (!is_null($this->id)) { - $search->filterById(explode(',', $this->id), Criteria::IN); + $id = $this->getArgValue('id'); + + if (!is_null($id)) { + $search->filterById(explode(',', $id), Criteria::IN); } - if (!is_null($this->parent)) { - $search->filterByParent($this->parent); + $parent = $this->getArgValue('parent'); + + if (!is_null($parent)) { + $search->filterByParent($parent); } - if ($this->current == 1) { + $current = $this->getArgValue('current'); + + if ($current == 1) { $search->filterById($this->request->get("category_id")); - } elseif (null !== $this->current && $this->current == 0) { + } elseif (null !== $current && $current == 0) { $search->filterById($this->request->get("category_id"), Criteria::NOT_IN); } - if (!is_null($this->exclude)) { - $search->filterById(explode(",", $this->exclude), Criteria::NOT_IN); + $exclude = $this->getArgValue('exclude'); + + if (!is_null($exclude)) { + $search->filterById(explode(",", $exclude), Criteria::NOT_IN); } - if (!is_null($this->link)) { - $search->filterByLink($this->link); + $link = $this->getArgValue('link'); + + if (!is_null($link)) { + $search->filterByLink($link); } - $search->filterByVisible($this->visible); + $search->filterByVisible($this->getArgValue('visible')); - switch ($this->order) { + switch ($this->getArgValue('order')) { case "alpha": $search->addAscendingOrderByColumn(\Thelia\Model\CategoryI18nPeer::TITLE); break; @@ -148,7 +158,7 @@ class Category extends BaseLoop break; } - if ($this->random == 1) { + if ($this->getArgValue('random') == 1) { $search->clearOrderByColumns(); $search->addAscendingOrderByColumn('RAND()'); } diff --git a/core/lib/Thelia/Core/Template/Loop/Feed.php b/core/lib/Thelia/Core/Template/Loop/Feed.php index 2cd1a1649..552e0dc3b 100644 --- a/core/lib/Thelia/Core/Template/Loop/Feed.php +++ b/core/lib/Thelia/Core/Template/Loop/Feed.php @@ -41,25 +41,13 @@ use Thelia\Type; */ class Feed extends BaseLoop { - - public $url; - public $limit; - public $timeout; - - public function defineArgs() + public function getArgDefinitions() { return new ArgumentCollection( new Argument( 'url', new TypeCollection(new Type\AnyType()) ), - new Argument( - 'limit', - new TypeCollection( - new Type\IntType() - ), - 3 - ), new Argument( 'timeout', new TypeCollection( @@ -75,7 +63,7 @@ class Feed extends BaseLoop * * @return \Thelia\Core\Template\Element\LoopResult */ - public function exec() + public function exec(&$pagination) { $cachedir = THELIA_ROOT . 'cache/feeds'; diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php index 24c5bf7c8..260c35f4e 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/TheliaLoop.php @@ -35,7 +35,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; class TheliaLoop implements SmartyPluginInterface { - protected static $pagination = null; + protected $pagination = null; protected $loopDefinition = array(); @@ -58,10 +58,10 @@ class TheliaLoop implements SmartyPluginInterface * * @return \PropelModelPager */ - public static function getPagination($loopId) + protected function getPagination($loopId) { - if(!empty(self::$pagination[$loopId])) { - return self::$pagination[$loopId]; + if(!empty($this->pagination[$loopId])) { + return $this->pagination[$loopId]; } else { return null; } @@ -95,14 +95,11 @@ class TheliaLoop implements SmartyPluginInterface throw new \InvalidArgumentException("A loop named '$name' already exists in the current scope."); } - $loop = $this->createLoopInstance(strtolower($params['type'])); + $loop = $this->createLoopInstance($params); - $this->getLoopArgument($loop, $params); - - self::$pagination[$name] = null; - - $loopResults = $loop->exec(self::$pagination[$name]); + $this->pagination[$name] = null; + $loopResults = $loop->exec($this->pagination[$name]); $this->loopstack[$name] = $loopResults; @@ -221,14 +218,15 @@ class TheliaLoop implements SmartyPluginInterface $loopName = $params['rel']; // Find loop results in the current template vars - $loopResults = $this->loopstack[$loopName]; - - if (empty($loopResults)) { + if (! isset($this->loopstack[$loopName])) { throw new \InvalidArgumentException("Loop $loopName is not defined."); } + $loopResults = $this->loopstack[$loopName]; + // Find pagination - $pagination = self::getPagination($loopName); + $pagination = $this->getPagination($loopName); + if ($pagination === null) { throw new \InvalidArgumentException("Loop $loopName : no pagination found."); } @@ -285,24 +283,30 @@ class TheliaLoop implements SmartyPluginInterface * @throws \Thelia\Tpex\Exception\InvalidElementException * @throws \Thelia\Tpex\Exception\ElementNotFoundException */ - protected function createLoopInstance($name) + protected function createLoopInstance($smartyParams) { - if (! isset($this->loopDefinition[$name])) { - throw new ElementNotFoundException(sprintf("%s loop does not exists", $name)); + $type = strtolower($smartyParams['type']); + + if (! isset($this->loopDefinition[$type])) { + throw new ElementNotFoundException(sprintf("%s loop does not exists", $type)); } - $class = new \ReflectionClass($this->loopDefinition[$name]); + $class = new \ReflectionClass($this->loopDefinition[$type]); if ($class->isSubclassOf("Thelia\Core\Template\Element\BaseLoop") === false) { throw new InvalidElementException(sprintf("%s Loop class have to extends Thelia\Core\Template\Element\BaseLoop", - $name)); + $type)); } - return $class->newInstance( + $loop = $class->newInstance( $this->request, $this->dispatcher ); + + $loop->initializeArgs($smartyParams); + + return $loop; } /** @@ -314,10 +318,6 @@ class TheliaLoop implements SmartyPluginInterface */ protected function getLoopArgument(BaseLoop $loop, $smartyParam) { - $defaultItemsParams = array('required' => true); - - $shortcutItemParams = array('optional' => array('required' => false)); - $faultActor = array(); $faultDetails = array(); diff --git a/core/lib/Thelia/Tests/Core/Template/Loop/Argument/ArgumentCollectionTest.php b/core/lib/Thelia/Tests/Core/Template/Loop/Argument/ArgumentCollectionTest.php index b7556c10c..21d1f132b 100755 --- a/core/lib/Thelia/Tests/Core/Template/Loop/Argument/ArgumentCollectionTest.php +++ b/core/lib/Thelia/Tests/Core/Template/Loop/Argument/ArgumentCollectionTest.php @@ -35,7 +35,7 @@ use Thelia\Type\TypeCollection; */ class ArgumentTest extends \PHPUnit_Framework_TestCase { - public function testArgumentCollectionConstruction() + public function testArgumentCollectionCreateAndWalk() { $collection = new ArgumentCollection( new Argument( @@ -61,30 +61,19 @@ class ArgumentTest extends \PHPUnit_Framework_TestCase ) ); - $this->assertAttributeEquals( - array( - 0 => new Argument( - 'arg0', - new TypeCollection( - new Type\AnyType() - ) - ), - 1 => new Argument( - 'arg1', - new TypeCollection( - new Type\AnyType() - ) - ), - 2 => new Argument( - 'arg2', - new TypeCollection( - new Type\AnyType() - ) - ), - ), - 'arguments', - $collection - ); + $this->assertTrue($collection->getCount() == 3); + + $this->assertTrue($collection->key() == 'arg0'); + $collection->next(); + $this->assertTrue($collection->key() == 'arg1'); + $collection->next(); + $this->assertTrue($collection->key() == 'arg2'); + $collection->next(); + + $this->assertFalse($collection->valid()); + + $collection->rewind(); + $this->assertTrue($collection->key() == 'arg0'); } public function testArgumentCollectionFetch()