ViewNotFoundSolutionProvider.php
3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<?php
namespace Facade\Ignition\SolutionProviders;
use Facade\Ignition\Exceptions\ViewException;
use Facade\Ignition\Support\StringComparator;
use Facade\IgnitionContracts\BaseSolution;
use Facade\IgnitionContracts\HasSolutionsForThrowable;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\View;
use InvalidArgumentException;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Throwable;
class ViewNotFoundSolutionProvider implements HasSolutionsForThrowable
{
    protected const REGEX = '/View \[(.*)\] not found/m';
    public function canSolve(Throwable $throwable): bool
    {
        if (! $throwable instanceof InvalidArgumentException && ! $throwable instanceof ViewException) {
            return false;
        }
        return (bool)preg_match(self::REGEX, $throwable->getMessage(), $matches);
    }
    public function getSolutions(Throwable $throwable): array
    {
        preg_match(self::REGEX, $throwable->getMessage(), $matches);
        $missingView = $matches[1] ?? null;
        $suggestedView = $this->findRelatedView($missingView);
        if ($suggestedView == $missingView) {
            return [
                BaseSolution::create("{$missingView} was not found.")
                    ->setSolutionDescription('View names should not contain the . character!'),
            ];
        }
        if ($suggestedView) {
            return [
                BaseSolution::create("{$missingView} was not found.")
                    ->setSolutionDescription("Did you mean `{$suggestedView}`?"),
            ];
        }
        return [
            BaseSolution::create("{$missingView} was not found.")
                ->setSolutionDescription('Are you sure the view exists and is a `.blade.php` file?'),
        ];
    }
    protected function findRelatedView(string $missingView): ?string
    {
        $views = $this->getAllViews();
        return StringComparator::findClosestMatch($views, $missingView);
    }
    protected function getAllViews(): array
    {
        /** @var \Illuminate\View\FileViewFinder $fileViewFinder */
        $fileViewFinder = View::getFinder();
        $extensions = $fileViewFinder->getExtensions();
        $viewsForHints = collect($fileViewFinder->getHints())
            ->flatMap(function ($paths, string $namespace) use ($extensions) {
                $paths = Arr::wrap($paths);
                return collect($paths)
                    ->flatMap(function (string $path) use ($extensions) {
                        return $this->getViewsInPath($path, $extensions);
                    })
                    ->map(function (string $view) use ($namespace) {
                        return "{$namespace}::{$view}";
                    })
                    ->toArray();
            });
        $viewsForViewPaths = collect($fileViewFinder->getPaths())
            ->flatMap(function (string $path) use ($extensions) {
                return $this->getViewsInPath($path, $extensions);
            });
        return $viewsForHints->merge($viewsForViewPaths)->toArray();
    }
    protected function getViewsInPath(string $path, array $extensions): array
    {
        $filePatterns = array_map(function (string $extension) {
            return "*.{$extension}";
        }, $extensions);
        $extensionsWithDots = array_map(function (string $extension) {
            return ".{$extension}";
        }, $extensions);
        $files = (new Finder())
            ->in($path)
            ->files();
        foreach ($filePatterns as $filePattern) {
            $files->name($filePattern);
        }
        $views = [];
        foreach ($files as $file) {
            if ($file instanceof SplFileInfo) {
                $view = $file->getRelativePathname();
                $view = str_replace($extensionsWithDots, '', $view);
                $view = str_replace('/', '.', $view);
                $views[] = $view;
            }
        }
        return $views;
    }
}