Completed
Push — master ( e98ec0...b5ecb4 )
by Arjay
07:49
created

EloquentDataTable::joinEagerLoadedColumn()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 45
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 34
nc 5
nop 2
dl 0
loc 45
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
namespace Yajra\DataTables;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Relations\Relation;
7
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
9
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
10
use Yajra\DataTables\Exceptions\Exception;
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 boolean
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
            || ! in_array($relation, array_keys($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->getQualifiedForeignKey();
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