AbstractRepository   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 376
Duplicated Lines 0 %

Test Coverage

Coverage 91.43%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 25
eloc 93
c 1
b 0
f 0
dl 0
loc 376
ccs 96
cts 105
cp 0.9143
rs 10

19 Methods

Rating   Name   Duplication   Size   Complexity  
A paginate() 0 6 1
A first() 0 10 1
A update() 0 13 2
A getPaginator() 0 11 1
A count() 0 10 1
A create() 0 7 1
A simplePaginate() 0 6 1
A get() 0 3 1
A clearModel() 0 5 1
A findByField() 0 12 1
A all() 0 15 2
A delete() 0 15 2
A find() 0 10 1
A applyScope() 0 8 3
A setScope() 0 5 1
A clearScope() 0 5 1
A setModel() 0 11 2
A init() 0 7 1
A __construct() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Noitran\Repositories\Repositories;
6
7
use Closure;
8
use Illuminate\Container\Container;
9
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Support\Collection;
12
use Noitran\Repositories\Contracts\Repository\Criticizable;
13
use Noitran\Repositories\Contracts\Repository\RepositoryInterface;
14
use Noitran\Repositories\Contracts\Schema\SchemaInterface;
15
use Noitran\Repositories\Events\EntityCreated;
16
use Noitran\Repositories\Events\EntityDeleted;
17
use Noitran\Repositories\Events\EntityUpdated;
18
use Noitran\Repositories\Exceptions\RepositoryException;
19
20
/**
21
 * Class AbstractRepository.
22
 */
