RelatedPlusTrait::selectRelated()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 3
b 0
f 0
nc 2
nop 2
dl 0
loc 11
rs 10
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\BelongsTo;
8
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
9
use Illuminate\Database\Query\Expression;
10
use Illuminate\Database\Query\JoinClause;
11
use Illuminate\Support\Facades\DB;
12
use Illuminate\Support\Facades\Schema;
13
14
/**
15
 * Trait RelatedPlusTrait
16
 *
17
 * @property array attributes
18
 * @property array nullable
19
 * @property array orderFields
20
 * @property array orderDefaults
21
 * @property array orderRelations
22
 * @property array orderWith
23
 * @property array searchFields
24
 * @property string connection
25
 * @method Model getModel()
26
 * @method string getTable()
27
 */
28
trait RelatedPlusTrait
29
{
30
    use CustomOrderTrait, SearchTrait, HelpersTrait;
31
32
    /**
33
     * Boot method for trait
34
     *
35
     */
36
    public static function bootRelatedPlusTrait()
37
    {
38
        static::saving(function ($model) {
39
            if (!empty($model->nullable)) {
40
                /* @var \Illuminate\Database\Eloquent\Model|RelatedPlusTrait|static $model */
41
                $model->setAttributesNull();
42
            }
43
        });
44
    }
45
46
47
    /**
48
     * Get the search fields for the model
49
     *
50
     * @return array
51
     */
52
    public function getSearchFields()
53
    {
54
        return $this->hasSearchFields() ? $this->searchFields : [];
55
    }
56
57
    /**
58
     * Set the search fields for the model
59
     *
60
     * @param array $searchFields
61
     */
62
    public function setSearchFields(array $searchFields)
63
    {
64
        $this->searchFields = $searchFields;
65
    }
66
67
    /**
68
     * Add joins for one or more relations
69
     * This determines the foreign key relations automatically to prevent the need to figure out the columns.
70
     * Usages:
71
     * $query->modelJoin('customers')
72
     * $query->modelJoin('customer.client')
73
     *
74
     * @param Builder $query
75
     * @param string $relationName
76
     * @param string $operator
77
     * @param string $type
78
     * @param bool $where
79
     * @param bool $relatedSelect
80
     * @param string|null $direction
81
     *
82
     * @return Builder
83
     */
84
    public function scopeModelJoin(
85
        Builder $query,
86
        $relationName,
87
        $operator = '=',
88
        $type = 'left',
89
        $where = false,
90
        $relatedSelect = true,
91
        $direction = null
92
    ) {
93
        foreach ($this->parseRelationNames($this->getModel(), $relationName) as $relation) {
94
            // Add selects
95
            $query = $this->modelJoinSelects($query, $relation, $relatedSelect);
96
97
            $query->relationJoin($relation, $operator, $type, $where, $direction);
98
        }
99
100
        return $query;
101
    }
102
103
    /**
104
     * Get the relations from a relation name
105
     * $relationName can be a single relation
106
     * Usage for User model:
107
     * parseRelationNames('customer') returns [$user->customer()]
108
     * parseRelationNames('customer.contact') returns [$user->customer(), $user->customer->contact()]
109
     *
110
     * @param Model $model
111
     * @param string $relationNameString
112
     * @return RelationPlus[]
113
     */
114
    protected function parseRelationNames($model, $relationNameString)
115
    {
116
        $relationNames = explode('.', $relationNameString);
117
        $parentRelation = null;
118
        $relations = [];
119
120
        foreach ($relationNames as $relationName) {
121
            $relation = $this->getRelationFromName($model, $parentRelation, $relationName);
122
            $relations[] = new RelationPlus($relation);
123
            $parentRelation = $relation->getModel();
124
        }
125
126
        return $relations;
127
    }
128
129
    /**
130
     * @param Model $model
131
     * @param BelongsTo|HasOneOrMany|null $parentRelation
132
     * @param string $relationName
133
     * @return BelongsTo|HasOneOrMany
134
     */
135
    protected function getRelationFromName($model, $parentRelation, $relationName)
136
    {
137
        if (is_null($parentRelation)) {
138
            return $model->$relationName();
139
        }
140
141
        return $parentRelation->$relationName();
142
    }
143
144
    /**
145
     * Add selects for model join
146
     *
147
     * @param Builder $query
148
     * @param RelationPlus $relation
149
     * @param bool $relatedSelect
150
     * @return mixed
151
     */
152
    protected function modelJoinSelects($query, $relation, $relatedSelect)
153
    {
154
        if (empty($query->getQuery()->columns)) {
155
            $query->select($this->getTable() . ".*");
156
        }
157
158
        if ($relatedSelect) {
159
            $query = $this->selectRelated($query, $relation);
160
        }
161
162
        return $query;
163
    }
164
165
    /**
166
     * Add select for related table fields
167
     *
168
     * @param Builder $query
169
     * @param RelationPlus $relation
170
     * @return Builder
171
     */
172
    protected function selectRelated(Builder $query, $relation)
173
    {
174
        $connection = $this->connection;
175
176
        foreach (Schema::connection($connection)->getColumnListing($relation->tableName) as $relatedColumn) {
177
            $query->addSelect(
178
                new Expression("`$relation->tableAlias`.`$relatedColumn` AS `$relation->tableAlias.$relatedColumn`")
179
            );
180
        }
181
182
        return $query;
183
    }
184
185
    /**
186
     * Set the order of a model
187
     *
188
     * @param Builder $query
189
     * @param string $orderField
190
     * @param string $direction
191
     * @return Builder
192
     */
193
    public function scopeOrderByCustom(Builder $query, $orderField, $direction)
194
    {
195
        if ($this->hasOrderFieldsAndDefaults($orderField, $direction)) {
196
            $query = $this->removeGlobalScopes($this->getModel(), $query, 'order');
197
        }
198
199
        return $query->setCustomOrder($orderField, $direction);
200
    }
201
202
    /**
203
     * Use a model method to add columns or joins if in the order options
204
     *
205
     * @param Builder $query
206
     * @param string $order
207
     * @return Builder
208
     */
209
    public function scopeOrderByWith(Builder $query, $order)
210
    {
211
        if (isset($this->orderWith[$order])) {
212
            $query = $this->addOrderWith($query, $order);
213
        }
214
215
        if (isset($this->orderFields[$order])) {
216
            $query = $this->addOrderJoin($query, $order);
217
        }
218
219
        return $query;
220
    }
221
222
    /**
223
     * Join a model
224
     *
225
     * @param Builder $query
226
     * @param RelationPlus $relation
227
     * @param string $operator
228
     * @param string $type
229
     * @param boolean $where
230
     * @param string $direction
231
     * @return Builder|\Illuminate\Database\Query\Builder
232
     */
233
    public function scopeRelationJoin(
234
        Builder $query,
235
        $relation,
236
        $operator,
237
        $type,
238
        $where,
239
        $direction = null
240
    ) {
241
        $fullTableName = $relation->getTableWithAlias();
242
243
        return $query->join($fullTableName, function (JoinClause $join) use (
244
            $relation,
245
            $operator,
246
            $direction
247
        ) {
248
            return $relation->getRelationJoin($join, $operator, $direction);
249
        }, null, null, $type, $where);
250
    }
251
252
    /**
253
     * Add where statements for the model search fields
254
     *
255
     * @param Builder $query
256
     * @param string $searchText
257
     * @return Builder
258
     */
259
    public function scopeSearch(Builder $query, $searchText = '')
260
    {
261
        $searchText = trim($searchText);
262
263
        // If search is set
264
        if ($searchText != "" && $this->hasSearchFields()) {
265
            $query = $this->checkSearchFields($query, $searchText);
266
        }
267
268
        return $query;
269
    }
270
271
    /**
272
     * Switch a query to be a subquery of a model
273
     *
274
     * @param Builder $query
275
     * @param Builder $model
276
     * @return Builder|\Illuminate\Database\Query\Builder
277
     */
278
    public function scopeSetSubquery(Builder $query, $model)
279
    {
280
        $sql = $this->toSqlWithBindings($model);
281
        $table = $model->getQuery()->from;
282
283
        return $query
284
            ->from(DB::raw("({$sql}) as " . $table))
285
            ->select($table . '.*');
286
    }
287
288
    /**
289
     * Set the model order
290
     *
291
     * @param Builder $query
292
     * @param string $column
293
     * @param string $direction
294
     * @return Builder
295
     */
296
    public function scopeSetCustomOrder(Builder $query, $column, $direction)
297
    {
298
        if (isset($this->orderDefaults)) {
299
            $column = $this->setOrderColumn($column);
300
            $direction = $this->setOrderDirection($direction);
301
        }
302
303
        return $this->setOrder($query, $column, $direction);
304
    }
305
306
    /**
307
     * Check if column being sorted by is from a related model
308
     *
309
     * @param Builder $query
310
     * @param string $column
311
     * @param string $direction
312
     * @return Builder
313
     */
314
    public function scopeOrderByCheckModel(Builder $query, $column, $direction)
315
    {
316
        $query->orderBy(DB::raw($column), $direction);
317
318
        if (isset($this->orderRelations) && (strpos($column, '.') !== false ||
319
                isset($this->orderRelations[$column]))) {
320
            $query = $this->joinRelatedTable($query, $this->getTableFromColumn($column));
321
        }
322
323
        return $query;
324
    }
325
}
326