diff --git a/src/JsParsing/ComputedKey.php b/src/JsParsing/ComputedKey.php new file mode 100644 index 0000000..c1a2c0b --- /dev/null +++ b/src/JsParsing/ComputedKey.php @@ -0,0 +1,24 @@ +expression = $expression; + } + + /** + * @param array $data + * + * @return expression as evaluated in the context of the data + */ + public function evaluate( array $data ) { + return $this->expression->evaluate( $data ); + } + +} diff --git a/src/JsParsing/PeastExpressionConverter.php b/src/JsParsing/PeastExpressionConverter.php index 47480b3..af644b0 100644 --- a/src/JsParsing/PeastExpressionConverter.php +++ b/src/JsParsing/PeastExpressionConverter.php @@ -50,11 +50,15 @@ protected function convertMemberExpression( MemberExpression $expression ) { $parts = []; while ( $expression !== null ) { if ( get_class( $expression ) === MemberExpression::class ) { - $property = $expression->getProperty()->getName(); - array_unshift( $parts, $property ); + if ( $expression->getComputed() ) { + array_unshift( $parts, new ComputedKey( $this->convertExpression( $expression->getProperty() ) ) ); + } else { + $propertyName = $this->convertKeyToLiteral( $expression->getProperty() ); + array_unshift( $parts, new StringLiteral( $propertyName ) ); + } $expression = $expression->getObject(); } elseif ( get_class( $expression ) === Identifier::class ) { - array_unshift( $parts, $expression->getName() ); + array_unshift( $parts, new StringLiteral( $expression->getName() ) ); $expression = null; } else { throw new RuntimeException( @@ -85,7 +89,7 @@ public function convertExpression( Expression $expression ) { UnaryExpression::class => $this->convertUnaryExpression( $expression ), MemberExpression::class => $this->convertMemberExpression( $expression ), PeastStringLiteral::class => new StringLiteral( $expression->getValue() ), - Identifier::class => new VariableAccess( [ $expression->getName() ] ), + Identifier::class => new VariableAccess( [ new StringLiteral( $expression->getName() ) ] ), CallExpression::class => $this->convertCallExpression( $expression ), ObjectExpression::class => $this->convertObjectExpression( $expression ), PeastBooleanLiteral::class => new BooleanLiteral( $expression->getValue() ), @@ -100,6 +104,7 @@ public function convertExpression( Expression $expression ) { protected function convertKeyToLiteral( $key ) { return match( get_class( $key ) ) { PeastStringLiteral::class => $key->getValue(), + PeastNumericLiteral::class => $key->getValue(), Identifier::class => $key->getName(), default => throw new RuntimeException( 'Unable to extract name from dictionary key of type ' . get_class( $key ) diff --git a/src/JsParsing/VariableAccess.php b/src/JsParsing/VariableAccess.php index f97be03..97cc9af 100644 --- a/src/JsParsing/VariableAccess.php +++ b/src/JsParsing/VariableAccess.php @@ -7,7 +7,7 @@ class VariableAccess implements ParsedExpression { /** - * @var string[] + * @var ParsedExpression[] */ private $pathParts; @@ -24,11 +24,14 @@ public function __construct( array $pathParts ) { public function evaluate( array $data ) { $value = $data; foreach ( $this->pathParts as $key ) { - if ( !array_key_exists( $key, $value ) ) { - $expression = implode( '.', $this->pathParts ); + $keyValue = $key->evaluate( $data ); + if ( !array_key_exists( $keyValue, $value ) ) { + $expression = implode( '.', array_map( + static fn ( $part ) => $part->evaluate( $data ), $this->pathParts + ) ); throw new RuntimeException( "Undefined variable '{$expression}'" ); } - $value = $value[$key]; + $value = $value[$keyValue]; } return $value; } diff --git a/tests/php/TemplatingTest.php b/tests/php/TemplatingTest.php index 02ef7d5..cdd49e2 100644 --- a/tests/php/TemplatingTest.php +++ b/tests/php/TemplatingTest.php @@ -255,6 +255,46 @@ public function testTemplateWithForLoopAndMultipleElementsInArrayToIterate_Rende $this->assertSame( '
', $result ); } + public function testTemplateWithForLoopAndMultipleElementsInNestedArrayWithStringKeys_ResolvesVariables() { + $result = $this->createAndRender( + '', + [ 'list' => [ 'data-values' => [ 1, 2 ] ] ] + ); + + $this->assertSame( '', $result ); + } + + public function testTemplateWithForLoopAndMultipleElementsInNestedIndexedArray_ResolvesVariables() { + $result = $this->createAndRender( + '', + [ 'list' => [ [ 3, 4, 5 ], [ 1, 2 ] ] ] + ); + + $this->assertSame( '', $result ); + } + + public function testForVariableIsAvailableForNestedExpressions() { + $result = $this->createAndRender( + '{{ data[index] }}
' . + '1
2
{{ indexKeys[index.key] }}
' . + 'value1
value2