diff --git a/composer.json b/composer.json index 8a9a63e..0638006 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" }, "installer-name": "components" }, diff --git a/src/SilbinaryWolf/ArrayListExportable.php b/src/SilbinaryWolf/ArrayListExportable.php new file mode 100644 index 0000000..0e1ad15 --- /dev/null +++ b/src/SilbinaryWolf/ArrayListExportable.php @@ -0,0 +1,30 @@ + [...])) + * + * And because ArrayList doens't implement '__set_state', executing the code throws errors. + * So we work around this by using an ArrayListExportable to produce: + * + * ArrayListExportable::__set_state(array('items' => [...])) + * + * And implement '__set_state' to return a constructed ArrayList. + */ +class ArrayListExportable +{ + public function __construct($array = array()) + { + // need to store items for var_export to recurse + $this->items = $array; + } + + public static function __set_state ($array) + { + // when executed, we naruto-style body-replace with in an ArrayList + return new ArrayList($array['items']); + } +} diff --git a/src/SilbinaryWolf/Components/ComponentService.php b/src/SilbinaryWolf/Components/ComponentService.php index c4f7b2a..1350634 100644 --- a/src/SilbinaryWolf/Components/ComponentService.php +++ b/src/SilbinaryWolf/Components/ComponentService.php @@ -90,7 +90,8 @@ public function generateTemplateCode(array $res, $parser) } foreach ($jsonData as $propertyName => $value) { if (is_array($value)) { - $value = 'new '.'ArrayList'.'('.var_export($value, true).')'; + // export valid template logic for nested data + $value = self::exportNestedDataForTemplates($value); } $phpCodeValueParts[] = "\$_props['".$propertyName."'][] = ".$value.";"; } @@ -185,6 +186,33 @@ public function generateTemplateCode(array $res, $parser) return $result; } + /** + * Recursively replace nonassociative arrays with ArrayListExportable and + * output with 'var_export' to produce template logic for the nested data. + * + * @param array $array The nested data to export + * @param bool $root Ignore this + * @return string Executable template logic + */ + private static function exportNestedDataForTemplates(array $array, $root = true) + { + // depth first + foreach ($array as $prop => &$value) { + if (is_array($value)) { + $value = self::exportNestedDataForTemplates($value, false); + } + } + unset($value); + + // json data expected to be keyed with ints, over the usual strings + if (isset($array[0])) { + // replace array with exportable array list + $array = new ArrayListExportable($array); + } + + return $root ? var_export($array, true) : $array; + } + /** * @return DBComponentField|SS_List|DataObject|ArrayData */ diff --git a/tests/ComponentTest.php b/tests/ComponentTest.php index 48238de..2f6bb6d 100644 --- a/tests/ComponentTest.php +++ b/tests/ComponentTest.php @@ -528,6 +528,113 @@ public function testJSONPropertyErrorHandling() } } + /** + * Test iterating nested arrays in templates + */ + public function testJSONDeeplyNested() + { + $template = << +SSTemplate; + $expectedHTML = <<Item 1.1 + +

Item 1.2

+

Item 1.3

+ +HTML; + $resultHTML = SSViewer::fromString($template)->process(null); + $this->assertEqualIgnoringWhitespace($expectedHTML, $resultHTML, 'Unexpected output'); + } + /** * Taken from "framework\tests\view\SSViewerTest.php" */ diff --git a/tests/templates/components/JSONNestedTest.ss b/tests/templates/components/JSONNestedTest.ss new file mode 100644 index 0000000..37e3abb --- /dev/null +++ b/tests/templates/components/JSONNestedTest.ss @@ -0,0 +1,23 @@ +<% if $NestedData %> + <% loop $NestedData %> +

$Title

+ <% if $Children %> + + <% end_if %> + <% end_loop %> +<% end_if %> \ No newline at end of file