Passed
Push — master ( a914c5...479ccf )
by Michael
02:27
created

RelatedPlusTrait   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 296
rs 9.6
c 0
b 0
f 0
wmc 32

14 Methods

Rating   Name   Duplication   Size   Complexity  
A addOrderWith() 0 5 1
A scopeRelationJoin() 0 19 1
A addOrderJoin() 0 15 2
A selectRelated() 0 11 2
A scopeOrderByCustom() 0 7 2
A scopeOrderByWith() 0 11 3
A bootRelatedPlusTrait() 0 6 2
A scopeSetCustomOrder() 0 8 2
A scopeModelJoin() 0 19 2
A scopeSearch() 0 10 3
A scopeSetSubquery() 0 8 1
A scopeOrderByCheckModel() 0 10 4
A setAttributesNull() 0 6 4
A modelJoinSelects() 0 10 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
        /** @var Model $this */
48
        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...
49
            if (isset($this->nullable[$key])) {
50
                $this->{$key} = empty(trim($value)) ? null : $value;
51
            }
52
        }
53
    }
54
55
    /**
56
     * Get the table associated with the model.
57
     *
58
     * @return string
59
     */
60
    abstract public function getTable();
61
62
    /**
63
     * Add joins for one or more relations
64
     * This determines the foreign key relations automatically to prevent the need to figure out the columns.
65
     * Usages:
66
     * $query->modelJoin('customers')
67
     * $query->modelJoin('customer.client')
68
     *
69
     * @param Builder $query
70
     * @param string $relationName
71
     * @param string $operator
72
     * @param string $type
73
     * @param bool $where
74
     * @param bool $relatedSelect
75
     * @param string|null $direction
76
     *
77
     * @return Builder
78
     */
79
    public function scopeModelJoin(
80
        Builder $query,
81
        $relationName,
82
        $operator = '=',
83
        $type = 'left',
84
        $where = false,
85
        $relatedSelect = true,
86
        $direction = null
87
    ) {
88
        foreach ($this->parseRelationNames($relationName) as $relation) {
89
            $table = $this->getRelationTables($relation);
90
91
            // Add selects
92
            $query = $this->modelJoinSelects($query, $table, $relatedSelect);
93
94
            $query->relationJoin($table, $relation, $operator, $type, $where, $direction);
95
        }
96
97
        return $query;
98
    }
99
100
    /**
101
     * Add selects for model join
102
     *
103
     * @param Builder $query
104
     * @param \stdClass $table
105
     * @param bool $relatedSelect
106
     * @return mixed
107
     */
108
    protected function modelJoinSelects($query, $table, $relatedSelect)
109
    {
110
        if (empty($query->getQuery()->columns)) {
111
            $query->select($this->getTable() . ".*");
112
        }
113
        if ($relatedSelect) {
114
            $query = $this->selectRelated($query, $table);
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
     * Add where statements for the model search fields
250
     *
251
     * @param Builder $query
252
     * @param string $searchText
253
     * @return Builder
254
     */
255
    public function scopeSearch(Builder $query, $searchText = '')
256
    {
257
        $searchText = trim($searchText);
258
259
        // If search is set
260
        if ($searchText != "" && $this->hasSearchFields()) {
261
            $query = $this->checkSearchFields($query, $searchText);
262
        }
263
264
        return $query;
265
    }
266
267
    /**
268
     * Switch a query to be a subquery of a model
269
     *
270
     * @param Builder $query
271
     * @param Builder $model
272
     * @return Builder
273
     */
274
    public function scopeSetSubquery(Builder $query, $model)
275
    {
276
        $sql = $this->toSqlWithBindings($model);
277
        $table = $model->getQuery()->from;
278
279
        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...
280
            ->from(DB::raw("({$sql}) as " . $table))
281
            ->select($table . '.*');
282
    }
283
284
    /**
285
     * Set the model order
286
     *
287
     * @param Builder $query
288
     * @param string $column
289
     * @param string $direction
290
     * @return Builder
291
     */
292
    public function scopeSetCustomOrder(Builder $query, $column, $direction)
293
    {
294
        if (isset($this->order_defaults)) {
295
            $column = $this->setOrderColumn($column);
296
            $direction = $this->setOrderDirection($direction);
297
        }
298
299
        return $this->setOrder($query, $column, $direction);
300
    }
301
302
    /**
303
     * Check if column being sorted by is from a related model
304
     *
305
     * @param Builder $query
306
     * @param string $column
307
     * @param string $direction
308
     * @return Builder
309
     */
310
    public function scopeOrderByCheckModel(Builder $query, $column, $direction)
311
    {
312
        $query->orderBy(DB::raw($column), $direction);
313
314
        if (isset($this->order_relations) && (strpos($column,
315
                    '.') !== false || isset($this->order_relations[$column]))) {
316
            $query = $this->joinRelatedTable($query, $this->getTableFromColumn($column));
317
        }
318
319
        return $query;
320
    }
321
}
322