Passed
Push — master ( 110432...a914c5 )
by Michael
02:54
created

RelatedPlusTrait::addOrder()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 2
nop 3
1
<?php
2
3
namespace Blasttech\EloquentRelatedPlus;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Relations\Relation;
8
use Illuminate\Database\Query\Expression;
9
use Illuminate\Database\Query\JoinClause;
10
use Illuminate\Support\Facades\DB;
11
use Illuminate\Support\Facades\Schema;
12
13
/**
14
 * Trait RelatedPlusTrait
15
 *
16
 * @property array nullable
17
 * @property array order_fields
18
 * @property array order_defaults
19
 * @property array order_relations
20
 * @property array order_with
21
 * @property array search_fields
22
 * @property string connection
23
 */
24
trait RelatedPlusTrait
25
{
26
    use CustomOrderTrait, HelperMethodTrait, JoinsTrait, SearchTrait;
27
28
    /**
29
     * Boot method for trait
30
     *
31
     */
32
    public static function bootRelatedPlusTrait()
33
    {
34
        static::saving(function ($model) {
35
            if (!empty($model->nullable)) {
36
                /* @var \Illuminate\Database\Eloquent\Model|RelatedPlusTrait|static $model */
37
                $model->setAttributesNull();
38
            }
39
        });
40
    }
41
42
    /**
43
     * Set empty fields to null
44
     */
45
    protected function setAttributesNull()
46
    {
47
        foreach ($this->attributes as $key => $value) {
0 ignored issues
show
Bug Best Practice introduced by
The property attributes does not exist on Blasttech\EloquentRelatedPlus\RelatedPlusTrait. Did you maybe forget to declare it?
Loading history...
48
            if (isset($this->nullable[$key])) {
49
                $this->{$key} = empty(trim($value)) ? null : $value;
50
            }
51
        }
52
    }
53
54
    /**
55
     * Get the table associated with the model.
56
     *
57
     * @return string
58
     */
59
    abstract public function getTable();
60
61
    /**
62
     * Add joins for one or more relations
63
     * This determines the foreign key relations automatically to prevent the need to figure out the columns.
64
     * Usages:
65
     * $query->modelJoin('customers')
66
     * $query->modelJoin('customer.client')
67
     *
68
     * @param Builder $query
69
     * @param string $relationName
70
     * @param string $operator
71
     * @param string $type
72
     * @param bool $where
73
     * @param bool $relatedSelect
74
     * @param string|null $direction
75
     *
76
     * @return Builder
77
     */
78
    public function scopeModelJoin(
79
        Builder $query,
80
        $relationName,
81
        $operator = '=',
82
        $type = 'left',
83
        $where = false,
84
        $relatedSelect = true,
85
        $direction = null
86
    ) {
87
        foreach ($this->parseRelationNames($relationName) as $relation) {
88
            $table = $this->getRelationTables($relation);
89
90
            // Add selects
91
            $query = $this->modelJoinSelects($query, $table, $relatedSelect);
0 ignored issues
show
Bug introduced by
It seems like $query can also be of type Illuminate\Database\Eloquent\Model; however, parameter $query of Blasttech\EloquentRelate...ait::modelJoinSelects() does only seem to accept Illuminate\Database\Eloquent\Builder, maybe add an additional type check? ( Ignorable by Annotation )

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

91
            $query = $this->modelJoinSelects(/** @scrutinizer ignore-type */ $query, $table, $relatedSelect);
Loading history...
92
93
            $query->relationJoin($table, $relation, $operator, $type, $where, $direction);
94
        }
95
96
        return $query;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query also could return the type Illuminate\Database\Eloquent\Model which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
97
    }
98
99
    /**
100
     * Add selects for model join
101
     *
102
     * @param Builder $query
103
     * @param \stdClass $table
104
     * @param bool $relatedSelect
105
     * @return mixed
106
     */
107
    protected function modelJoinSelects($query, $table, $relatedSelect)
108
    {
109
        /** @var Model $query */
110
        if (empty($query->getQuery()->columns)) {
111
            $query->select($this->getTable() . ".*");
112
        }
113
        if ($relatedSelect) {
114
            $query = $this->selectRelated($query, $table);
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Model is incompatible with the type Illuminate\Database\Eloquent\Builder expected by parameter $query of Blasttech\EloquentRelate...sTrait::selectRelated(). ( Ignorable by Annotation )

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

114
            $query = $this->selectRelated(/** @scrutinizer ignore-type */ $query, $table);
Loading history...
115
        }
116
117
        return $query;
118
    }
119
120
    /**
121
     * Add select for related table fields
122
     *
123
     * @param Builder $query
124
     * @param \stdClass $table
125
     * @return Builder
126
     */
127
    public function selectRelated(Builder $query, $table)
128
    {
129
        $connection = $this->connection;
130
131
        foreach (Schema::connection($connection)->getColumnListing($table->name) as $relatedColumn) {
132
            $query->addSelect(
133
                new Expression("`$table->alias`.`$relatedColumn` AS `$table->alias.$relatedColumn`")
134
            );
135
        }
136
137
        return $query;
138
    }
139
140
    /**
141
     * Set the order of a model
142
     *
143
     * @param Builder $query
144
     * @param string $orderField
145
     * @param string $direction
146
     * @return Builder
147
     */
148
    public function scopeOrderByCustom(Builder $query, $orderField, $direction)
149
    {
150
        if ($this->hasOrderFieldsAndDefaults($orderField, $direction)) {
151
            $query = $this->removeGlobalScope($query, 'order');
152
        }
153
154
        return $query->setCustomOrder($orderField, $direction);
155
    }
156
157
    /**
158
     * Use a model method to add columns or joins if in the order options
159
     *
160
     * @param Builder $query
161
     * @param string $order
162
     * @return Builder
163
     */
164
    public function scopeOrderByWith(Builder $query, $order)
165
    {
166
        if (isset($this->order_with[$order])) {
167
            $query = $this->addOrderWith($query, $order);
168
        }
169
170
        if (isset($this->order_fields[$order])) {
171
            $query = $this->addOrderJoin($query, $order);
172
        }
173
174
        return $query;
175
    }
176
177
    /**
178
     * Execute a scope in the order_width settings
179
     *
180
     * @param Builder $query
181
     * @param string $order
182
     * @return Builder
183
     */
184
    protected function addOrderWith(Builder $query, $order)
185
    {
186
        $with = 'with' . $this->order_with[$order];
187
188
        return $query->$with();
189
    }
190
191
    /**
192
     * Add join from order_fields
193
     *
194
     * @param Builder $query
195
     * @param string $order
196
     * @return Builder
197
     */
198
    protected function addOrderJoin(Builder $query, $order)
199
    {
200
        $orderOption = (explode('.', $this->order_fields[$order]))[0];
201
202
        if (isset($this->order_relations[$orderOption])) {
203
            $query->modelJoin(
204
                $this->order_relations[$orderOption],
205
                '=',
206
                'left',
207
                false,
208
                false
209
            );
210
        }
211
212
        return $query;
213
    }
214
215
    /**
216
     * Join a model
217
     *
218
     * @param Builder $query
219
     * @param \stdClass $table
220
     * @param Relation $relation
221
     * @param string $operator
222
     * @param string $type
223
     * @param boolean $where
224
     * @param string $direction
225
     * @return Builder
226
     */
227
    public function scopeRelationJoin(
228
        Builder $query,
229
        $table,
230
        $relation,
231
        $operator,
232
        $type,
233
        $where,
234
        $direction = null
235
    ) {
236
        $fullTableName = $this->getTableWithAlias($table);
237
238
        return $query->join($fullTableName, function (JoinClause $join) use (
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->join($ful...l, null, $type, $where) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
239
            $table,
240
            $relation,
241
            $operator,
242
            $direction
243
        ) {
244
            $this->relationJoinType($relation, $join, $table, $operator, $direction);
245
        }, null, null, $type, $where);
246
    }
247
248
    /**
249
     * Adds a select for a min or max on the given column, depending on direction given
250
     *
251
     * @param Builder $query
252
     * @param string $column
253
     * @param string $direction
254
     * @return Builder
255
     */
256
    public function selectMinMax($query, $column, $direction)
257
    {
258
        $column = $this->addBackticks($column);
259
260
        /** @var Model $query */
261
        if ($direction == 'asc') {
262
            return $query->select(DB::raw('MIN(' . $column . ')'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->select(Il...MIN(' . $column . ')')) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
263
        } else {
264
            return $query->select(DB::raw('MAX(' . $column . ')'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->select(Il...MAX(' . $column . ')')) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
265
        }
266
    }
267
268
    /**
269
     * Add where statements for the model search fields
270
     *
271
     * @param Builder $query
272
     * @param string $searchText
273
     * @return Builder
274
     */
275
    public function scopeSearch(Builder $query, $searchText = '')
276
    {
277
        $searchText = trim($searchText);
278
279
        // If search is set
280
        if ($searchText != "" && $this->hasSearchFields()) {
281
            $query = $this->checkSearchFields($query, $searchText);
282
        }
283
284
        return $query;
285
    }
286
287
    /**
288
     * Switch a query to be a subquery of a model
289
     *
290
     * @param Builder $query
291
     * @param Builder $model
292
     * @return Builder
293
     */
294
    public function scopeSetSubquery(Builder $query, $model)
295
    {
296
        $sql = $this->toSqlWithBindings($model);
297
        $table = $model->getQuery()->from;
298
299
        return $query
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->from(Illu...->select($table . '.*') also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
300
            ->from(DB::raw("({$sql}) as " . $table))
301
            ->select($table . '.*');
302
    }
303
304
    /**
305
     * Set the model order
306
     *
307
     * @param Builder $query
308
     * @param string $column
309
     * @param string $direction
310
     * @return Builder
311
     */
312
    public function scopeSetCustomOrder(Builder $query, $column, $direction)
313
    {
314
        if (isset($this->order_defaults)) {
315
            $column = $this->setOrderColumn($column);
316
            $direction = $this->setOrderDirection($direction);
317
        }
318
319
        return $this->setOrder($query, $column, $direction);
320
    }
321
322
    /**
323
     * Check if column being sorted by is from a related model
324
     *
325
     * @param Builder $query
326
     * @param string $column
327
     * @param string $direction
328
     * @return Builder
329
     */
330
    public function scopeOrderByCheckModel(Builder $query, $column, $direction)
331
    {
332
        /** @var Model $query */
333
        $query->orderBy(DB::raw($column), $direction);
334
335
        if (isset($this->order_relations) && (strpos($column,
336
                    '.') !== false || isset($this->order_relations[$column]))) {
337
            $query = $this->joinRelatedTable($query, $this->getTableFromColumn($column));
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Model is incompatible with the type Illuminate\Database\Eloquent\Builder expected by parameter $query of Blasttech\EloquentRelate...ait::joinRelatedTable(). ( Ignorable by Annotation )

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

337
            $query = $this->joinRelatedTable(/** @scrutinizer ignore-type */ $query, $this->getTableFromColumn($column));
Loading history...
338
        }
339
340
        return $query;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query also could return the type Illuminate\Database\Eloquent\Model which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
341
    }
342
}
343