Skip to content

Commit 5bd58dd

Browse files
committed
AttributeBuilder
1 parent 92e2a5e commit 5bd58dd

File tree

3 files changed

+195
-162
lines changed

3 files changed

+195
-162
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
namespace Symfony\UX\StimulusBundle\Builder;
4+
5+
use Symfony\UX\StimulusBundle\Helper\StimulusSyntaxHelper;
6+
7+
class StimulusAttributeBuilder
8+
{
9+
private string $controllerName;
10+
private array $actions = [];
11+
private array $values = [];
12+
private array $classes = [];
13+
private array $targets = [];
14+
private array $outlets = [];
15+
private array $attributes = [];
16+
17+
public static function controller(string $controllerName): self
18+
{
19+
$builder = new self();
20+
$builder->controllerName = $controllerName;
21+
22+
return $builder;
23+
}
24+
25+
public function action(string $actionName, ?string $eventName = null, array $parameters = []): self
26+
{
27+
$this->actions[] = [
28+
'actionName' => $actionName,
29+
'eventName' => $eventName,
30+
'parameters' => $parameters,
31+
];
32+
33+
return $this;
34+
}
35+
36+
public function value(string $key, mixed $value): self
37+
{
38+
$this->values[$key] = $value;
39+
40+
return $this;
41+
}
42+
43+
public function class(string $key, mixed $value): self
44+
{
45+
$this->classes[$key] = $value;
46+
47+
return $this;
48+
}
49+
50+
public function target(string $key, mixed $value): self
51+
{
52+
$this->targets[$key] = $value;
53+
54+
return $this;
55+
}
56+
57+
public function outlet(string $identifier, string $outlet, mixed $selector): self
58+
{
59+
$this->outlets[] = [
60+
'identifier' => $identifier,
61+
'outlet' => $outlet,
62+
'selector' => $selector,
63+
];
64+
65+
return $this;
66+
}
67+
68+
public function build(): array
69+
{
70+
$syntaxHelper = new StimulusSyntaxHelper();
71+
$controllerName = $syntaxHelper->normalizeControllerName($this->controllerName);
72+
73+
$this->attributes['data-controller'] = $controllerName;
74+
75+
//Actions
76+
$this->attributes = array_merge(
77+
$this->attributes,
78+
...
79+
array_map(function (array $actionData) use ($controllerName): array {
80+
$actionName = htmlspecialchars($actionData['actionName']);
81+
$eventName = $actionData['eventName'];
82+
83+
$action = sprintf('%s#%s', $controllerName, $actionName);
84+
if (null !== $eventName) {
85+
$action = sprintf('%s->%s', htmlspecialchars($eventName), $action);
86+
}
87+
88+
return ['data-action' => $action];
89+
}, $this->actions)
90+
);
91+
92+
//Action Parameters
93+
$this->attributes = array_merge(
94+
$this->attributes,
95+
...
96+
array_map(
97+
function (array $actionData) use ($controllerName, $syntaxHelper): array {
98+
$parameters = [];
99+
100+
foreach ($actionData['parameters'] as $name => $value) {
101+
$key = $syntaxHelper->normalizeKeyName($name);
102+
$value = $syntaxHelper->getFormattedValue($value);
103+
104+
$parameters[sprintf(
105+
'data-%s-%s-param',
106+
$controllerName,
107+
$key
108+
)] = htmlspecialchars($value);
109+
}
110+
111+
return $parameters;
112+
},
113+
$this->actions
114+
)
115+
);
116+
117+
//Values
118+
foreach ($this->values as $key => $value) {
119+
if (null === $value) {
120+
continue;
121+
}
122+
123+
$key = $syntaxHelper->normalizeKeyName($key);
124+
$value = $syntaxHelper->getFormattedValue($value);
125+
126+
$this->attributes[sprintf('data-%s-%s-value', $controllerName, $key)] = $value;
127+
}
128+
129+
//Classes
130+
foreach ($this->classes as $key => $class) {
131+
$key = $syntaxHelper->normalizeKeyName($key);
132+
133+
$this->attributes[sprintf('data-%s-%s-class', $controllerName, $key)] = $class;
134+
}
135+
136+
//Outlets
137+
foreach ($this->outlets as $outletItem) {
138+
$identifier = $syntaxHelper->normalizeControllerName($outletItem['identifier']);
139+
$outlet = $syntaxHelper->normalizeKeyName($outletItem['outlet']);
140+
141+
$this->attributes[sprintf('data-%s-%s-outlet', $identifier, $outlet)] = htmlspecialchars(
142+
$outletItem['selector']
143+
);
144+
}
145+
146+
//Targets
147+
foreach ($this->targets as $key => $target) {
148+
$key = $syntaxHelper->normalizeKeyName($key);
149+
150+
$this->attributes[sprintf('data-%s-target', $key)] = htmlspecialchars($target);
151+
}
152+
153+
return $this->attributes;
154+
}
155+
}

src/StimulusBundle/src/Form/Extension/FormTypeExtension.php

-162
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Symfony\UX\StimulusBundle\Helper;
4+
5+
use Stringable;
6+
7+
use function is_bool;
8+
use function is_callable;
9+
use function is_object;
10+
use function is_scalar;
11+
12+
class StimulusSyntaxHelper
13+
{
14+
public function normalizeControllerName(string $controllerName): string
15+
{
16+
return preg_replace('/^@/', '', str_replace('_', '-', str_replace('/', '--', $controllerName)));
17+
}
18+
19+
public function normalizeKeyName(string $str): string
20+
{
21+
// Adapted from ByteString::camel
22+
$str = ucfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $str))));
23+
24+
// Adapted from ByteString::snake
25+
return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1-\2', $str));
26+
}
27+
28+
public function getFormattedValue(mixed $value): string
29+
{
30+
if ($value instanceof Stringable || (is_object($value) && is_callable([$value, '__toString']))) {
31+
$value = (string)$value;
32+
} elseif (!is_scalar($value)) {
33+
$value = json_encode($value);
34+
} elseif (is_bool($value)) {
35+
$value = $value ? 'true' : 'false';
36+
}
37+
38+
return (string)$value;
39+
}
40+
}

0 commit comments

Comments
 (0)