Completed
Pull Request — master (#175)
by
unknown
39:02 queued 34:59
created

SearchableFinder::fromCommand()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.072

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 2
nop 1
dl 0
loc 9
ccs 4
cts 5
cp 0.8
crap 3.072
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of Scout Extended.
7
 *
8
 * (c) Algolia Team <[email protected]>
9
 *
10
 *  For the full copyright and license information, please view the LICENSE
11
 *  file that was distributed with this source code.
12
 */
13
14
namespace Algolia\ScoutExtended\Helpers;
15
16
use function in_array;
17
use Illuminate\Support\Str;
18
use Laravel\Scout\Searchable;
19
use Illuminate\Console\Command;
20
use RuntimeException;
21
use Symfony\Component\Finder\Finder;
22
use Symfony\Component\Console\Exception\InvalidArgumentException;
23
24
/**
25
 * @internal
26
 */
27
final class SearchableFinder
28
{
29
    /**
30
     * @var array
31
     */
32
    private static $declaredClasses;
33
34
    /**
35
     * @var \Illuminate\Contracts\Foundation\Application
36
     */
37
    private $app;
38
39
    /**
40
     * @var array
41
     */
42
    private $projectNamespaces;
43
44
    /**
45
     * @var array
46
     */
47
    private $projectSources;
48
49
    /**
50
     * SearchableModelsFinder constructor.
51
     *
52
     * @param \Illuminate\Contracts\Foundation\Application $app
53
     * @return void
54
     */
55 17
    public function __construct($app)
56
    {
57 17
        $this->app = $app;
58 17
    }
59
60
    /**
61
     * Get a list of searchable models from the given command.
62
     *
63
     * @param \Illuminate\Console\Command $command
64
     *
65
     * @return array
66
     */
67 17
    public function fromCommand(Command $command): array
68
    {
69 17
        $searchables = (array) $command->argument('searchable');
70
71 17
        if (empty($searchables) && empty($searchables = $this->find())) {
72
            throw new InvalidArgumentException('No searchable classes found.');
73
        }
74
75 17
        return $searchables;
76
    }
77
78
    /**
79
     * Get a list of searchable models.
80
     *
81
     * @return string[]
82
     */
83 2
    public function find(): array
84
    {
85 2
        $this->inferProjectSourcePaths();
86
87
        return array_values(array_filter($this->getProjectClasses(), function (string $class) {
88 2
            return Str::startsWith($class, $this->projectNamespaces) && $this->isSearchableModel($class);
89 2
        }));
90
    }
91
92
    /**
93
     * @param  string $class
94
     *
95
     * @return bool
96
     */
97 2
    private function isSearchableModel($class): bool
98
    {
99 2
        return in_array(Searchable::class, class_uses_recursive($class), true);
100
    }
101
102
    /**
103
     * @return array
104
     */
105 2
    private function getProjectClasses(): array
106
    {
107 2
        if (self::$declaredClasses === null) {
0 ignored issues
show
introduced by
The condition self::declaredClasses === null is always false.
Loading history...
108 1
            $configFiles = Finder::create()
109 1
                ->files()
110 1
                ->notName('*.blade.php')
111 1
                ->name('*.php')
112 1
                ->in($this->projectSources);
113
114 1
            foreach ($configFiles->files() as $file) {
115 1
                require_once $file;
116
            }
117
118 1
            self::$declaredClasses = get_declared_classes();
119
        }
120
121 2
        return self::$declaredClasses;
122
    }
123
124
    /**
125
     * Using the laravel project's composer.json retrieve the PSR-4 autoload to determine
126
     * the paths to search and namespaces to check against.
127
     *
128
     * @return void
129
     */
130 2
    private function inferProjectSourcePaths(): void
131
    {
132 2
        $composerJson = json_decode(file_get_contents(base_path('composer.json')), true);
133 2
        $autoload = $composerJson['autoload'] ?? [];
134
135 2
        if (!isset($autoload['psr-4'])) {
136
            throw new RuntimeException('psr-4 autoload mappings are not present in composer.json');
137
        }
138
139 2
        $psr4 = collect($autoload['psr-4']);
140
141
        $this->projectSources = $psr4->values()->map(function($path) { return base_path($path); })->toArray();
142 2
        $this->projectNamespaces = $psr4->keys()->toArray();
143 2
    }
144
}
145