Completed
Push — master ( b1bb78...82c1ec )
by Arjay
01:57 queued 12s
created

src/EloquentDataTable.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Yajra\DataTables;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Relations\BelongsTo;
7
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
8
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
9
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use Yajra\DataTables\Exceptions\Exception;
12
13
class EloquentDataTable extends QueryDataTable
14
{
15
    /**
16
     * @var \Illuminate\Database\Eloquent\Builder
17
     */
18
    protected $query;
19
20
    /**
21
     * Can the DataTable engine be created with these parameters.
22
     *
23
     * @param mixed $source
24
     * @return bool
25
     */
26
    public static function canCreate($source)
27
    {
28
        return $source instanceof Builder || $source instanceof Relation;
29
    }
30
31
    /**
32
     * EloquentEngine constructor.
33
     *
34
     * @param mixed $model
35
     */
36
    public function __construct($model)
37
    {
38
        $builder = $model instanceof Builder ? $model : $model->getQuery();
39
        parent::__construct($builder->getQuery());
40
41
        $this->query = $builder;
42
    }
43
44
    /**
45
     * Add columns in collection.
46
     *
47
     * @param  array  $names
48
     * @param  bool|int  $order
49
     * @return $this
50
     */
51
    public function addColumns(array $names, $order = false)
52
    {
53
        foreach ($names as $name => $attribute) {
54
            if (is_int($name)) {
55
                $name = $attribute;
56
            }
57
58
            $this->addColumn($name, function ($model) use ($attribute) {
59
                return $model->getAttribute($attribute);
60
            }, is_int($order) ? $order++ : $order);
61
        }
62
63
        return $this;
64
    }
65
66
    /**
67
     * If column name could not be resolved then use primary key.
68
     *
69
     * @return string
70
     */
71
    protected function getPrimaryKeyName()
72
    {
73
        return $this->query->getModel()->getKeyName();
0 ignored issues
show
The method getKeyName does only exist in Illuminate\Database\Eloquent\Model, 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...
74
    }
75
76
    /**
77
     * Compile query builder where clause depending on configurations.
78
     *
79
     * @param mixed  $query
80
     * @param string $columnName
81
     * @param string $keyword
82
     * @param string $boolean
83
     */
84
    protected function compileQuerySearch($query, $columnName, $keyword, $boolean = 'or')
85
    {
86
        $parts    = explode('.', $columnName);
87
        $column   = array_pop($parts);
88
        $relation = implode('.', $parts);
89
90
        if ($this->isNotEagerLoaded($relation)) {
91
            return parent::compileQuerySearch($query, $columnName, $keyword, $boolean);
92
        }
93
94
        $query->{$boolean . 'WhereHas'}($relation, function (Builder $query) use ($column, $keyword) {
95
            parent::compileQuerySearch($query, $column, $keyword, '');
96
        });
97
    }
98
99
    /**
100
     * Resolve the proper column name be used.
101
     *
102
     * @param string $column
103
     * @return string
104
     */
105
    protected function resolveRelationColumn($column)
106
    {
107
        $parts      = explode('.', $column);
108
        $columnName = array_pop($parts);
109
        $relation   = implode('.', $parts);
110
111
        if ($this->isNotEagerLoaded($relation)) {
112
            return $column;
113
        }
114
115
        return $this->joinEagerLoadedColumn($relation, $columnName);
116
    }
117
118
    /**
119
     * Check if a relation was not used on eager loading.
120
     *
121
     * @param  string $relation
122
     * @return bool
123
     */
124
    protected function isNotEagerLoaded($relation)
125
    {
126
        return ! $relation
127
            || ! array_key_exists($relation, $this->query->getEagerLoads())
128
            || $relation === $this->query->getModel()->getTable();
0 ignored issues
show
The method getTable does only exist in Illuminate\Database\Eloquent\Model, 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...
129
    }
130
131
    /**
132
     * Join eager loaded relation and get the related column name.
133
     *
134
     * @param string $relation
135
     * @param string $relationColumn
136
     * @return string
137
     * @throws \Yajra\DataTables\Exceptions\Exception
138
     */
139
    protected function joinEagerLoadedColumn($relation, $relationColumn)
140
    {
141
        $table     = '';
142
        $lastQuery = $this->query;
143
        foreach (explode('.', $relation) as $eachRelation) {
144
            $model = $lastQuery->getRelation($eachRelation);
145
            switch (true) {
146
                case $model instanceof BelongsToMany:
147
                    $pivot   = $model->getTable();
148
                    $pivotPK = $model->getExistenceCompareKey();
149
                    $pivotFK = $model->getQualifiedParentKeyName();
150
                    $this->performJoin($pivot, $pivotPK, $pivotFK);
151
152
                    $related = $model->getRelated();
153
                    $table   = $related->getTable();
154
                    $tablePK = $related->getForeignKey();
155
                    $foreign = $pivot . '.' . $tablePK;
156
                    $other   = $related->getQualifiedKeyName();
157
158
                    $lastQuery->addSelect($table . '.' . $relationColumn);
159
                    $this->performJoin($table, $foreign, $other);
160
161
                    break;
162
163
                case $model instanceof HasOneThrough:
164
                    $pivot    = explode('.', $model->getQualifiedParentKeyName())[0]; // extract pivot table from key
165
                    $pivotPK  = $pivot . '.' . $model->getLocalKeyName();
166
                    $pivotFK  = $model->getQualifiedLocalKeyName();
167
                    $this->performJoin($pivot, $pivotPK, $pivotFK);
168
169
                    $related = $model->getRelated();
170
                    $table   = $related->getTable();
171
                    $tablePK = $related->getForeignKey();
172
                    $foreign = $pivot . '.' . $tablePK;
173
                    $other   = $related->getQualifiedKeyName();
174
175
                    break;
176
177
                case $model instanceof HasOneOrMany:
178
                    $table     = $model->getRelated()->getTable();
179
                    $foreign   = $model->getQualifiedForeignKeyName();
180
                    $other     = $model->getQualifiedParentKeyName();
181
                    break;
182
183
                case $model instanceof BelongsTo:
184
                    $table     = $model->getRelated()->getTable();
185
                    $foreign   = $model->getQualifiedForeignKeyName();
186
                    $other     = $model->getQualifiedOwnerKeyName();
187
                    break;
188
189
                default:
190
                    throw new Exception('Relation ' . get_class($model) . ' is not yet supported.');
191
            }
192
            $this->performJoin($table, $foreign, $other);
193
            $lastQuery = $model->getQuery();
194
        }
195
196
        return $table . '.' . $relationColumn;
197
    }
198
199
    /**
200
     * Perform join query.
201
     *
202
     * @param string $table
203
     * @param string $foreign
204
     * @param string $other
205
     * @param string $type
206
     */
207
    protected function performJoin($table, $foreign, $other, $type = 'left')
208
    {
209
        $joins = [];
210
        foreach ((array) $this->getBaseQueryBuilder()->joins as $key => $join) {
211
            $joins[] = $join->table;
212
        }
213
214
        if (! in_array($table, $joins)) {
215
            $this->getBaseQueryBuilder()->join($table, $foreign, '=', $other, $type);
216
        }
217
    }
218
}
219