Passed
Push — master ( e7b285...ac21bd )
by noitran
02:40
created

AbstractRepository::find()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 10
c 0
b 0
f 0
ccs 6
cts 6
cp 1
rs 10
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Noitran\Repositories\Repositories;
4
5
use Illuminate\Container\Container;
6
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
7
use Illuminate\Database\Eloquent\Model;
8
use Noitran\Repositories\Contracts\Criteria\CriteriaInterface;
9
use Noitran\Repositories\Contracts\Repository\RepositoryInterface;
10
use Noitran\Repositories\Contracts\Schema\SchemaInterface;
11
use Noitran\Repositories\Exceptions\RepositoryException;
12
use Illuminate\Support\Collection;
13
use Closure;
14
15
/**
16
 * Class AbstractRepository
17
 */
18
abstract class AbstractRepository implements RepositoryInterface, SchemaInterface
19
{
20
    use InteractsWithSchema;
0 ignored issues
show
Bug introduced by
The trait Noitran\Repositories\Rep...ies\InteractsWithSchema requires the property $from which is not provided by Noitran\Repositories\Rep...ries\AbstractRepository.
Loading history...
21
22
    /**
23
     * @var Container
24
     */
25
    protected $app;
26
27
    /**
28
     * @var Model
29
     */
30
    protected $model;
31
32
    /**
33
     * Collection of Criteria
34
     *
35
     * @var Collection
36
     */
37
    protected $criteria;
38
39
    /**
40
     * @var bool
41
     */
42
    protected $skipCriteria = false;
43
44
    /**
45
     * @var Closure
46
     */
47
    protected $scopeQuery;
48
49
    /**
50
     * AbstractRepository constructor.
51
     *
52
     * @param Container $app
53
     *
54
     * @throws RepositoryException
55
     */
56 4
    public function __construct(Container $app)
57
    {
58 4
        $this->app = $app;
59
60 4
        $this->init();
61 4
    }
62
63
    /**
64
     * @throws RepositoryException
65
     *
66
     * @return AbstractRepository
67
     */
68 4
    public function init(): self
69
    {
70 4
        $this->setModel();
71
72 4
        return $this;
73
    }
74
75
    /**
76
     * Returns model's fully qualified class name
77
     *
78
     * @return string
79
     */
80
    abstract public function getModelClassName(): string;
81
82
    /**
83
     * Fires when repository is created
84
     */
85
    abstract public function boot(): void;
86
87
    /**
88
     * @throws RepositoryException
89
     *
90
     * @return Model
91
     */
92 4
    public function setModel(): Model
93
    {
94 4
        $model = $this->app->make($this->getModelClassName());
95
96 4
        if (! $model instanceof Model) {
97
            throw new RepositoryException("Class {$this->getModelClassName()} must be an instance of " . Model::class);
98
        }
99
100 4
        return $this->model = $model;
101
    }
102
103
    /**
104
     * Get Collection of Criteria
105
     *
106
     * @return Collection|null
107
     */
108 4
    public function getCriteria(): ?Collection
109
    {
110 4
        return $this->criteria;
111
    }
112
113
    /**
114
     * Clears model
115
     *
116
     * @throws RepositoryException
117
     */
118 4
    public function clearModel(): self
119
    {
120 4
        $this->setModel();
121
122 4
        return $this;
123
    }
124
125
    /**
126
     * Clear all Criteria
127
     *
128
     * @return $this
129
     */
130
    public function clearCriteria(): self
131
    {
132
        $this->criteria = new Collection();
133
134
        return $this;
135
    }
136
137
    /**
138
     * Clears query scope
139
     *
140
     * @return $this
141
     */
142 1
    public function clearScope(): self
143
    {
144 1
        $this->scopeQuery = null;
145
146 1
        return $this;
147
    }
148
149
    /**
150
     * @return $this
151
     */
152
    public function withTrashed(): self
153
    {
154
        $this->model = $this->model->withTrashed();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->model->withTrashed() can also be of type Illuminate\Database\Eloquent\Builder. However, the property $model is declared as type Illuminate\Database\Eloquent\Model. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
155
156
        return $this;
157
    }
158
159
    /**
160
     * Apply criteria in current Query
161
     *
162
     * @return $this
163
     */
164 4
    protected function applyCriteria(): self
165
    {
166 4
        if ($this->skipCriteria === true) {
167
            return $this;
168
        }
169
170 4
        $criteria = $this->getCriteria();
171
172 4
        if (! $criteria) {
0 ignored issues
show
introduced by
$criteria is of type Illuminate\Support\Collection, thus it always evaluated to true.
Loading history...
173 4
            return $this;
174
        }
175
176
        foreach ($criteria as $c) {
177
            if ($c instanceof CriteriaInterface) {
178
                $this->model = $c->apply($this->model, $this);
179
            }
180
        }
181
182
        return $this;
183
    }
184
185
    /**
186
     * Apply scope in current Query
187
     *
188
     * @return $this
189
     */
190 4
    protected function applyScope(): self
191
    {
192 4
        if (isset($this->scopeQuery) && is_callable($this->scopeQuery)) {
193
            $callback = $this->scopeQuery;
194
            $this->model = $callback($this->model);
195
        }
196
197 4
        return $this;
198
    }
199
200
    /**
201
     * Get list of records
202
     *
203
     * @param array $columns
204
     *
205
     * @throws RepositoryException
206
     *
207
     * @return EloquentBuilder[]|\Illuminate\Database\Eloquent\Collection|Model[]|mixed
208
     */
209 1
    public function all($columns = ['*'])
210
    {
211 1
        $this->applyCriteria()
212 1
            ->applyScope();
213
214 1
        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...
215
            $output = $this->model->get($columns);
216
        } else {
217 1
            $output = $this->model::all($columns);
218
        }
219
220 1
        $this->clearModel()
221 1
            ->clearScope();
222
223 1
        return $output;
224
    }
225
226
    /**
227
     * Get collection of paginated records
228
     *
229
     * @param null $perPage
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $perPage is correct as it would always require null to be passed?
Loading history...
230
     * @param array $columns
231
     *
232
     * @throws RepositoryException
233
     *
234
     * @return mixed
235
     */
236 1
    public function paginate($perPage = null, $columns = ['*'])
237
    {
238 1
        $this->applyCriteria()
239 1
            ->applyScope();
240
241 1
        $perPage = $perPage ?? config('repositories.pagination.per_page', $this->model->getPerPage());
242
243 1
        $results = $this->model
244 1
            ->paginate($perPage, $columns)
245 1
            ->appends(app('request')->query());
246
247 1
        $this->clearModel();
248
249 1
        return $results;
250
    }
251
252
    /**
253
     * Get single or multiple records by their primary ids
254
     *
255
     * @param mixed $id
256
     * @param array $columns
257
     *
258
     * @throws RepositoryException
259
     *
260
     * @return mixed
261
     */
262 2
    public function find($id, $columns = ['*'])
263
    {
264 2
        $this->applyCriteria()
265 2
            ->applyScope();
266
267 2
        $model = $this->model->findOrFail($id, $columns);
268
269 2
        $this->clearModel();
270
271 2
        return $model;
272
    }
273
}
274