Skip to content

Commit

Permalink
feature #1031 [TwigComponent] Improve exception message when componen…
Browse files Browse the repository at this point in the history
…t 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
-------

eeef204 [TwigComponent] Improve exception message when component not found
  • Loading branch information
weaverryan committed Aug 15, 2023
2 parents db67962 + eeef204 commit 3237b1b
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 6 deletions.
28 changes: 27 additions & 1 deletion src/TwigComponent/src/ComponentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,32 @@ private function isAnonymousComponent(string $name): bool
*/
private function throwUnknownComponentException(string $name): void
{
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))));
$message = sprintf('Unknown component "%s".', $name);
$lowerName = strtolower($name);
$nameLength = \strlen($lowerName);
$alternatives = [];

foreach (array_keys($this->config) as $type) {
$lowerType = strtolower($type);
$lev = levenshtein($lowerName, $lowerType);

if ($lev <= $nameLength / 3 || str_contains($lowerType, $lowerName)) {
$alternatives[] = $type;
}
}

if ($alternatives) {
if (1 === \count($alternatives)) {
$message .= ' Did you mean this: "';
} else {
$message .= ' Did you mean one of these: "';
}

$message .= implode('", "', $alternatives).'"?';
} else {
$message .= ' And no matching anonymous component template was found.';
}

throw new \InvalidArgumentException($message);
}
}
17 changes: 12 additions & 5 deletions src/TwigComponent/tests/Integration/ComponentFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,17 +152,24 @@ public function testCanGetMetadataForSameComponentWithDifferentName(): void
public function testCannotGetConfigByNameForNonRegisteredComponent(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessageMatches('/^Unknown component "invalid"\. The registered components are:.* component_a/');
$this->expectExceptionMessage('Unknown component "tabl". Did you mean this: "table"?');

$this->factory()->metadataFor('invalid');
$this->factory()->metadataFor('tabl');
}

public function testCannotGetInvalidComponent(): void
/**
* @testWith ["tabl", "Unknown component \"tabl\". Did you mean this: \"table\"?"]
* ["Basic", "Unknown component \"Basic\". Did you mean this: \"BasicComponent\"?"]
* ["basic", "Unknown component \"basic\". Did you mean this: \"BasicComponent\"?"]
* ["with", "Unknown component \"with\". Did you mean one of these: \"with_attributes\", \"with_exposed_variables\", \"WithSlots\"?"]
* ["anonAnon", "Unknown component \"anonAnon\". And no matching anonymous component template was found."]
*/
public function testCannotGetInvalidComponent(string $name, string $expectedExceptionMessage): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessageMatches('/^Unknown component "invalid"\. The registered components are:.* component_a/');
$this->expectExceptionMessage($expectedExceptionMessage);

$this->factory()->get('invalid');
$this->factory()->get($name);
}

public function testInputPropsStoredOnMountedComponent(): void
Expand Down

0 comments on commit 3237b1b

Please sign in to comment.