EloquentRepository   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 322
Duplicated Lines 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
eloc 94
c 5
b 1
f 0
dl 0
loc 322
rs 9.76
wmc 33

19 Methods

Rating   Name   Duplication   Size   Complexity  
A eagerLoadRelations() 0 6 2
A cleanUnfillableFields() 0 5 1
A create() 0 9 1
A updateBy() 0 23 3
A applyCriteria() 0 11 4
A setCurrentPage() 0 8 1
A paginate() 0 9 1
A findOneBy() 0 12 2
A count() 0 8 1
A findAllBy() 0 12 3
A destroy() 0 14 3
A skipCriteria() 0 4 1
A resetScope() 0 6 1
A delete() 0 14 3
A __construct() 0 10 1
A addCriteria() 0 5 1
A findAllWhereIn() 0 9 1
A with() 0 7 2
A findAll() 0 9 1
1
<?php
2
3
namespace OkayBueno\Repositories\src;
4
5
use OkayBueno\Repositories\Criteria\CriteriaInterface;
6
use OkayBueno\Repositories\Criteria\Eloquent\With;
7
use OkayBueno\Repositories\RepositoryInterface;
8
use Illuminate\Database\Eloquent\Model;
9
10
/**
11
 * Class EloquentRepository
12
 * @package OkayBueno\Repositories\src
13
 */
