Skip to content

Commit 054d12a

Browse files
committed
fix: allow correct parsing of expressions inside cakephp proxied methods (like n:context).
1 parent 1c6f599 commit 054d12a

File tree

7 files changed

+85
-5
lines changed

7 files changed

+85
-5
lines changed

src/Extension/Frontend/Nodes/DataSerializationNode.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,30 @@ public function &getIterator(): Generator
109109
yield $this->data;
110110
}
111111

112+
/**
113+
* Get attribute name (public for FormNContextNode integration).
114+
*/
115+
public function getPublicAttributeName(): string
116+
{
117+
return $this->getAttributeName();
118+
}
119+
120+
/**
121+
* Get the data expression (public for FormNContextNode integration).
122+
*/
123+
public function getDataExpression(): ExpressionNode
124+
{
125+
return $this->data;
126+
}
127+
128+
/**
129+
* Check if this node is in JavaScript mode.
130+
*/
131+
public function isJsMode(): bool
132+
{
133+
return $this->jsMode;
134+
}
135+
112136
/**
113137
* Get attribute name.
114138
*/

src/Latte/Nodes/AttributeParserTrait.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@
88
use Latte\Compiler\Nodes\Html\AttributeNode;
99
use Latte\Compiler\Nodes\Html\ElementNode;
1010
use Latte\Compiler\Nodes\Html\ExpressionAttributeNode;
11+
use Latte\Compiler\Nodes\Php\ArgumentNode;
1112
use Latte\Compiler\Nodes\Php\ArrayItemNode;
1213
use Latte\Compiler\Nodes\Php\Expression\ArrayNode;
14+
use Latte\Compiler\Nodes\Php\Expression\FunctionCallNode;
1315
use Latte\Compiler\Nodes\Php\ExpressionNode;
16+
use Latte\Compiler\Nodes\Php\NameNode;
1417
use Latte\Compiler\Nodes\Php\Scalar\StringNode;
1518
use Latte\Compiler\Nodes\PrintNode;
1619
use Latte\Compiler\Nodes\TextNode;
20+
use LatteView\Extension\Frontend\Nodes\DataSerializationNode;
21+
use LatteView\Extension\Frontend\Serializers\UniversalSerializer;
1722

1823
trait AttributeParserTrait
1924
{
@@ -28,6 +33,21 @@ protected function getAttributesNode(?ElementNode $el): ArrayNode
2833

2934
$items = [];
3035
foreach ($el->attributes->children as $child) {
36+
// Handle DataSerializationNode (e.g., n:data-alpine="$data")
37+
if ($child instanceof DataSerializationNode) {
38+
$attrName = $child->getPublicAttributeName();
39+
$nameNode = new StringNode($attrName);
40+
41+
// Create a function call expression: UniversalSerializer::serialize($data)
42+
$valueNode = new FunctionCallNode(
43+
new NameNode(UniversalSerializer::class . '::serialize'),
44+
[new ArgumentNode($child->getDataExpression(), false, false, null, $child->position)],
45+
);
46+
47+
$items[] = new ArrayItemNode($valueNode, $nameNode);
48+
continue;
49+
}
50+
3151
// Handle standard AttributeNode (e.g., method="get")
3252
if ($child instanceof AttributeNode && $child->name instanceof TextNode) {
3353
$name = $child->name->content;

tests/TestCase/Extension/Frontend/FrontendIntegrationTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,31 @@ public function testJavaScriptModeFallback(): void
234234
$this->assertStringContainsString('x-data="{"key":"value"', $output);
235235
$this->assertStringContainsString('"number":42}"', $output);
236236
}
237+
238+
public function testFormWithAlpineIntegration(): void
239+
{
240+
// Test that x-data attributes work correctly with form n:context
241+
$action = null;
242+
$isActive = false;
243+
$items = [1, 2, 3];
244+
245+
$this->view->set([
246+
'action' => $action,
247+
'isActive' => $isActive,
248+
'items' => $items,
249+
]);
250+
251+
$output = $this->view->render('frontend/form_with_alpine', false);
252+
253+
// Test that x-data attribute is present
254+
$this->assertStringContainsString('x-data=', $output);
255+
256+
// Test that boolean values are properly serialized
257+
$this->assertStringContainsString('isActive', $output);
258+
$this->assertStringContainsString('false', $output);
259+
260+
// Test that array values are properly serialized
261+
$this->assertStringContainsString('items', $output);
262+
$this->assertStringContainsString('[1,2,3]', $output);
263+
}
237264
}

tests/TestCase/Extension/Frontend/UniversalSerializerTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ public function testMaxDepthProtection(): void
175175
// Create a circular reference by using reflection to access the private method
176176
$reflection = new ReflectionClass(UniversalSerializer::class);
177177
$method = $reflection->getMethod('prepareData');
178-
$method->setAccessible(true);
179178

180179
$data = ['key' => 'value'];
181180

@@ -192,7 +191,6 @@ public function testUnknownDataTypeFallback(): void
192191

193192
$reflection = new ReflectionClass(UniversalSerializer::class);
194193
$method = $reflection->getMethod('prepareData');
195-
$method->setAccessible(true);
196194

197195
// Use a resource (which is not handled by any other condition)
198196
$resource = fopen('php://memory', 'r');

tests/TestCase/Integration/LatteViewIntegrationTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ public function testCustomViewExtension(): void
5454
{
5555
$reflection = new ReflectionClass($this->view);
5656
$property = $reflection->getProperty('_ext');
57-
$property->setAccessible(true);
5857

5958
$ext = $property->getValue($this->view);
6059

tests/TestCase/View/LatteViewTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,6 @@ public function testLayoutLookupWithReferenceType(): void
260260
$view = new AppView();
261261
$reflection = new ReflectionClass($view);
262262
$method = $reflection->getMethod('layoutLookup');
263-
$method->setAccessible(true);
264263

265264
// Create a mock template with reference type
266265
$mockTemplate = $this->createMock(Template::class);
@@ -277,7 +276,6 @@ public function testLayoutLookupWithoutReferenceType(): void
277276

278277
$reflection = new ReflectionClass($view);
279278
$method = $reflection->getMethod('layoutLookup');
280-
$method->setAccessible(true);
281279

282280
// Create a mock template without reference type
283281
$mockTemplate = $this->createMock(Template::class);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{block content}
2+
3+
{* Form with Alpine.js x-data attribute *}
4+
<form n:context="$action"
5+
hx-post="/"
6+
hx-target="#modal-content"
7+
hx-swap="innerHTML"
8+
hx-indicator="#modalIndicator"
9+
n:data-alpine="[
10+
'isActive' => ($isActive ? 'true' : 'false'),
11+
'items' => $items,
12+
]">
13+
<p>Form content</p>
14+
</form>

0 commit comments

Comments
 (0)