From 8dd088b69f13b687cbbdcf025f7040cf2013bff8 Mon Sep 17 00:00:00 2001 From: jcarter Date: Thu, 18 Oct 2018 11:35:13 +1100 Subject: [PATCH 1/4] add(ComponentService): Template logic for nested data I.E. Child objects can now have arrays of iterable children. --- .../Components/ComponentService.php | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/SilbinaryWolf/Components/ComponentService.php b/src/SilbinaryWolf/Components/ComponentService.php index c4f7b2a..f94a00e 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 */ @@ -233,3 +261,32 @@ public function renderComponent($name, array $props, SSViewer_Scope $scope) return $result; } } + +/** + * Used in place of ArrayList when preparing nested arrays for export for SS templates. + * + * var_export calls '__set_state' on classes, so it produces code like: + * + * ArrayList::__set_state(array('items' => [...])) + * + * 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']); + } +} \ No newline at end of file From 7ea21917df11808d26960c33b99525676fca312a Mon Sep 17 00:00:00 2001 From: jcarter Date: Thu, 18 Oct 2018 12:57:56 +1100 Subject: [PATCH 2/4] add(ComponentTest): testJSONDeeplyNested --- tests/ComponentTest.php | 107 +++++++++++++++++++ tests/templates/components/JSONNestedTest.ss | 23 ++++ 2 files changed, 130 insertions(+) create mode 100644 tests/templates/components/JSONNestedTest.ss 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 2.1

    +
  • +
  • +

    Item 2.2

    +
  • +
  • +

    item 2.3

    +
      +
    • +

      Item 3.1

      +
    • +
    • +

      Item 3.2

      +
    • +
    +
  • +
+

Item 1.2

+

Item 1.3

+
    +
  • +

    Item 2.4

    +
      +
    • +

      Item 3.3

      +
    • +
    • +

      Item 3.4

      +
    • +
    +
  • +
  • +

    Item 2.5

    +
  • +
  • +

    item 2.6

    +
  • +
+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 %> +
    + <% loop $Children %> +
  • +

    $Title

    + <% if $Children %> +
      + <% loop $Children %> +
    • +

      $Title

      +
    • + <% end_loop %> +
    + <% end_if %> +
  • + <% end_loop %> +
+ <% end_if %> + <% end_loop %> +<% end_if %> \ No newline at end of file From 60b79316bcaa4c47c1409d9c6ebe873367cfe900 Mon Sep 17 00:00:00 2001 From: jcarter Date: Tue, 23 Oct 2018 16:12:10 +1100 Subject: [PATCH 3/4] move(ArrayListExportable): To a new file. --- src/SilbinaryWolf/ArrayListExportable.php | 30 +++++++++++++++++++ .../Components/ComponentService.php | 29 ------------------ 2 files changed, 30 insertions(+), 29 deletions(-) create mode 100644 src/SilbinaryWolf/ArrayListExportable.php 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 f94a00e..1350634 100644 --- a/src/SilbinaryWolf/Components/ComponentService.php +++ b/src/SilbinaryWolf/Components/ComponentService.php @@ -261,32 +261,3 @@ public function renderComponent($name, array $props, SSViewer_Scope $scope) return $result; } } - -/** - * Used in place of ArrayList when preparing nested arrays for export for SS templates. - * - * var_export calls '__set_state' on classes, so it produces code like: - * - * ArrayList::__set_state(array('items' => [...])) - * - * 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']); - } -} \ No newline at end of file From ce1ac8a8cf416d9590fdbd3c95efc00a75c8c65a Mon Sep 17 00:00:00 2001 From: jcarter Date: Tue, 23 Oct 2018 16:21:27 +1100 Subject: [PATCH 4/4] update(composer): Bump branch-alias to 1.2.x --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" },