14
abstract class EloquentRepository implements RepositoryInterface
15
{
16
17
    protected $model;
18
    protected $with;
19
    protected $skipCriteria;
20
    protected $criteria;
21
    private $modelClassName;
22
23
    /**
24
     * EloquentRepository constructor.
25
     * @param Model $model
26
     * @throws \ReflectionException
27
     */
28
    public function __construct( Model $model )
29
    {
30
        $this->model = $model;
31
32
        // A clean copy of the model is needed when the scope needs to be reset.
33
        $reflex = new \ReflectionClass( $model );
34
        $this->modelClassName = $reflex->getName();
35
36
        $this->skipCriteria = FALSE;
37
        $this->criteria = [];
38
    }
39
40
    /**
41
     * @param $value
42
     * @param string $field
43
     * @param array $columns
44
     * @return mixed
45
     */
46
    public function findOneBy($value = NULL, $field = 'id', array $columns = ['*'])
47
    {
48
        $this->eagerLoadRelations();
49
        $this->applyCriteria();
50
51
        if ( !is_null( $value ) ) $this->model = $this->model->where( $field, $value );
52
53
        $result = $this->model->first( $columns );
54
55
        $this->resetScope();
56
57
        return $result;
58
    }
59
60
    /**
61
     * @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...
62
     * @param null $field
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $field is correct as it would always require null to be passed?
Loading history...
63
     * @param array $columns
64
     * @return mixed
65
     */
66
    public function findAllBy($value = NULL, $field = NULL, array $columns = ['*'])
67
    {
68
        $this->eagerLoadRelations();
69
        $this->applyCriteria();
70
71
        if ( !is_null( $value ) && !is_null( $field ) ) $this->model = $this->model->where( $field, $value );
0 ignored issues
show
introduced by
The condition is_null($value) is always true.
Loading history...
72
73
        $result = $this->model->get( $columns );
74
75
        $this->resetScope();
76
77
        return $result;
78
    }
79
80
    /**
81
     * @param array $value
82
     * @param string $field
83
     * @param array $columns
84
     * @return mixed
85
     */
86
    public function findAllWhereIn(array $value, $field, array $columns = ['*'])
87
    {
88
        $this->eagerLoadRelations();
89
        $this->applyCriteria();
90
        $result = $this->model->whereIn( $field, $value )->get( $columns );
91
92
        $this->resetScope();
93
94
        return $result;
95
    }
96
97
    /**
98
     * @param array $columns
99
     * @return \Illuminate\Database\Eloquent\Collection|static[]
100
     */
101
    public function findAll( array $columns = ['*'] )
102
    {
103
        $this->eagerLoadRelations();
104
        $this->applyCriteria();
105
        $result = $this->model->all( $columns );
106
107
        $this->resetScope();
108
109
        return $result;
110
    }
111
112
    /**
113
     * @param array|string $relations
114
     * @return $this
115
     */
116
    public function with( $relations )
117
    {
118
        if ( is_string( $relations ) ) $relations = func_get_args();
119
120
        $this->with = $relations;
121
122
        return $this;
123
    }
124
125
126
    /**
127
     * @param CriteriaInterface $criteria
128
     * @return $this
129
     */
130
    public function addCriteria( CriteriaInterface $criteria)
131
    {
132
        $this->criteria[] = $criteria;
133
134
        return $this;
135
    }
136
137
138
    /**
139
     * @param bool $status
140
     * @return $this
141
     */
142
    public function skipCriteria( $status = TRUE )
143
    {
144
        $this->skipCriteria = $status;
145
        return $this;
146
    }
147
148
149
    /**
150
     * @param int $perPage
151
     * @param array $columns
152
     * @return mixed
153
     */
154
    public function paginate($perPage, array $columns = ['*'])
155
    {
156
        $this->eagerLoadRelations();
157
        $this->applyCriteria();
158
        $result = $this->model->paginate( $perPage, $columns );
159
160
        $this->resetScope();
161
162
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result returns the type Illuminate\Pagination\LengthAwarePaginator which is incompatible with the return type mandated by OkayBueno\Repositories\R...ryInterface::paginate() of OkayBueno\Repositories\Paginator.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
163
    }
164
165
166
    /**
167
     * @param int $currentPage
168
     * @return $this
169
     */
170
    public function setCurrentPage( $currentPage )
171
    {
172
        \Illuminate\Pagination\Paginator::currentPageResolver(function() use ( $currentPage )
173
        {
174
            return $currentPage;
175
        });
176
177
        return $this;
178
    }
179
180
181
    /**
182
     * @param array $data
183
     * @return mixed
184
     */
185
    public function create(array $data)
186
    {
187
        $cleanFields = $this->cleanUnfillableFields( $data );
188
189
        $createdObject = $this->model->create( $cleanFields );
190
191
        $this->resetScope();
192
193
        return $createdObject;
194
    }
195
196
    /**
197
     * @param array $data
198
     * @param $value
199
     * @param string $field
200
     * @return mixed
201
     */
202
    public function updateBy(array $data, $value = NULL, $field = 'id')
203
    {
204
        $cleanFields = $this->cleanUnfillableFields( $data );
205
206
        if ( !is_null( $value ) )
207
        {
208
            // Single update.
209
            $this->model->where( $field, $value)->update( $cleanFields );
210
211
            foreach( $cleanFields as $F => $V ) $this->model->{$F} = $V;
212
213
            $returnedVal = $this->model;
214
        } else
215
        {
216
            // Mass update.
217
            $this->applyCriteria();
218
219
            $returnedVal = $this->model->update( $cleanFields );
220
        }
221
222
        $this->resetScope();
223
224
        return $returnedVal;
225
    }
226
227
    /**
228
     * @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...
229
     * @param string $field
230
     * @return bool
231
     * @throws \Exception
232
     */
233
    public function delete( $value = null, $field = 'id' )
234
    {
235
        $this->applyCriteria();
236
237
        if ( !is_null( $value ) ) $result = $this->model->where( $field, $value )->delete();
0 ignored issues
show
introduced by
The condition is_null($value) is always true.
Loading history...
238
        else
239
        {
240
            if ( !empty( $this->criteria ) ) $result = $this->model->delete();
241
            else $result = FALSE;
242
        }
243
244
        $this->resetScope();
245
246
        return (bool)$result;
247
    }
248
249
    /**
250
     * @return mixed
251
     */
252
    public function count()
253
    {
254
        $this->applyCriteria();
255
        $result = $this->model->count();
256
257
        $this->resetScope();
258
259
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the return type mandated by OkayBueno\Repositories\R...itoryInterface::count() of integer.
Loading history...
260
    }
261
262
    /**
263
     * @return $this
264
     */
265
    public function resetScope()
266
    {
267
        $this->criteria = [];
268
        $this->skipCriteria( FALSE );
269
        $this->model = new $this->modelClassName();
270
        return $this;
271
    }
272
273
    /**
274
     * @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...
275
     * @param string $field
276
     * @return mixed
277
     */
278
    public function destroy($value = null, $field = 'id')
279
    {
280
        $this->applyCriteria();
281
282
        if ( !is_null( $value ) ) $result = $this->model->where( $field, $value )->forceDelete();
0 ignored issues
show
introduced by
The condition is_null($value) is always true.
Loading history...
283
        else
284
        {
285
            if ( !empty( $this->criteria ) ) $result = $this->model->forceDelete();
286
            else $result = FALSE;
287
        }
288
289
        $this->resetScope();
290
291
        return (bool)$result;
292
    }
293
294
295
    /*******************************************************************************************************************
296
     *******************************************************************************************************************
297
     *******************************************************************************************************************/
298
    /**
299
     *
300
     */
301
    protected function eagerLoadRelations()
302
    {
303
        if ( is_array( $this->with ) )
304
        {
305
            $with = new With( $this->with );
306
            $this->addCriteria( $with );
307
        }
308
    }
309
310
311
    /**
312
     * @param array $data
313
     * @return array
314
     */
315
    protected function cleanUnfillableFields( array $data )
316
    {
317
        return array_filter($data, function ($key) {
318
            return $this->model->isFillable($key);
319
        }, ARRAY_FILTER_USE_KEY);
320
    }
321
322
    /**
323
     * @return $this
324
     */
325
    protected function applyCriteria()
326
    {
327
        if( !$this->skipCriteria )
328
        {
329
            foreach( $this->criteria as $criteria )
330
            {
331
                if( $criteria instanceof CriteriaInterface ) $this->model = $criteria->apply( $this->model, $this );
0 ignored issues
show
Unused Code introduced by
The call to OkayBueno\Repositories\C...teriaInterface::apply() has too many arguments starting with $this. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

331
                if( $criteria instanceof CriteriaInterface ) /** @scrutinizer ignore-call */ $this->model = $criteria->apply( $this->model, $this );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
332
            }
333
        }
334
335
        return $this;
336
    }
337
}
338