Completed
Push — master ( e64fb3...ac2ac0 )
by Arjay
14:39
created

EloquentDataTable::performJoin()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 8
nc 8
nop 5
dl 0
loc 15
rs 9.2
c 1
b 0
f 0
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
        $deletedAt = false;
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 HasOneOrMany:
164
                    $table     = $model->getRelated()->getTable();
165
                    $foreign   = $model->getQualifiedForeignKeyName();
166
                    $other     = $model->getQualifiedParentKeyName();
167
                    $deletedAt = $this->checkSoftDeletesOnModel($model->getRelated());
168
                    break;
169
170
                case $model instanceof BelongsTo:
171
                    $table     = $model->getRelated()->getTable();
172
                    $foreign   = $model->getQualifiedForeignKey();
173
                    $other     = $model->getQualifiedOwnerKeyName();
174
                    $deletedAt = $this->checkSoftDeletesOnModel($model->getRelated());
175
                    break;
176
177
                default:
178
                    throw new Exception('Relation ' . get_class($model) . ' is not yet supported.');
179
            }
180
            $this->performJoin($table, $foreign, $other, $deletedAt);
181
            $lastQuery = $model->getQuery();
182
        }
183
184
        return $table . '.' . $relationColumn;
185
    }
186
187
    protected function checkSoftDeletesOnModel($model)
188
    {
189
        if (in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($model))) {
190
            return $model->getQualifiedDeletedAtColumn();
191
        }
192
193
        return false;
194
    }
195
196
    /**
197
     * Perform join query.
198
     *
199
     * @param string $table
200
     * @param string $foreign
201
     * @param string $other
202
     * @param string $deletedAt
203
     * @param string $type
204
     */
205
    protected function performJoin($table, $foreign, $other, $deletedAt = false, $type = 'left')
206
    {
207
        $joins = [];
208
        foreach ((array) $this->getBaseQueryBuilder()->joins as $key => $join) {
209
            $joins[] = $join->table;
210
        }
211
212
        if (! in_array($table, $joins)) {
213
            $this->getBaseQueryBuilder()->join($table, $foreign, '=', $other, $type);
214
        }
215
216
        if ($deletedAt !== false) {
217
            $this->getBaseQueryBuilder()->whereNull($deletedAt);
218
        }
219
    }
220
}
221