Passed
Push — master ( 5940d6...768f3e )
by Michael
02:35
created

RelatedPlusTrait::hasOneJoinSql()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
c 0
b 0
f 0
rs 9.4285
cc 1
eloc 9
nc 1
nop 2
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\HasMany;
9
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use Illuminate\Database\Query\Expression;
12
use Illuminate\Database\Query\JoinClause;
13
use Illuminate\Support\Facades\DB;
14
use Illuminate\Support\Facades\Schema;
15
use InvalidArgumentException;
16
17
/**
18
 * Trait RelatedPlusTrait
19
 *
20
 * @property array order_fields
21
 * @property array order_defaults
22
 * @property array order_relations
23
 * @property array order_with
24
 *
25
 * @package Blasttech\WherePlus
26
 */
27
trait RelatedPlusTrait
28
{
29
    /**
30
     * Boot method for trait
31
     *
32
     */
33
    public static function bootRelatedPlusTrait()
34
    {
35
        static::saving(function ($model) {
36
            if (!empty($model->nullable)) {
37
                foreach ($model->attributes as $key => $value) {
38
                    if (isset($model->nullable[$key])) {
39
                        $model->{$key} = empty(trim($value)) ? null : $value;
40
                    }
41
                }
42
            }
43
        });
44
    }
45
46
    /**
47
     * Add joins for one or more relations
48
     * This determines the foreign key relations automatically to prevent the need to figure out the columns.
49
     * Usages:
50
     * $query->modelJoin('customers')
51
     * $query->modelJoin('customer.client')
52
     *
53
     * @param Builder $query
54
     * @param string $relationName
55
     * @param string $operator
56
     * @param string $type
57
     * @param bool $where
58
     * @param bool $relatedSelect
59
     * @param string|null $direction
60
     *
61
     * @return Builder
62
     */
63
    public function scopeModelJoin(
64
        Builder $query,
65
        $relationName,
66
        $operator = '=',
67
        $type = 'left',
68
        $where = false,
69
        $relatedSelect = true,
70
        $direction = null
71
    ) {
72
        $connection = $this->connection;
0 ignored issues
show
Bug introduced by
The property connection does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
73
74
        foreach ($this->parseRelationNames($relationName) as $relation) {
75
            $tableName = $relation->getRelated()->getTable();
76
            // if using a 'table' AS 'tableAlias' in a from statement, otherwise alias will be the table name
77
            $from = explode(' ', $relation->getQuery()->getQuery()->from);
78
            $tableAlias = array_pop($from);
79
80
            if (empty($query->getQuery()->columns)) {
81
                $query->select($this->getTable() . ".*");
0 ignored issues
show
Bug introduced by
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
Bug introduced by
The method select() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean createSelectWithConstraint()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
82
            }
83
            if ($relatedSelect) {
84
                foreach (Schema::connection($connection)->getColumnListing($tableName) as $relatedColumn) {
85
                    $query->addSelect(
86
                        new Expression("`$tableAlias`.`$relatedColumn` AS `$tableAlias.$relatedColumn`")
87
                    );
88
                }
89
            }
90
            $query->relationJoin($tableName, $tableAlias, $relation, $operator, $type, $where, $direction);
91
        }
92
93
        return $query;
94
    }
95
96
    /**
97
     * Get the relations from a relation name
98
     * $relationName can be a single relation
99
     * Usage for User model:
100
     * parseRelationNames('customer') returns [$user->customer()]
101
     * parseRelationNames('customer.contact') returns [$user->customer(), $user->customer->contact()]
102
     *
103
     * @param string $relationName
104
     * @return Relation[]
105
     */
106
    protected function parseRelationNames($relationName)
107
    {
108
        $relationNames = explode('.', $relationName);
109
        $parentRelationName = null;
110
        $relations = [];
111
112
        foreach ($relationNames as $relationName) {
113
            if (is_null($parentRelationName)) {
114
                $relations[] = $this->$relationName();
115
                $parentRelationName = $this->$relationName()->getRelated();
116
            } else {
117
                $relations[] = $parentRelationName->$relationName();
118
            }
119
        }
120
121
        return $relations;
122
    }
123
124
    /**
125
     * Join a model
126
     *
127
     * @param Builder $query
128
     * @param string $tableName
129
     * @param string $tableAlias
130
     * @param Relation $relation
131
     * @param string $operator
132
     * @param string $type
133
     * @param boolean $where
134
     * @param null $direction
135
     * @return Builder
136
     */
137
    public function scopeRelationJoin(
138
        Builder $query,
139
        $tableName,
140
        $tableAlias,
141
        $relation,
142
        $operator,
143
        $type,
144
        $where,
145
        $direction = null
146
    ) {
147
        if ($tableAlias !== '' && $tableName !== $tableAlias) {
148
            $fullTableName = $tableName . ' AS ' . $tableAlias;
149
        } else {
150
            $fullTableName = $tableName;
151
        }
152
153
        return $query->join($fullTableName, function (JoinClause $join) use (
154
            $tableName,
155
            $tableAlias,
156
            $relation,
157
            $operator,
158
            $direction
159
        ) {
160
            // If a HasOne relation and ordered - ie join to the latest/earliest
161
            if (class_basename($relation) === 'HasOne' && !empty($relation->toBase()->orders)) {
162
                // Get first relation order (should only be one)
163
                $order = $relation->toBase()->orders[0];
164
165
                return $join->on($order['column'], $this->hasOneJoinSql($relation, $order));
166
            } else {
167
                // Get relation join columns
168
                $joinColumns = $this->getJoinColumns($relation);
169
170
                $first = $joinColumns->first;
171
                $second = $joinColumns->second;
172
                if ($tableName !== $tableAlias) {
173
                    $first = str_replace($tableName, $tableAlias, $first);
174
                    $second = str_replace($tableName, $tableAlias, $second);
175
                }
176
177
                $join->on($first, $operator, $second);
178
179
                // Add any where clauses from the relationship
180
                $join = $this->addWhereConstraints($join, $relation, $tableAlias);
181
182
                if (!is_null($direction) && get_class($relation) === HasMany::class) {
183
                    $join = $this->hasManyJoin($join, $first, $relation, $tableAlias, $direction);
0 ignored issues
show
Bug introduced by
It seems like $join can also be of type object<Illuminate\Database\Eloquent\Builder>; however, Blasttech\EloquentRelate...lusTrait::hasManyJoin() does only seem to accept object<Illuminate\Database\Query\JoinClause>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
184
                }
185
186
                return $join;
187
            }
188
        }, null, null, $type, $where);
189
    }
190
191
    /**
192
     * Get join sql for a HasOne relation
193
     *
194
     * @param Relation $relation
195
     * @param array $order
196
     * @return Expression
197
     */
198
    public function hasOneJoinSql($relation, $order)
199
    {
200
        // Build subquery for getting first/last record in related table
201
        $subQuery = $this
202
            ->joinOne(
203
                $relation->getRelated()->newQuery(),
204
                $relation,
205
                $order['column'],
206
                $order['direction']
207
            )
208
            ->setBindings($relation->getBindings());
209
210
        return DB::raw('(' . $this->toSqlWithBindings($subQuery) . ')');
211
    }
212
213
    /**
214
     * Adds a where for a relation's join columns and and min/max for a given column
215
     *
216
     * @param Builder $query
217
     * @param Relation $relation
218
     * @param string $column
219
     * @param string $direction
220
     * @return Builder
221
     */
222
    public function joinOne($query, $relation, $column, $direction)
223
    {
224
        // Get join fields
225
        $joinColumns = $this->getJoinColumns($relation);
226
227
        return $this->selectMinMax(
228
            $query->whereColumn($joinColumns->first, '=', $joinColumns->second),
229
            $column,
230
            $direction
231
        );
232
    }
233
234
    /**
235
     * Get the join columns for a relation
236
     *
237
     * @param Relation|BelongsTo|HasOneOrMany $relation
238
     * @return \stdClass
239
     */
240
    protected function getJoinColumns($relation)
241
    {
242
        // Get keys with table names
243
        if ($relation instanceof BelongsTo) {
244
            $first = $relation->getOwnerKey();
245
            $second = $relation->getForeignKey();
246
        } else {
247
            $first = $relation->getQualifiedParentKeyName();
248
            $second = $relation->getQualifiedForeignKeyName();
249
        }
250
251
        return (object)['first' => $first, 'second' => $second];
252
    }
253
254
    /**
255
     * Adds a select for a min or max on the given column, depending on direction given
256
     *
257
     * @param Builder $query
258
     * @param string $column
259
     * @param string $direction
260
     * @return Builder
261
     */
262
    public function selectMinMax($query, $column, $direction)
263
    {
264
        $column = $this->addBackticks($column);
265
266
        if ($direction == 'asc') {
267
            return $query->select(DB::raw('MIN(' . $column . ')'));
0 ignored issues
show
Bug introduced by
The method select() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean createSelectWithConstraint()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
268
        } else {
269
            return $query->select(DB::raw('MAX(' . $column . ')'));
0 ignored issues
show
Bug introduced by
The method select() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean createSelectWithConstraint()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
270
        }
271
    }
272
273
    /**
274
     * Add backticks to a table/column
275
     *
276
     * @param string $column
277
     * @return string
278
     */
279
    private function addBackticks($column)
280
    {
281
        return preg_match('/^[0-9a-zA-Z\.]*$/', $column) ?
282
            '`' . str_replace(['`', '.'], ['', '`.`'], $column) . '`' : $column;
283
    }
284
285
    /**
286
     * Return the sql for a query with the bindings replaced with the binding values
287
     *
288
     * @param Builder $builder
289
     * @return string
290
     */
291
    private function toSqlWithBindings(Builder $builder)
292
    {
293
        return vsprintf($this->replacePlaceholders($builder), array_map('addslashes', $builder->getBindings()));
294
    }
295
296
    /**
297
     * Replace SQL placeholders with '%s'
298
     *
299
     * @param Builder $builder
300
     * @return string
301
     */
302
    private function replacePlaceholders(Builder $builder)
303
    {
304
        return str_replace(['?'], ['\'%s\''], $builder->toSql());
305
    }
306
307
    /**
308
     * Add wheres if they exist for a relation
309
     *
310
     * @param Builder|JoinClause $builder
311
     * @param Relation|BelongsTo|HasOneOrMany $relation
312
     * @param string $table
313
     * @return Builder|JoinClause
314
     */
315
    protected function addWhereConstraints($builder, $relation, $table)
316
    {
317
        // Get where clauses from the relationship
318
        $wheres = collect($relation->toBase()->wheres)
319
            ->where('type', 'Basic')
320
            ->map(function ($where) use ($table) {
321
                // Add table name to column if it is absent
322
                return [$this->columnWithTableName($table, $where['column']), $where['operator'], $where['value']];
323
            })->toArray();
324
325
        if (!empty($wheres)) {
326
            $builder->where($wheres);
327
        }
328
329
        return $builder;
330
    }
331
332
    /**
333
     * Add table name to column name if table name not already included in column name
334
     *
335
     * @param string $table
336
     * @param string $column
337
     * @return string
338
     */
339
    private function columnWithTableName($table, $column)
340
    {
341
        return (preg_match('/(' . $table . '\.|`' . $table . '`)/i', $column) > 0 ? '' : $table . '.') . $column;
342
    }
343
344
    /**
345
     * If the relation is one-to-many, just get the first related record
346
     *
347
     * @param JoinClause $joinClause
348
     * @param string $column
349
     * @param HasMany|Relation $relation
350
     * @param string $table
351
     * @param string $direction
352
     *
353
     * @return JoinClause
354
     */
355
    public function hasManyJoin(JoinClause $joinClause, $column, $relation, $table, $direction)
356
    {
357
        return $joinClause->where(
358
            $column,
359
            function ($subQuery) use ($table, $direction, $relation, $column) {
360
                $subQuery = $this->joinOne(
361
                    $subQuery->from($table),
362
                    $relation,
363
                    $column,
364
                    $direction
365
                );
366
367
                // Add any where statements with the relationship
368
                $subQuery = $this->addWhereConstraints($subQuery, $relation, $table);
369
370
                // Add any order statements with the relationship
371
                return $this->addOrder($subQuery, $relation, $table);
372
            }
373
        );
374
    }
375
376
    /**
377
     * Add orderBy if orders exist for a relation
378
     *
379
     * @param Builder|JoinClause $builder
380
     * @param Relation|BelongsTo|HasOneOrMany $relation
381
     * @param string $table
382
     * @return Builder
383
     */
384
    protected function addOrder($builder, $relation, $table)
385
    {
386
        if (!empty($relation->toBase()->orders)) {
387
            // Get where clauses from the relationship
388
            foreach ($relation->toBase()->orders as $order) {
389
                $builder->orderBy($this->columnWithTableName($table, $order['column']), $order['direction']);
0 ignored issues
show
Bug introduced by
The method orderBy does only exist in Illuminate\Database\Query\JoinClause, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
390
            }
391
        }
392
393
        return $builder;
394
    }
395
396
    /**
397
     * Set the order of a model
398
     *
399
     * @param Builder $query
400
     * @param string $orderField
401
     * @param string $dir
402
     * @return Builder
403
     */
404
    public function scopeOrderByCustom(Builder $query, $orderField, $dir)
405
    {
406
        if (!isset($this->order_fields) || !is_array($this->order_fields)) {
407
            throw new InvalidArgumentException(get_class($this) . ' order fields not set correctly.');
408
        }
409
410
        if (($orderField === '' || $dir === '')
411
            && (!isset($this->order_defaults) || !is_array($this->order_defaults))) {
412
            throw new InvalidArgumentException(get_class($this) . ' order defaults not set and not overriden.');
413
        }
414
415
        // Remove order global scope if it exists
416
        /** @var Model $this */
417
        $globalScopes = $this->getGlobalScopes();
418
        if (isset($globalScopes['order'])) {
419
            $query->withoutGlobalScope('order');
420
        }
421
422
        return $query->setCustomOrder($orderField, $dir);
423
    }
424
425
    /**
426
     * Check if column being sorted by is from a related model
427
     *
428
     * @param Builder $query
429
     * @param string $column
430
     * @param string $direction
431
     * @return Builder
432
     */
433
    public function scopeOrderByCheckModel(Builder $query, $column, $direction)
434
    {
435
        $query->orderBy(DB::raw($column), $direction);
0 ignored issues
show
Bug introduced by
The method orderBy() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean enforceOrderBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
436
437
        $periodPos = strpos($column, '.');
438
        if (isset($this->order_relations) && ($periodPos !== false || isset($this->order_relations[$column]))) {
439
            $table = ($periodPos !== false ? substr($column, 0, $periodPos) : $column);
440
441
            if (isset($this->order_relations[$table]) &&
442
                !$this->hasJoin($query, $table, $this->order_relations[$table])) {
443
                $columnRelations = $this->order_relations[$table];
444
445
                $query->modelJoin(
446
                    $columnRelations,
447
                    '=',
448
                    'left',
449
                    false,
450
                    false
451
                );
452
            }
453
        }
454
455
        return $query;
456
    }
457
458
    /**
459
     * Check if this model has already been joined to a table or relation
460
     *
461
     * @param Builder $builder
462
     * @param string $table
463
     * @param \Illuminate\Database\Eloquent\Relations\Relation $relation
464
     * @return bool
465
     */
466
    protected function hasJoin(Builder $builder, $table, $relation)
467
    {
468
        $joins = $builder->getQuery()->joins;
469
        if (!is_null($joins)) {
470
            foreach ($joins as $joinClause) {
471
                if ($joinClause->table == $table) {
472
                    return true;
473
                }
474
            }
475
        }
476
477
        $eagerLoads = $builder->getEagerLoads();
478
479
        return !is_null($eagerLoads) && in_array($relation, $eagerLoads);
480
    }
481
482
    /**
483
     * Set the model order
484
     *
485
     * @param Builder $query
486
     * @param string $column
487
     * @param string $direction
488
     * @return Builder
489
     */
490
    public function scopeSetCustomOrder(Builder $query, $column, $direction)
491
    {
492
        if (isset($this->order_defaults)) {
493
            // If $column not in order_fields list, use default
494
            if ($column == '' || !isset($this->order_fields[$column])) {
495
                $column = $this->order_defaults['field'];
496
            }
497
498
            // If $direction not asc or desc, use default
499
            if ($direction == '' || !in_array(strtoupper($direction), ['ASC', 'DESC'])) {
500
                $direction = $this->order_defaults['dir'];
501
            }
502
        }
503
504
        if (!is_array($this->order_fields[$column])) {
505
            $query->orderByCheckModel($this->order_fields[$column], $direction);
506
        } else {
507
            foreach ($this->order_fields[$column] as $dbField) {
508
                $query->orderByCheckModel($dbField, $direction);
509
            }
510
        }
511
512
        return $query;
513
    }
514
515
    /**
516
     * Switch a query to be a subquery of a model
517
     *
518
     * @param Builder $query
519
     * @param Builder $model
520
     * @return Builder
521
     */
522
    public function scopeSetSubquery(Builder $query, $model)
523
    {
524
        $sql = $this->toSqlWithBindings($model);
525
        $table = $model->getQuery()->from;
526
527
        return $query
528
            ->from(DB::raw("({$sql}) as " . $table))
529
            ->select($table . '.*');
530
    }
531
532
    /**
533
     * Use a model method to add columns or joins if in the order options
534
     *
535
     * @param Builder $query
536
     * @param string $order
537
     * @return Builder
538
     */
539
    public function scopeOrderByWith(Builder $query, $order)
540
    {
541
        if (isset($this->order_with[$order])) {
542
            $with = 'with' . $this->order_with[$order];
543
544
            $query->$with();
545
        }
546
547
        if (isset($this->order_fields[$order])) {
548
            $orderOption = (explode('.', $this->order_fields[$order]))[0];
549
550
            if (isset($this->order_relations[$orderOption])) {
551
                $query->modelJoin(
552
                    $this->order_relations[$orderOption],
553
                    '=',
554
                    'left',
555
                    false,
556
                    false
557
                );
558
            }
559
        }
560
561
        return $query;
562
    }
563
564
    /**
565
     * Add where statements for the model search fields
566
     *
567
     * @param Builder $query
568
     * @param string $searchText
569
     * @return Builder
570
     */
571
    public function scopeSearch(Builder $query, $searchText = '')
572
    {
573
        $searchText = trim($searchText);
574
575
        // If search is set
576
        if ($searchText != "") {
577
            if (!isset($this->search_fields) || !is_array($this->search_fields) || empty($this->search_fields)) {
0 ignored issues
show
Bug introduced by
The property search_fields does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
578
                throw new InvalidArgumentException(get_class($this) . ' search properties not set correctly.');
579
            } else {
580
                $query = $this->addSearch($query, $searchText);
581
            }
582
        }
583
584
        return $query;
585
    }
586
587
    /**
588
     * Add where statements for search fields to search for searchText
589
     *
590
     * @param Builder $query
591
     * @param string $searchText
592
     * @return Builder
593
     */
594
    private function addSearch($query, $searchText)
595
    {
596
        return $query->where(function (Builder $query) use ($searchText) {
597
            $searchFields = $this->search_fields ?? [];
598
            if (!empty($searchFields)) {
599
                /** @var Model $this */
600
                $table = $this->getTable();
601
                foreach ($searchFields as $searchField => $searchFieldParameters) {
602
                    if (!isset($searchFieldParameters['regex']) ||
603
                        preg_match($searchFieldParameters['regex'], $searchText)) {
604
                        $searchColumn = is_array($searchFieldParameters)
605
                            ? $searchField : $searchFieldParameters;
606
607
                        if (isset($searchFieldParameters['relation'])) {
608
                            $query = $this->searchRelation($query, $searchFieldParameters, $searchColumn, $searchText);
609
                        } else {
610
                            $query = $this->searchThis($query, $searchFieldParameters, $table, $searchColumn, $searchText);
611
                        }
612
                    }
613
                }
614
            }
615
616
            return $query;
617
        });
618
    }
619
620
    /**
621
     * Add where condition to search current model
622
     *
623
     * @param Builder $query
624
     * @param array $searchFieldParameters
625
     * @param string $table
626
     * @param string $searchColumn
627
     * @param string $searchText
628
     * @return Builder
629
     */
630
    public function searchThis(Builder $query, $searchFieldParameters, $table, $searchColumn, $searchText)
631
    {
632
        $searchOperator = $searchFieldParameters['operator'] ?? 'like';
633
        $searchValue = $searchFieldParameters['value'] ?? '%{{search}}%';
634
635
        return $query->orWhere(
636
            $table . '.' . $searchColumn,
637
            $searchOperator,
638
            str_replace('{{search}}', $searchText, $searchValue)
639
        );
640
    }
641
642
    /**
643
     * Add where condition to search a relation
644
     *
645
     * @param Builder $query
646
     * @param array $searchFieldParameters
647
     * @param string $searchColumn
648
     * @param string $searchText
649
     * @return Builder
650
     */
651
    private function searchRelation(Builder $query, $searchFieldParameters, $searchColumn, $searchText)
652
    {
653
        $relation = $searchFieldParameters['relation'];
654
        $relatedTable = $this->$relation()->getRelated()->getTable();
655
656
        return $query->orWhere(function (Builder $query) use (
657
            $searchText,
658
            $searchColumn,
659
            $searchFieldParameters,
660
            $relation,
661
            $relatedTable
662
        ) {
663
            return $query->orWhereHas($relation, function (Builder $query2) use (
664
                $searchText,
665
                $searchColumn,
666
                $searchFieldParameters,
667
                $relatedTable
668
            ) {
669
                return $query2->where($relatedTable . '.' . $searchColumn, 'like', $searchText . '%');
670
            });
671
        });
672
    }
673
}
674