Skip to content

Commit c6adf0a

Browse files
committed
feature #1031 [TwigComponent] Improve exception message when component not found (norkunas)
This PR was merged into the 2.x branch. Discussion ---------- [TwigComponent] Improve exception message when component not found | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | Fix #... | License | MIT I have over 150 components and to me it's just useless to list all component names in exception message and to look for the right name in a very long list. Used same code to get alternatives like in Symfony codebase. Commits ------- eeef2043 [TwigComponent] Improve exception message when component not found
2 parents eb67605 + 2b3c019 commit c6adf0a

File tree

2 files changed

+39
-6
lines changed

2 files changed

+39
-6
lines changed

src/ComponentFactory.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,32 @@ private function isAnonymousComponent(string $name): bool
217217
*/
218218
private function throwUnknownComponentException(string $name): void
219219
{
220-
throw new \InvalidArgumentException(sprintf('Unknown component "%s". The registered components are: %s. And no matching anonymous component template was found', $name, implode(', ', array_keys($this->config))));
220+
$message = sprintf('Unknown component "%s".', $name);
221+
$lowerName = strtolower($name);
222+
$nameLength = \strlen($lowerName);
223+
$alternatives = [];
224+
225+
foreach (array_keys($this->config) as $type) {
226+
$lowerType = strtolower($type);
227+
$lev = levenshtein($lowerName, $lowerType);
228+
229+
if ($lev <= $nameLength / 3 || str_contains($lowerType, $lowerName)) {
230+
$alternatives[] = $type;
231+
}
232+
}
233+
234+
if ($alternatives) {
235+
if (1 === \count($alternatives)) {
236+
$message .= ' Did you mean this: "';
237+
} else {
238+
$message .= ' Did you mean one of these: "';
239+
}
240+
241+
$message .= implode('", "', $alternatives).'"?';
242+
} else {
243+
$message .= ' And no matching anonymous component template was found.';
244+
}
245+
246+
throw new \InvalidArgumentException($message);
221247
}
222248
}

tests/Integration/ComponentFactoryTest.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,24 @@ 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+
* ["anonAnon", "Unknown component \"anonAnon\". And no matching anonymous component template was found."]
166+
*/
167+
public function testCannotGetInvalidComponent(string $name, string $expectedExceptionMessage): void
161168
{
162169
$this->expectException(\InvalidArgumentException::class);
163-
$this->expectExceptionMessageMatches('/^Unknown component "invalid"\. The registered components are:.* component_a/');
170+
$this->expectExceptionMessage($expectedExceptionMessage);
164171

165-
$this->factory()->get('invalid');
172+
$this->factory()->get($name);
166173
}
167174

168175
public function testInputPropsStoredOnMountedComponent(): void

0 commit comments

Comments
 (0)