InvalidRouteActionSolutionProvider   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 67
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 7
lcom 1
cbo 4
dl 0
loc 67
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A canSolve() 0 12 3
A getSolutions() 0 27 3
A findRelatedController() 0 21 1
1
<?php
2
3
namespace Facade\Ignition\SolutionProviders;
4
5
use Facade\Ignition\Support\ComposerClassMap;
6
use Facade\Ignition\Support\StringComparator;
7
use Facade\IgnitionContracts\BaseSolution;
8
use Facade\IgnitionContracts\HasSolutionsForThrowable;
9
use Illuminate\Support\Str;
10
use Throwable;
11
use UnexpectedValueException;
12
13
class InvalidRouteActionSolutionProvider implements HasSolutionsForThrowable
14
{
15
    protected const REGEX = '/\[([a-zA-Z\\\\]+)\]/m';
16
17
    public function canSolve(Throwable $throwable): bool
18
    {
19
        if (! $throwable instanceof UnexpectedValueException) {
20
            return false;
21
        }
22
23
        if (! preg_match(self::REGEX, $throwable->getMessage(), $matches)) {
24
            return false;
25
        }
26
27
        return Str::startsWith($throwable->getMessage(), 'Invalid route action: ');
28
    }
29
30
    public function getSolutions(Throwable $throwable): array
31
    {
32
        preg_match(self::REGEX, $throwable->getMessage(), $matches);
33
34
        $invalidController = $matches[1] ?? null;
35
36
        $suggestedController = $this->findRelatedController($invalidController);
37
38
        if ($suggestedController === $invalidController) {
39
            return [
40
                BaseSolution::create("`{$invalidController}` is not invokable.")
41
                    ->setSolutionDescription("The controller class `{$invalidController}` is not invokable. Did you forget to add the `__invoke` method or is the controller's method missing in your routes file?"),
42
            ];
43
        }
44
45
        if ($suggestedController) {
46
            return [
47
                BaseSolution::create("`{$invalidController}` was not found.")
48
                    ->setSolutionDescription("Controller class `{$invalidController}` for one of your routes was not found. Did you mean `{$suggestedController}`?"),
49
            ];
50
        }
51
52
        return [
53
            BaseSolution::create("`{$invalidController}` was not found.")
54
                ->setSolutionDescription("Controller class `{$invalidController}` for one of your routes was not found. Are you sure this controller exists and is imported correctly?"),
55
        ];
56
    }
57
58
    protected function findRelatedController(string $invalidController): ?string
59
    {
60
        $composerClassMap = app(ComposerClassMap::class);
61
62
        $controllers = collect($composerClassMap->listClasses())
63
            ->filter(function (string $file, string $fqcn) {
64
                return Str::endsWith($fqcn, 'Controller');
65
            })
66
            ->mapWithKeys(function (string $file, string $fqcn) {
67
                return [$fqcn => class_basename($fqcn)];
68
            })
69
            ->toArray();
70
71
        $basenameMatch = StringComparator::findClosestMatch($controllers, $invalidController, 4);
72
73
        $controllers = array_flip($controllers);
74
75
        $fqcnMatch = StringComparator::findClosestMatch($controllers, $invalidController, 4);
76
77
        return $fqcnMatch ?? $basenameMatch;
78
    }
79
}
80