Passed
Pull Request — master (#131)
by Mark
03:07
created

Aggregator::toSearchableArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
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 3
nop 0
dl 0
loc 8
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\Searchable;
15
16
use function in_array;
17
use Illuminate\Support\Str;
18
use Laravel\Scout\Searchable;
19
use Illuminate\Database\Eloquent\Model;
20
use Laravel\Scout\Events\ModelsImported;
21
use Illuminate\Database\Eloquent\Collection;
22
use Illuminate\Database\Eloquent\SoftDeletes;
23
use Algolia\ScoutExtended\Contracts\SearchableCountableContract;
24
use Algolia\ScoutExtended\Exceptions\ModelNotDefinedInAggregatorException;
25
26
abstract class Aggregator implements SearchableCountableContract
27
{
28
    use Searchable;
0 ignored issues
show
Bug introduced by
The trait Laravel\Scout\Searchable requires the property $queryCallback which is not provided by Algolia\ScoutExtended\Searchable\Aggregator.
Loading history...
29
30
    /**
31
     * The names of the models that should be aggregated.
32
     *
33
     * @var string[]
34
     */
35
    protected $models = [];
36
37
    /**
38
     * The model being queried, if any.
39
     *
40
     * @var \Illuminate\Database\Eloquent\Model|null
41
     */
42
    protected $model;
43
44
    /**
45
     * Map of model relations to load.
46
     *
47
     * @var string[]
48
     */
49
    protected $relations = [];
50
51
    /**
52
     * Returns the index name.
53
     *
54
     * @var string
55
     */
56
    protected $indexName;
57
58
    /**
59
     * Boot the aggregator.
60
     *
61
     * @return void
62
     */
63 6
    public static function bootSearchable(): void
64
    {
65 6
        ($self = new static)->registerSearchableMacros();
66
67 6
        $observer = tap(resolve(AggregatorObserver::class))->setAggregator(static::class, $models = $self->getModels());
68
69 6
        foreach ($models as $model) {
70 6
            $model::observe($observer);
71
        }
72 6
    }
73
74
    /**
75
     * Creates an instance of the aggregator.
76
     *
77
     * @param \Illuminate\Database\Eloquent\Model $model
78
     *
79
     * @return \Algolia\ScoutExtended\Searchable\Aggregator
80
     */
81 7
    public static function create(Model $model): Aggregator
82
    {
83 7
        return (new static)->setModel($model);
84
    }
85
86
    /**
87
     * Get the names of the models that should be aggregated.
88
     *
89
     * @return string[]
90
     */
91 6
    public function getModels(): array
92
    {
93 6
        return $this->models;
94
    }
95
96
    /**
97
     * Get the model instance being queried.
98
     *
99
     * @return \Illuminate\Database\Eloquent\Model
100
     *
101
     * @throws \Algolia\ScoutExtended\Exceptions\ModelNotDefinedInAggregatorException
102
     */
103 7
    public function getModel(): Model
104
    {
105 7
        if ($this->model === null) {
106
            throw new ModelNotDefinedInAggregatorException();
107
        }
108
109 7
        return $this->model;
110
    }
111
112
    /**
113
     * Set a model instance for the model being queried.
114
     *
115
     * @param \Illuminate\Database\Eloquent\Model $model
116
     *
117
     * @return \Algolia\ScoutExtended\Searchable\Aggregator
118
     */
119 7
    public function setModel(Model $model): Aggregator
120
    {
121 7
        $this->model = $model;
122
123 7
        return $this;
124
    }
125
126
    /**
127
     * Get the relations to load.
128
     *
129
     * @param  string  $modelClass
130
     *
131
     * @return  array
132
     */
133 1
    public function getRelations($modelClass): array
134
    {
135 1
        return $this->relations[$modelClass] ?? [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->relations[$modelClass] ?? array() could return the type string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
136
    }
137
138
    /**
139
     * Get the value used to index the model.
140
     *
141
     * @return mixed
142
     */
143 6
    public function getScoutKey()
144
    {
145 6
        if ($this->model === null) {
146
            throw new ModelNotDefinedInAggregatorException();
147
        }
148
149 6
        return method_exists($this->model, 'getScoutKey') ? $this->model->getScoutKey() : $this->model->getKey();
150
    }
151
152
    /**
153
     * Get the index name for the searchable.
154
     *
155
     * @return string
156
     */
157 8
    public function searchableAs(): string
158
    {
159 8
        return config('scout.prefix').str_replace('\\', '', Str::snake(class_basename(static::class)));
160
    }
161
162
    /**
163
     * Get the searchable array of the searchable.
164
     *
165
     * @return array
166
     */
167 6
    public function toSearchableArray(): array
168
    {
169 6
        if ($this->model === null) {
170
            throw new ModelNotDefinedInAggregatorException();
171
        }
172
173 6
        return method_exists($this->model, 'toSearchableArray') ? $this->model->toSearchableArray() :
0 ignored issues
show
Bug Best Practice introduced by
The expression return method_exists($th...$this->model->toArray() could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
174 6
            $this->model->toArray();
175
    }
176
177
    /**
178
     * Make all instances of the model searchable.
179
     *
180
     * @return void
181
     */
182 1
    public static function makeAllSearchable()
183
    {
184 1
        foreach ((new static)->getModels() as $model) {
185 1
            $instance = new $model;
186
187
            $softDeletes =
188 1
                in_array(SoftDeletes::class, class_uses_recursive($model)) && config('scout.soft_delete', false);
189
190
            $instance->newQuery()->when($softDeletes, function ($query) {
191
                $query->withTrashed();
192
            })->orderBy($instance->getKeyName())->chunk(config('scout.chunk.searchable', 500), function ($models) {
193
                $models = $models->map(function ($model) {
194 1
                    return static::create($model);
195 1
                })->filter->shouldBeSearchable();
196
197 1
                $models->searchable();
198
199 1
                event(new ModelsImported($models));
200 1
            });
201
        }
202 1
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207 6
    public function searchable(): void
208
    {
209 6
        AggregatorCollection::make([$this])->searchable();
210 6
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215 4
    public function unsearchable(): void
216
    {
217 4
        AggregatorCollection::make([$this])->unsearchable();
218 4
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223
    public function getSearchableCount(): int
224
    {
225
        $count = 0;
226
227
        foreach ($this->getModels() as $model) {
228
            $softDeletes =
229
                in_array(SoftDeletes::class, class_uses_recursive($model), true) && config('scout.soft_delete', false);
230
231
            $count += $model::query()->when($softDeletes, function ($query) {
232
                $query->withTrashed();
233
            })->count();
234
        }
235
236
        return (int) $count;
237
    }
238
239
    /**
240
     * Create a new Eloquent Collection instance.
241
     *
242
     * @param  array  $searchables
243
     *
244
     * @return \Illuminate\Database\Eloquent\Collection
245
     */
246 1
    public function newCollection(array $searchables = []): Collection
247
    {
248 1
        return new Collection($searchables);
249
    }
250
251
    /**
252
     * Handle dynamic method calls into the model.
253
     *
254
     * @param  string $method
255
     * @param  array $parameters
256
     *
257
     * @return mixed
258
     */
259
    public function __call($method, $parameters)
260
    {
261
        $model = $this->model ?? new class extends Model {
262
        };
263
264
        return $model->$method(...$parameters);
265
    }
266
}
267