Skip to content

Commit 4622298

Browse files
authored
fix: view component attribute fixes (tempestphp#459)
1 parent ee84b94 commit 4622298

16 files changed

+154
-28
lines changed

src/Tempest/Support/src/StringHelper.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ public function pascal(): self
7777
return new self(implode('', $studlyWords));
7878
}
7979

80+
public function camel(): self
81+
{
82+
return new self(lcfirst((string) $this->pascal()));
83+
}
84+
8085
public function deduplicate(string|array $characters = ' '): self
8186
{
8287
$string = $this->string;

src/Tempest/View/src/Elements/ElementFactory.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Tempest\View\Elements;
66

7+
use DOMAttr;
78
use DOMElement;
89
use DOMNode;
910
use DOMText;
@@ -48,8 +49,11 @@ private function makeElement(View $view, DOMNode $node, ?Element $parent): ?Elem
4849
} else {
4950
$attributes = [];
5051

52+
/** @var DOMAttr $attribute */
5153
foreach ($node->attributes as $attribute) {
52-
$attributes[$attribute->name] = $attribute->value;
54+
$name = (string) \Tempest\Support\str($attribute->name)->camel();
55+
56+
$attributes[$name] = $attribute->value;
5357
}
5458

5559
$element = new GenericElement(

src/Tempest/View/src/Elements/GenericElement.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,13 @@ public function getSlot(string $name = 'slot'): ?Element
9494

9595
public function getData(?string $key = null): mixed
9696
{
97+
if ($key && $this->hasAttribute($key)) {
98+
return $this->getAttribute($key);
99+
}
100+
97101
$parentData = $this->getParent()?->getData() ?? [];
98102

99-
$data = [...$this->view->getData(), ...$parentData, ...$this->data];
103+
$data = [...$this->attributes, ...$this->view->getData(), ...$parentData, ...$this->data];
100104

101105
if ($key) {
102106
return $data[$key] ?? null;

src/Tempest/View/src/IsView.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ public function get(string $key): mixed
5858
return $this->{$key} ?? $this->data[$key] ?? null;
5959
}
6060

61+
public function has(string $key): bool
62+
{
63+
return array_key_exists($key, $this->data) || property_exists($this, $key);
64+
}
65+
6166
public function data(mixed ...$params): self
6267
{
6368
$this->rawData = [...$this->rawData, ...$params];

src/Tempest/View/src/Renderers/TempestViewRenderer.php

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -135,19 +135,7 @@ private function resolveContent(View $view): string
135135
$path = $view->getPath();
136136

137137
if (! str_ends_with($path, '.php')) {
138-
ob_start();
139-
140-
try {
141-
// TODO: find a better way of dealing with views that declare strict types
142-
$path = str_replace('declare(strict_types=1);', '', $path);
143-
144-
/** @phpstan-ignore-next-line */
145-
eval('?>' . $path . '<?php');
146-
} catch (ParseError) {
147-
return $path;
148-
}
149-
150-
return ob_get_clean();
138+
return $this->evalContentIsolated($view, $path);
151139
}
152140

153141
$discoveryLocations = $this->kernel->discoveryLocations;
@@ -161,7 +149,7 @@ private function resolveContent(View $view): string
161149
throw new Exception("View {$path} not found");
162150
}
163151

164-
return $this->resolveContentIsolated($path, $view->getData());
152+
return $this->resolveContentIsolated($view, $path);
165153
}
166154

167155
private function resolveViewComponent(GenericElement $element): ?ViewComponent
@@ -239,9 +227,9 @@ private function renderCollectionElement(View $view, CollectionElement $collecti
239227
private function renderViewComponent(View $view, ViewComponent $viewComponent, GenericElement $element): string
240228
{
241229
$renderedContent = preg_replace_callback(
242-
pattern: '/<x-slot\s*(name="(?<name>\w+)")?\s*\/>/',
230+
pattern: '/<x-slot\s*(name="(?<name>\w+)")?((\s*\/>)|><\/x-slot>)/',
243231
callback: function ($matches) use ($view, $element) {
244-
$name = $matches['name'] ?? 'slot';
232+
$name = $matches['name'] ?: 'slot';
245233

246234
$slot = $element->getSlot($name);
247235

@@ -306,14 +294,53 @@ private function renderGenericElement(View $view, GenericElement $element): stri
306294
return "<{$element->getTag()}{$attributes}>{$content}</{$element->getTag()}>";
307295
}
308296

309-
private function resolveContentIsolated(string $_path, array $_data): string
297+
private function resolveContentIsolated(View $_view, string $_path): string
310298
{
311299
ob_start();
312300

301+
$_data = $_view->getData();
302+
313303
extract($_data, flags: EXTR_SKIP);
314304

315305
include $_path;
316306

307+
$content = ob_get_clean();
308+
309+
// If the view defines local variables, we add them here to the view object as well
310+
foreach (get_defined_vars() as $key => $value) {
311+
if (! $_view->has($key)) {
312+
$_view->data(...[$key => $value]);
313+
}
314+
}
315+
316+
return $content;
317+
}
318+
319+
private function evalContentIsolated(View $_view, string $_content): string
320+
{
321+
ob_start();
322+
323+
$_data = $_view->getData();
324+
325+
extract($_data, flags: EXTR_SKIP);
326+
327+
try {
328+
// TODO: find a better way of dealing with views that declare strict types
329+
$_content = str_replace('declare(strict_types=1);', '', $_content);
330+
331+
/** @phpstan-ignore-next-line */
332+
eval('?>' . $_content . '<?php');
333+
} catch (ParseError) {
334+
return $_content;
335+
}
336+
337+
// If the view defines local variables, we add them here to the view object as well
338+
foreach (get_defined_vars() as $key => $value) {
339+
if (! $_view->has($key)) {
340+
$_view->data(...[$key => $value]);
341+
}
342+
}
343+
317344
return ob_get_clean();
318345
}
319346
}

src/Tempest/View/src/View.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public function getRaw(string $key): mixed;
1616

1717
public function get(string $key): mixed;
1818

19+
public function has(string $key): bool;
20+
1921
public function data(...$params): self;
2022

2123
public function raw(string $name): ?string;

src/Tempest/View/src/ViewComponentView.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
namespace Tempest\View;
66

7-
use Tempest\View\Elements\GenericElement;
8-
97
final class ViewComponentView implements View
108
{
119
use IsView;
@@ -18,15 +16,14 @@ public function __construct(
1816
$this->path = $content;
1917
}
2018

21-
public function __get(string $name): mixed
19+
public function getData(): array
2220
{
23-
$value = null;
24-
25-
if ($this->wrappingElement instanceof GenericElement) {
26-
$value = $this->wrappingElement->getAttribute($name);
27-
}
21+
return $this->wrappingElement->getData();
22+
}
2823

29-
return $value ?? $this->wrappingElement->getData($name);
24+
public function __get(string $name): mixed
25+
{
26+
return $this->wrappingElement->getData($name);
3027
}
3128

3229
public function __call(string $name, array $arguments)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<x-component name="x-view-component-attribute-without-this-a">
2+
<?= $var ?? 'nothing' ?>
3+
</x-component>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<x-view-component-attribute-without-this-a var="fromString" />
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<x-component name="x-view-component-with-camelcase-attribute-a">
2+
<?= $metaType ?? 'nothing' ?>
3+
</x-component>

0 commit comments

Comments
 (0)