Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
…endframework#3758-scope-object-key-in-nested-partialloop-calls'

Close zendframework/zendframework#7093
Close zendframework/zendframework#3758
  • Loading branch information
Ocramius committed Jan 3, 2015
2 parents 179ff28 + 0d39443 commit b694af0
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 13 deletions.
5 changes: 3 additions & 2 deletions src/Helper/Partial.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ public function __invoke($name = null, $values = null)
/**
* Set object key
*
* @param string $key
* @return Partial
* @param string|null $key
*
* @return self
*/
public function setObjectKey($key)
{
Expand Down
106 changes: 95 additions & 11 deletions src/Helper/PartialLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ class PartialLoop extends Partial
*/
protected $partialCounter = 0;

/**
* The current nesting level
*
* @var int
*/
private $nestingLevel = 0;

/**
* Stack with object keys for each nested level
*
* @var array indexed by nesting level
*/
private $objectKeyStack = array(
0 => null,
);

/**
* Renders a template fragment within a variable scope distinct from the
* calling View object.
Expand All @@ -43,23 +59,17 @@ public function __invoke($name = null, $values = null)
return $this;
}

if (!is_array($values)) {
if ($values instanceof Traversable) {
$values = ArrayUtils::iteratorToArray($values, false);
} elseif (is_object($values) && method_exists($values, 'toArray')) {
$values = $values->toArray();
} else {
throw new Exception\InvalidArgumentException('PartialLoop helper requires iterable data');
}
}

// reset the counter if it's called again
$this->partialCounter = 0;
$content = '';

foreach ($values as $item) {
foreach ($this->extractViewVariables($values) as $item) {
$this->nestObjectKey();

$this->partialCounter++;
$content .= parent::__invoke($name, $item);

$this->unNestObjectKey();
}

return $content;
Expand All @@ -74,4 +84,78 @@ public function getPartialCounter()
{
return $this->partialCounter;
}

/**
* Set object key in this loop and any child loop
*
* {@inheritDoc}
*
* @param string|null $key
*
* @return self
*/
public function setObjectKey($key)
{
if (null === $key) {
unset($this->objectKeyStack[$this->nestingLevel]);
} else {
$this->objectKeyStack[$this->nestingLevel] = (string) $key;
}

return parent::setObjectKey($key);
}

/**
* Increment nestedLevel and default objectKey to parent's value
*
* @return self
*/
private function nestObjectKey()
{
$this->nestingLevel += 1;

$this->setObjectKey($this->getObjectKey());

return $this;
}

/**
* Decrement nestedLevel and restore objectKey to parent's value
*
* @return self
*/
private function unNestObjectKey()
{
$this->setObjectKey(null);

$this->nestingLevel -= 1;
$this->objectKey = $this->objectKeyStack[$this->nestingLevel];

return $this;
}

/**
* @param mixed $values
*
* @return array Variables to populate in the view
*/
private function extractViewVariables($values)
{
if ($values instanceof Traversable) {
return ArrayUtils::iteratorToArray($values, false);
}

if (is_array($values)) {
return $values;
}

if (is_object($values) && method_exists($values, 'toArray')) {
return $values->toArray();
}

throw new Exception\InvalidArgumentException(sprintf(
'PartialLoop helper requires iterable data, %s given',
is_object($values) ? get_class($values) : gettype($values)
));
}
}
50 changes: 50 additions & 0 deletions test/Helper/PartialLoopTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,56 @@ public function testShouldNotConvertToArrayRecursivelyIfModelIsTraversable()
}
}
}

/**
* @group 7093
*/
public function testNestedCallsShouldNotOverrideObjectKey()
{
$data = array();
for ($i = 0; $i < 3; $i++) {
$obj = new \stdClass();
$obj->helper = $this->helper;
$obj->objectKey = "foo" . $i;
$obj->message = "bar";
$obj->data = array(
$obj
);
$data[] = $obj;
}

$view = new View();
$view->resolver()->addPath($this->basePath . '/application/views/scripts');
$this->helper->setView($view);

$this->helper->setObjectKey('obj');
$result = $this->helper->__invoke('partialLoopParentObject.phtml', $data);

foreach ($data as $item) {
$string = 'This is an iteration with objectKey: ' . $item->objectKey;
$this->assertContains($string, $result, $result);
}
}

public function testPartialLoopWithInvalidValuesWillRaiseException()
{
$this->setExpectedException(
'Zend\View\Exception\InvalidArgumentException',
'PartialLoop helper requires iterable data, string given'
);

$this->helper->__invoke('partialLoopParentObject.phtml', 'foo');
}

public function testPartialLoopWithInvalidObjectValuesWillRaiseException()
{
$this->setExpectedException(
'Zend\View\Exception\InvalidArgumentException',
'PartialLoop helper requires iterable data, stdClass given'
);

$this->helper->__invoke('partialLoopParentObject.phtml', new \stdClass());
}
}

class IteratorTest implements Iterator
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

$vars = $this->vars();

if (empty($vars)) {
echo "No object model passed";
} else {
$objKey = current($this->vars())->helper->getObjectKey();
echo 'This is an iteration with objectKey: ' . $objKey;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

if (!isset($this->vars()->obj)) {
echo "No object model passed";
} else {
echo $this->obj->message;
$this->obj->helper->setObjectKey($this->vars()->obj->objectKey);
echo $this->obj->helper->__invoke('partialLoopChildObject.phtml', $obj->data);
}

0 comments on commit b694af0

Please sign in to comment.