Completed
Push — master ( 75fb75...593219 )
by Nuno
14:21 queued 11:10
created

Aggregator::newCollection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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
     * Returns the index name.
46
     *
47
     * @var string
48
     */
49
    protected $indexName;
50
51
    /**
52
     * Boot the aggregator.
53
     *
54
     * @return void
55
     */
56 6
    public static function bootSearchable(): void
57
    {
58 6
        ($self = new static)->registerSearchableMacros();
59
60 6
        $observer = tap(resolve(AggregatorObserver::class))->setAggregator(static::class, $models = $self->getModels());
61
62 6
        foreach ($models as $model) {
63 6
            $model::observe($observer);
64
        }
65 6
    }
66
67
    /**
68
     * Creates an instance of the aggregator.
69
     *
70
     * @param \Illuminate\Database\Eloquent\Model $model
71
     *
72
     * @return \Algolia\ScoutExtended\Searchable\Aggregator
73
     */
74 7
    public static function create(Model $model): Aggregator
75
    {
76 7
        return (new static)->setModel($model);
77
    }
78
79
    /**
80
     * Get the names of the models that should be aggregated.
81
     *
82
     * @return string[]
83
     */
84 6
    public function getModels(): array
85
    {
86 6
        return $this->models;
87
    }
88
89
    /**
90
     * Get the model instance being queried.
91
     *
92
     * @return \Illuminate\Database\Eloquent\Model
93
     *
94
     * @throws \Algolia\ScoutExtended\Exceptions\ModelNotDefinedInAggregatorException
95
     */
96 7
    public function getModel(): Model
97
    {
98 7
        if ($this->model === null) {
99
            throw new ModelNotDefinedInAggregatorException();
100
        }
101
102 7
        return $this->model;
103
    }
104
105
    /**
106
     * Set a model instance for the model being queried.
107
     *
108
     * @param \Illuminate\Database\Eloquent\Model $model
109
     *
110
     * @return \Algolia\ScoutExtended\Searchable\Aggregator
111
     */
112 7
    public function setModel(Model $model): Aggregator
113
    {
114 7
        $this->model = $model;
115
116 7
        return $this;
117
    }
118
119
    /**
120
     * Get the value used to index the model.
121
     *
122
     * @return mixed
123
     */
124 6
    public function getScoutKey()
125
    {
126 6
        if ($this->model === null) {
127
            throw new ModelNotDefinedInAggregatorException();
128
        }
129
130 6
        return method_exists($this->model, 'getScoutKey') ? $this->model->getScoutKey() : $this->model->getKey();
131
    }
132
133
    /**
134
     * Get the index name for the searchable.
135
     *
136
     * @return string
137
     */
138 8
    public function searchableAs(): string
139
    {
140 8
        return config('scout.prefix').str_replace('\\', '', Str::snake(class_basename(static::class)));
141
    }
142
143
    /**
144
     * Get the searchable array of the searchable.
145
     *
146
     * @return array
147
     */
148 6
    public function toSearchableArray(): array
149
    {
150 6
        if ($this->model === null) {
151
            throw new ModelNotDefinedInAggregatorException();
152
        }
153
154 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...
155 6
            $this->model->toArray();
156
    }
157
158
    /**
159
     * Make all instances of the model searchable.
160
     *
161
     * @return void
162
     */
163 1
    public static function makeAllSearchable()
164
    {
165 1
        foreach ((new static)->getModels() as $model) {
166 1
            $instance = new $model;
167
168
            $softDeletes =
169 1
                in_array(SoftDeletes::class, class_uses_recursive($model)) && config('scout.soft_delete', false);
170
171
            $instance->newQuery()->when($softDeletes, function ($query) {
172
                $query->withTrashed();
173
            })->orderBy($instance->getKeyName())->chunk(config('scout.chunk.searchable', 500), function ($models) {
174
                $models = $models->map(function ($model) {
175 1
                    return static::create($model);
176 1
                })->filter->shouldBeSearchable();
177
178 1
                $models->searchable();
179
180 1
                event(new ModelsImported($models));
181 1
            });
182
        }
183 1
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188 6
    public function searchable(): void
189
    {
190 6
        AggregatorCollection::make([$this])->searchable();
191 6
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196 4
    public function unsearchable(): void
197
    {
198 4
        AggregatorCollection::make([$this])->unsearchable();
199 4
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function getSearchableCount(): int
205
    {
206
        $count = 0;
207
208
        foreach ($this->getModels() as $model) {
209
            $softDeletes =
210
                in_array(SoftDeletes::class, class_uses_recursive($model), true) && config('scout.soft_delete', false);
211
212
            $count += $model::query()->when($softDeletes, function ($query) {
213
                $query->withTrashed();
214
            })->count();
215
        }
216
217
        return (int) $count;
218
    }
219
220
    /**
221
     * Create a new Eloquent Collection instance.
222
     *
223
     * @param  array  $searchables
224
     *
225
     * @return \Illuminate\Database\Eloquent\Collection
226
     */
227 1
    public function newCollection(array $searchables = []): Collection
228
    {
229 1
        return new Collection($searchables);
230
    }
231
232
    /**
233
     * Handle dynamic method calls into the model.
234
     *
235
     * @param  string $method
236
     * @param  array $parameters
237
     *
238
     * @return mixed
239
     */
240
    public function __call($method, $parameters)
241
    {
242
        $model = $this->model ?? new class extends Model {
243
        };
244
245
        return $model->$method(...$parameters);
246
    }
247
}
248