1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Yajra\DataTables; |
4
|
|
|
|
5
|
|
|
use Illuminate\Database\Eloquent\Builder; |
6
|
|
|
use Yajra\DataTables\Exceptions\Exception; |
7
|
|
|
use Illuminate\Database\Eloquent\Relations\Relation; |
8
|
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo; |
9
|
|
|
use Illuminate\Database\Eloquent\Relations\HasOneOrMany; |
10
|
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany; |
11
|
|
|
|
12
|
|
|
class EloquentDataTable extends QueryDataTable |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* @var \Illuminate\Database\Eloquent\Builder |
16
|
|
|
*/ |
17
|
|
|
protected $query; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Can the DataTable engine be created with these parameters. |
21
|
|
|
* |
22
|
|
|
* @param mixed $source |
23
|
|
|
* @return bool |
24
|
|
|
*/ |
25
|
|
|
public static function canCreate($source) |
26
|
|
|
{ |
27
|
|
|
return $source instanceof Builder || $source instanceof Relation; |
|
|
|
|
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* EloquentEngine constructor. |
32
|
|
|
* |
33
|
|
|
* @param mixed $model |
34
|
|
|
*/ |
35
|
|
|
public function __construct($model) |
36
|
|
|
{ |
37
|
|
|
$builder = $model instanceof Builder ? $model : $model->getQuery(); |
|
|
|
|
38
|
|
|
parent::__construct($builder->getQuery()); |
39
|
|
|
|
40
|
|
|
$this->query = $builder; |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Add columns in collection. |
45
|
|
|
* |
46
|
|
|
* @param array $names |
47
|
|
|
* @param bool|int $order |
48
|
|
|
* @return $this |
49
|
|
|
*/ |
50
|
|
|
public function addColumns(array $names, $order = false) |
51
|
|
|
{ |
52
|
|
|
foreach ($names as $name => $attribute) { |
53
|
|
|
if (is_int($name)) { |
54
|
|
|
$name = $attribute; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
$this->addColumn($name, function ($model) use ($attribute) { |
58
|
|
|
return $model->getAttribute($attribute); |
59
|
|
|
}, is_int($order) ? $order++ : $order); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
return $this; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* If column name could not be resolved then use primary key. |
67
|
|
|
* |
68
|
|
|
* @return string |
69
|
|
|
*/ |
70
|
|
|
protected function getPrimaryKeyName() |
71
|
|
|
{ |
72
|
|
|
return $this->query->getModel()->getKeyName(); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Compile query builder where clause depending on configurations. |
77
|
|
|
* |
78
|
|
|
* @param mixed $query |
79
|
|
|
* @param string $columnName |
80
|
|
|
* @param string $keyword |
81
|
|
|
* @param string $boolean |
82
|
|
|
*/ |
83
|
|
|
protected function compileQuerySearch($query, $columnName, $keyword, $boolean = 'or') |
84
|
|
|
{ |
85
|
|
|
$parts = explode('.', $columnName); |
86
|
|
|
$column = array_pop($parts); |
87
|
|
|
$relation = implode('.', $parts); |
88
|
|
|
|
89
|
|
|
if ($this->isNotEagerLoaded($relation)) { |
90
|
|
|
return parent::compileQuerySearch($query, $columnName, $keyword, $boolean); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
$query->{$boolean . 'WhereHas'}($relation, function (Builder $query) use ($column, $keyword) { |
94
|
|
|
parent::compileQuerySearch($query, $column, $keyword, ''); |
95
|
|
|
}); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Resolve the proper column name be used. |
100
|
|
|
* |
101
|
|
|
* @param string $column |
102
|
|
|
* @return string |
103
|
|
|
*/ |
104
|
|
|
protected function resolveRelationColumn($column) |
105
|
|
|
{ |
106
|
|
|
$parts = explode('.', $column); |
107
|
|
|
$columnName = array_pop($parts); |
108
|
|
|
$relation = implode('.', $parts); |
109
|
|
|
|
110
|
|
|
if ($this->isNotEagerLoaded($relation)) { |
111
|
|
|
return $column; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
return $this->joinEagerLoadedColumn($relation, $columnName); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Check if a relation was not used on eager loading. |
119
|
|
|
* |
120
|
|
|
* @param string $relation |
121
|
|
|
* @return bool |
122
|
|
|
*/ |
123
|
|
|
protected function isNotEagerLoaded($relation) |
124
|
|
|
{ |
125
|
|
|
return ! $relation |
126
|
|
|
|| ! array_key_exists($relation, $this->query->getEagerLoads()) |
127
|
|
|
|| $relation === $this->query->getModel()->getTable(); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Join eager loaded relation and get the related column name. |
132
|
|
|
* |
133
|
|
|
* @param string $relation |
134
|
|
|
* @param string $relationColumn |
135
|
|
|
* @return string |
136
|
|
|
* @throws \Yajra\DataTables\Exceptions\Exception |
137
|
|
|
*/ |
138
|
|
|
protected function joinEagerLoadedColumn($relation, $relationColumn) |
139
|
|
|
{ |
140
|
|
|
$table = ''; |
141
|
|
|
$lastQuery = $this->query; |
142
|
|
|
foreach (explode('.', $relation) as $eachRelation) { |
143
|
|
|
$model = $lastQuery->getRelation($eachRelation); |
144
|
|
|
switch (true) { |
145
|
|
|
case $model instanceof BelongsToMany: |
|
|
|
|
146
|
|
|
$pivot = $model->getTable(); |
147
|
|
|
$pivotPK = $model->getExistenceCompareKey(); |
148
|
|
|
$pivotFK = $model->getQualifiedParentKeyName(); |
149
|
|
|
$this->performJoin($pivot, $pivotPK, $pivotFK); |
150
|
|
|
|
151
|
|
|
$related = $model->getRelated(); |
152
|
|
|
$table = $related->getTable(); |
153
|
|
|
$tablePK = $related->getForeignKey(); |
154
|
|
|
$foreign = $pivot . '.' . $tablePK; |
155
|
|
|
$other = $related->getQualifiedKeyName(); |
156
|
|
|
|
157
|
|
|
$lastQuery->addSelect($table . '.' . $relationColumn); |
158
|
|
|
$this->performJoin($table, $foreign, $other); |
159
|
|
|
|
160
|
|
|
break; |
161
|
|
|
|
162
|
|
|
case $model instanceof HasOneOrMany: |
|
|
|
|
163
|
|
|
$table = $model->getRelated()->getTable(); |
164
|
|
|
$foreign = $model->getQualifiedForeignKeyName(); |
165
|
|
|
$other = $model->getQualifiedParentKeyName(); |
166
|
|
|
break; |
167
|
|
|
|
168
|
|
|
case $model instanceof BelongsTo: |
|
|
|
|
169
|
|
|
$table = $model->getRelated()->getTable(); |
170
|
|
|
$foreign = $model->getQualifiedForeignKeyName(); |
171
|
|
|
$other = $model->getQualifiedOwnerKeyName(); |
172
|
|
|
break; |
173
|
|
|
|
174
|
|
|
default: |
175
|
|
|
throw new Exception('Relation ' . get_class($model) . ' is not yet supported.'); |
176
|
|
|
} |
177
|
|
|
$this->performJoin($table, $foreign, $other); |
178
|
|
|
$lastQuery = $model->getQuery(); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
return $table . '.' . $relationColumn; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Perform join query. |
186
|
|
|
* |
187
|
|
|
* @param string $table |
188
|
|
|
* @param string $foreign |
189
|
|
|
* @param string $other |
190
|
|
|
* @param string $type |
191
|
|
|
*/ |
192
|
|
|
protected function performJoin($table, $foreign, $other, $type = 'left') |
193
|
|
|
{ |
194
|
|
|
$joins = []; |
195
|
|
|
foreach ((array) $this->getBaseQueryBuilder()->joins as $key => $join) { |
196
|
|
|
$joins[] = $join->table; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
if (! in_array($table, $joins)) { |
200
|
|
|
$this->getBaseQueryBuilder()->join($table, $foreign, '=', $other, $type); |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.