Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/JsParsing/ComputedKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare( strict_types = 1 );

namespace WMDE\VueJsTemplating\JsParsing;

class ComputedKey implements ParsedExpression {

private ParsedExpression $expression;

public function __construct( ParsedExpression $expression ) {
$this->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 );
}

}
13 changes: 9 additions & 4 deletions src/JsParsing/PeastExpressionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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() ),
Expand All @@ -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 )
Expand Down
11 changes: 7 additions & 4 deletions src/JsParsing/VariableAccess.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class VariableAccess implements ParsedExpression {

/**
* @var string[]
* @var ParsedExpression[]
*/
private $pathParts;

Expand All @@ -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;
}
Expand Down
40 changes: 40 additions & 0 deletions tests/php/TemplatingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,46 @@ public function testTemplateWithForLoopAndMultipleElementsInArrayToIterate_Rende
$this->assertSame( '<p><a></a><a></a></p>', $result );
}

public function testTemplateWithForLoopAndMultipleElementsInNestedArrayWithStringKeys_ResolvesVariables() {
$result = $this->createAndRender(
'<p><a v-for="item in list[\'data-values\']"></a></p>',
[ 'list' => [ 'data-values' => [ 1, 2 ] ] ]
);

$this->assertSame( '<p><a></a><a></a></p>', $result );
}

public function testTemplateWithForLoopAndMultipleElementsInNestedIndexedArray_ResolvesVariables() {
$result = $this->createAndRender(
'<p><a v-for="item in list[1]"></a></p>',
[ 'list' => [ [ 3, 4, 5 ], [ 1, 2 ] ] ]
);

$this->assertSame( '<p><a></a><a></a></p>', $result );
}

public function testForVariableIsAvailableForNestedExpressions() {
$result = $this->createAndRender(
'<div><div v-for="index in indexKeys">' .
'<p>{{ data[index] }}</p>' .
'</div></div>',
[ 'indexKeys' => [ 'index1', 'index2' ],
'data' => [ 'index1' => 1, 'index2' => 2 ] ]
);
$this->assertSame( '<div><div><p>1</p></div><div><p>2</p></div></div>', $result );
}

public function testForVariableIsAvailableForNestedExpressions_NestedDataAccess() {
$result = $this->createAndRender(
'<div><div v-for="index in data">' .
'<p>{{ indexKeys[index.key] }}</p>' .
'</div></div>',
[ 'indexKeys' => [ 'value1', 'value2' ],
'data' => [ 'index1' => [ 'key' => 0 ], 'index2' => [ 'key' => 1 ] ] ]
);
$this->assertSame( '<div><div><p>value1</p></div><div><p>value2</p></div></div>', $result );
}

public function testTemplateWithForLoopMustache_RendersCorrectValues() {
$result = $this->createAndRender(
'<p><a v-for="item in list">{{item}}</a></p>',
Expand Down