23
abstract class AbstractRepository implements RepositoryInterface, SchemaInterface, Criticizable
24
{
25
    use Concerns\InteractsWithSchema,
0 ignored issues
show
Bug introduced by
The trait Noitran\Repositories\Rep...rns\InteractsWithSchema requires the property $from which is not provided by Noitran\Repositories\Rep...ries\AbstractRepository.
Loading history...
26
        Concerns\HasCriteria,
27
        Concerns\BuildsQueries;
28
29
    /**
30
     * @var Container
31
     */
32
    protected $app;
33
34
    /**
35
     * @var Model
36
     */
37
    protected $model;
38
39
    /**
40
     * @var Closure
41
     */
42
    protected $scope;
43
44
    /**
45
     * AbstractRepository constructor.
46
     *
47
     * @param Container $app
48
     *
49 28
     * @throws RepositoryException
50
     */
51 28
    public function __construct(Container $app)
52
    {
53 28
        $this->app = $app;
54 28
55
        $this->init();
56
    }
57
58
    /**
59
     * @throws RepositoryException
60
     *
61 28
     * @return AbstractRepository
62
     */
63 28
    public function init(): self
64 28
    {
65 28
        $this->criteria = new Collection();
66
        $this->setModel();
67 28
        $this->boot();
68
69
        return $this;
70
    }
71
72
    /**
73
     * Returns model's fully qualified class name.
74
     *
75
     * @return string
76
     */
77
    abstract public function getModelClassName(): string;
78
79
    /**
80
     * Fires when repository is created.
81
     */
82
    abstract public function boot(): void;
83
84
    /**
85
     * @throws RepositoryException
86
     *
87 28
     * @return Model
88
     */
89 28
    public function setModel(): Model
90
    {
91 28
        $model = $this->app->make($this->getModelClassName());
92
93
        if (! $model instanceof Model) {
94
            throw new RepositoryException(
95
                "Class {$this->getModelClassName()} must be an instance of " . Model::class
96
            );
97 28
        }
98
99
        return $this->model = $model;
100
    }
101
102
    /**
103
     * Clears model.
104
     *
105 26
     * @throws RepositoryException
106
     */
107 26
    public function clearModel(): self
108
    {
109 26
        $this->setModel();
110
111
        return $this;
112
    }
113
114
    /**
115
     * @param Closure $scope
116
     *
117
     * @return $this
118
     */
119
    public function setScope(Closure $scope): self
120
    {
121
        $this->scope = $scope;
122
123
        return $this;
124
    }
125
126
    /**
127
     * Clears query scope.
128
     *
129 15
     * @return $this
130
     */
131 15
    public function clearScope(): self
132
    {
133 15
        $this->scope = null;
134
135
        return $this;
136
    }
137
138
    /**
139
     * Apply scope in current Query.
140
     *
141 26
     * @return $this
142
     */
143 26
    protected function applyScope(): self
144
    {
145
        if (isset($this->scope) && \is_callable($this->scope)) {
146
            $callback = $this->scope;
147
            $this->model = $callback($this->model);
148 26
        }
149
150
        return $this;
151
    }
152
153
    /**
154
     * Get list of records.
155
     *
156
     * @param array $columns
157
     *
158
     * @throws RepositoryException
159
     *
160 15
     * @return EloquentBuilder[]|\Illuminate\Database\Eloquent\Collection|Model[]|mixed
161
     */
162 15
    public function all($columns = ['*'])
163 15
    {
164
        $this->applyCriteria()
165 15
            ->applyScope();
166 14
167
        if ($this->model instanceof EloquentBuilder) {
0 ignored issues
show
introduced by
$this->model is never a sub-type of Illuminate\Database\Eloquent\Builder.
Loading history...
168 1
            $output = $this->model->get($columns);
169
        } else {
170
            $output = $this->model::all($columns);
171 15
        }
172 15
173
        $this->clearModel()
174 15
            ->clearScope();
175
176
        return $output;
177
    }
178
179
    /**
180
     * Alias of all().
181
     *
182
     * @param array $columns
183
     *
184
     * @throws RepositoryException
185
     *
186
     * @return EloquentBuilder[]|\Illuminate\Database\Eloquent\Collection|Model[]|mixed
187
     */
188
    public function get($columns = ['*'])
189
    {
190
        return $this->all($columns);
191
    }
192
193
    /**
194
     * Get collection of paginated records.
195
     *
196
     * @param int|null $perPage
197
     * @param array $columns
198
     *
199
     * @throws RepositoryException
200
     *
201 1
     * @return mixed
202
     */
203
    public function paginate(int $perPage = null, $columns = ['*'])
204 1
    {
205 1
        return $this->getPaginator($perPage, $columns, function ($perPage, $columns) {
206 1
            return $this->model
207 1
                ->paginate($perPage, $columns)
208
                ->appends(app('request')->query());
209
        });
210
    }
211
212
    /**
213
     * @param int|null $perPage
214
     * @param array $columns
215
     *
216
     * @throws RepositoryException
217
     *
218 1
     * @return mixed
219
     */
220
    public function simplePaginate(int $perPage = null, $columns = ['*'])
221 1
    {
222 1
        return $this->getPaginator($perPage, $columns, function ($perPage, $columns) {
223 1
            return $this->model
224 1
                ->simplePaginate($perPage, $columns)
225
                ->appends(app('request')->query());
226
        });
227
    }
228
229
    /**
230
     * @param int|null $perPage
231
     * @param array $columns
232
     * @param callable|null $callback
233
     *
234
     * @throws RepositoryException
235
     *
236 2
     * @return mixed
237
     */
238 2
    protected function getPaginator(int $perPage = null, $columns = ['*'], callable $callback = null)
239 2
    {
240
        $this->applyCriteria()
241 2
            ->applyScope();
242
243 2
        $perPage = $perPage ?? config('repositories.filtering.default_settings.per_page', $this->model->getPerPage());
244 2
245
        $results = $callback($perPage, $columns);
246 2
        $this->clearModel();
247
248
        return $results;
249
    }
250
251
    /**
252
     * Get single or multiple records by their primary ids.
253
     *
254
     * @param mixed $id
255
     * @param array $columns
256
     *
257
     * @throws RepositoryException
258
     *
259 3
     * @return mixed
260
     */
261 3
    public function find($id, $columns = ['*'])
262 3
    {
263
        $this->applyCriteria()
264 3
            ->applyScope();
265
266 3
        $model = $this->model->find($id, $columns);
267
268 3
        $this->clearModel();
269
270
        return $model;
271
    }
272
273
    /**
274
     * @param $field
275
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
276
     * @param array $columns
277
     *
278
     * @throws RepositoryException
279
     *
280 1
     * @return mixed
281
     */
282 1
    public function findByField($field, $value = null, $columns = ['*'])
283 1
    {
284
        $this->applyCriteria()
285 1
            ->applyScope();
286 1
287 1
        $results = $this->model
288
            ->where($field, '=', $value)
289 1
            ->get($columns);
290
291 1
        $this->clearModel();
292
293
        return $results;
294
    }
295
296
    /**
297
     * Count results of repository.
298
     *
299
     * @param array $columns
300
     *
301
     * @throws RepositoryException
302
     *
303 1
     * @return int
304
     */
305 1
    public function count($columns = ['*']): int
306 1
    {
307
        $this->applyCriteria()
308 1
            ->applyScope();
309
310 1
        $count = $this->model->count($columns);
311
312 1
        $this->clearModel();
313
314
        return $count;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $count could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
315
    }
316
317
    /**
318
     * Execute the query and get the first result.
319
     *
320
     * @param array $columns
321
     *
322
     * @throws RepositoryException
323
     *
324 1
     * @return mixed
325
     */
326 1
    public function first($columns = ['*']): ?Model
327 1
    {
328
        $this->applyCriteria()
329 1
            ->applyScope();
330
331 1
        $model = $this->model->first($columns);
332
333 1
        $this->clearModel();
334
335
        return $model;
336
    }
337
338
    /**
339
     * @param array $attributes
340
     *
341
     * @throws RepositoryException
342
     *
343 1
     * @return Model|null
344
     */
345 1
    public function create(array $attributes = []): ?Model
346 1
    {
347 1
        $model = $this->model->create($attributes);
348
        $this->clearModel();
349 1
        event(new EntityCreated($this, $model));
350
351
        return $model;
352
    }
353
354
    /**
355
     * @param mixed $model
356
     * @param array $attributes
357
     *
358
     * @throws RepositoryException
359
     *
360 2
     * @return Model
361
     */
362 2
    public function update($model, array $attributes): Model
363
    {
364 2
        $this->applyScope();
365 1
366
        if (! $model instanceof Model) {
367
            $model = $this->model->findOrFail($model);
368 2
        }
369 2
370 2
        $model->update($attributes);
371
        $this->clearModel();
372 2
        event(new EntityUpdated($this, $model));
373
374
        return $model;
375
    }
376
377
    /**
378
     * @param $model
379
     *
380
     * @throws RepositoryException
381
     *
382 2
     * @return bool|null
383
     */
384 2
    public function delete($model): ?bool
385
    {
386 2
        $this->applyScope();
387 1
388
        if (! $model instanceof Model) {
389
            $model = $this->find($model);
390 2
        }
391 2
392 2
        $clonedModel = clone $model;
393
        $this->clearModel();
394 2
        $deleted = $model->delete();
395
396 2
        event(new EntityDeleted($this, $clonedModel));
397
398
        return $deleted;
399
    }
400
}
401