Skip to content

Commit da3b611

Browse files
committed
[TwigComponent] Improve exception message when component not found
1 parent 670a37a commit da3b611

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

src/TwigComponent/src/ComponentFactory.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,31 @@ private function postMount(object $component, array $data): array
194194
*/
195195
private function throwUnknownComponentException(string $name): void
196196
{
197-
throw new \InvalidArgumentException(sprintf('Unknown component "%s". The registered components are: %s', $name, implode(', ', array_keys($this->config))));
197+
$message = sprintf('Unknown component "%s".', $name);
198+
199+
$alternatives = [];
200+
201+
foreach (array_keys($this->config) as $type) {
202+
$lowerName = strtolower($name);
203+
$lowerType = strtolower($type);
204+
205+
$lev = levenshtein($lowerName, $lowerType);
206+
207+
if ($lev <= \strlen($lowerName) / 3 || str_contains($lowerType, $lowerName)) {
208+
$alternatives[] = $type;
209+
}
210+
}
211+
212+
if ($alternatives) {
213+
if (1 === \count($alternatives)) {
214+
$message .= ' Did you mean this: "';
215+
} else {
216+
$message .= ' Did you mean one of these: "';
217+
}
218+
219+
$message .= implode('", "', $alternatives).'"?';
220+
}
221+
222+
throw new \InvalidArgumentException($message);
198223
}
199224
}

src/TwigComponent/tests/Integration/ComponentFactoryTest.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,23 @@ public function testCanGetMetadataForSameComponentWithDifferentName(): void
152152
public function testCannotGetConfigByNameForNonRegisteredComponent(): void
153153
{
154154
$this->expectException(\InvalidArgumentException::class);
155-
$this->expectExceptionMessageMatches('/^Unknown component "invalid"\. The registered components are:.* component_a/');
155+
$this->expectExceptionMessage('Unknown component "tabl". Did you mean this: "table"?');
156156

157-
$this->factory()->metadataFor('invalid');
157+
$this->factory()->metadataFor('tabl');
158158
}
159159

160-
public function testCannotGetInvalidComponent(): void
160+
/**
161+
* @testWith ["tabl", "Unknown component \"tabl\". Did you mean this: \"table\"?"]
162+
* ["Basic", "Unknown component \"Basic\". Did you mean this: \"BasicComponent\"?"]
163+
* ["basic", "Unknown component \"basic\". Did you mean this: \"BasicComponent\"?"]
164+
* ["with", "Unknown component \"with\". Did you mean one of these: \"with_attributes\", \"with_exposed_variables\", \"WithSlots\"?"]
165+
*/
166+
public function testCannotGetInvalidComponent(string $name, string $expectedExceptionMessage): void
161167
{
162168
$this->expectException(\InvalidArgumentException::class);
163-
$this->expectExceptionMessageMatches('/^Unknown component "invalid"\. The registered components are:.* component_a/');
169+
$this->expectExceptionMessage($expectedExceptionMessage);
164170

165-
$this->factory()->get('invalid');
171+
$this->factory()->get($name);
166172
}
167173

168174
public function testInputPropsStoredOnMountedComponent(): void

0 commit comments

Comments
 (0)