Completed
Push — master ( 37e2b7...6ae8ad )
by Maxime
329:05 queued 327:28
created

EloquentDatatable::baseQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Distilleries\DatatableBuilder;
4
5
use Route;
6
use Schema;
7
use Request;
8
use Datatable;
9
use FormBuilder;
10
use ReflectionClass;
11
use Illuminate\Database\Eloquent\Model;
12
13
abstract class EloquentDatatable
14
{
15
    /**
16
     * Eloquent model.
17
     * 
18
     * @var \Illuminate\Database\Eloquent\Model
19
     */
20
    protected $model;
21
22
    /**
23
     * Eloquent model underlying table.
24
     * 
25
     * @var string
26
     */
27
    protected $table = '';
28
    
29
    /**
30
     * Datatable columns.
31
     * 
32
     * @var array
33
     */
34
    protected $colomns;
35
    
36
    /**
37
     * Form implementation.
38
     *
39
     * @var \Kris\LaravelFormBuilder\Form|null
40
     */
41
    protected $form = null;
42
43
    /**
44
     * Datatable columns to display.
45
     *
46
     * @var array
47
     */
48
    protected $colomnsDisplay = [];
49
50
    /**
51
     * Datatable columns orderable state.
52
     *
53
     * @var array
54
     */
55
    protected $columnsOrderable = [];
56
57
    /**
58
     * Datatable extra options.
59
     *
60
     * @var array
61
     */
62
    protected $datatableOptions = [];
63
64
    /**
65
     * Datatable order data (0 can be an integer to represents the column's number or it can be a string that references the column's name).
66
     *
67
     * @var array
68
     */
69
    protected $defaultOrder = [[0, 'desc']];
70
71
    /**
72
     * EloquentDatatable constructor.
73
     *
74
     * @param \Illuminate\Database\Eloquent\Model|null $model
75
     */
76 8
    public function __construct(Model $model = null)
77
    {
78 8
        if (!empty($model)) {
79
            $this->setModel($model);
80
        }
81
    }
82
83
    /**
84
     * Model setter.
85
     *
86
     * @param \Illuminate\Database\Eloquent\Model $model
87
     * @return void
88
     */
89 4
    public function setModel(Model $model)
90
    {
91 4
        $this->model = $model;
92 4
        $this->table = $this->model->getTable();
93
    }
94
95
    /**
96
     * Add column to datatable.
97
     *
98
     * @param string $name
99
     * @param \Closure|null $closure
100
     * @param string|\Symfony\Component\Translation\TranslatorInterface $translation
101
     * @param bool $orderable
102
     * @return $this
103
     */
104 8
    public function add($name, $closure = null, $translation = '', $orderable = true)
105
    {
106 8
        if (! empty($closure)) {
107 8
            $this->colomns[] = [
108 8
                $name,
109 8
                $closure,
110
            ];
111
        } else {
112 8
            $this->colomns[] = $name;
113
        }
114
115 8
        $this->addTranslation($name, $translation);
116 8
        $this->addOrderable($name, $orderable);
117
118 8
        return $this;
119
    }
120
121
    /**
122
     * Add translation column.
123
     *
124
     * @param string $name
125
     * @param string|\Symfony\Component\Translation\TranslatorInterface $translation
126
     * @return void
127
     */
128 8
    public function addTranslation($name, $translation)
129
    {
130 8
        $this->colomnsDisplay[] = ! empty($translation) ? $translation : ucfirst($name);
131
    }
132
133
    /**
134
     * Add orderable column.
135
     *
136
     * @param string $name
137
     * @param bool $translation
0 ignored issues
show
Bug introduced by
There is no parameter named $translation. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
138
     * @return void
139
     */
140 8
    public function addOrderable($name, $orderable)
141
    {
142 8
        $this->columnsOrderable[$name] = $orderable;
143
    }
144
145
    /**
146
     * Generate specified columns for current datatable.
147
     *
148
     * @return mixed
149
     */
150 4
    public function generateColomns()
151
    {
152 4
        $this->model = $this->baseQuery();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->baseQuery() of type object<Illuminate\Database\Eloquent\Builder> is incompatible with the declared type object<Illuminate\Database\Eloquent\Model> of property $model.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
153
154 4
        $this->applyFilters();
155
156 4
        $datatable = Datatable::query($this->model);
157 4
        $colSearchAndSort = [];
158 4
        $sortOnly = [];
159
160 4
        if (! empty($this->colomns)) {
161 4
            foreach ($this->colomns as $key => $value) {
162
163 4
                if (is_string($value)) {
164 4
                    $datatable->showColumns($value);
165 4
                    $colSearchAndSort[] = $value;
166
                } else {
167 4
                    if (is_array($value) && (count($value) === 2)) {
168 4
                        $datatable->addColumn($value[0], $value[1]);
169 4
                        $sortOnly[] = $value[0];
170
                    }
171
                }
172
            }
173
        }
174
175 4
        $datatable = $this->setClassRow($datatable);
176 4
        $datatable->orderColumns(array_merge($colSearchAndSort, $sortOnly));
177 4
        $datatable->searchColumns($colSearchAndSort);
178
179 4
        return $datatable->make();
180
    }
181
182
    /**
183
     * Return Datatable base query.
184
     *
185
     * @return \Illuminate\Database\Eloquent\Builder
186
     */
187 4
    protected function baseQuery()
188
    {
189 4
        return $this->model->newModelQuery();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->model->newModelQuery(); of type Illuminate\Database\Eloq...Database\Eloquent\Model adds the type Illuminate\Database\Eloquent\Model to the return on line 189 which is incompatible with the return type documented by Distilleries\DatatableBu...entDatatable::baseQuery of type Illuminate\Database\Eloquent\Builder.
Loading history...
190
    }
191
192
    /**
193
     * Set DT_RowClass for given datatable.
194
     *
195
     * @param \Chumper\Datatable\Engines\QueryEngine $datatable
196
     * @return \Chumper\Datatable\Engines\QueryEngine
197
     */
198 4
    public function setClassRow($datatable)
199
    {
200
        $datatable->setRowClass(function ($row) {
201 4
            return (isset($row->status) && empty($row->status)) ? 'danger' : '';
202 4
        });
203
204 4
        return $datatable;
205
    }
206
207
    /**
208
     * Generate rendered HTML view for current datatable.
209
     *
210
     * @param string $template
211
     * @param string $route
212
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
213
     */
214 8
    public function generateHtmlRender($template = 'datatable-builder::part.datatable', $route = '')
215
    {
216 8
        return view($template, [
217 8
            'colomns_display' => $this->colomnsDisplay,
218 8
            'datatable_options' => $this->addOptions(),
219 8
            'id' => strtolower(str_replace('\\', '_', get_class($this))),
220 8
            'route' => ! empty($route) ? $route : $this->getControllerNameForAction() . '@getDatatable',
221 8
            'filters' => $this->addFilter(),
222
        ]);
223
    }
224
225
    /**
226
     * Add default actions to datatable.
227
     *
228
     * @param string $template
229
     * @param string $route
230
     * @return void
231
     */
232 8
    public function addDefaultAction($template = 'datatable-builder::form.components.datatable.actions', $route = '')
233
    {
234 8
        $reflection = new ReflectionClass(get_class($this));
235
236
        $this->add('actions', function ($model) use ($template, $reflection, $route) {
237 4
            return view($template, [
0 ignored issues
show
Bug introduced by
The method render does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

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...
238 4
                'data' => $model->toArray(),
239 4
                'route' => ! empty($route) ? $route . '@' : $this->getControllerNameForAction() . '@',
240 4
            ])->render();
241 8
        }, 'Actions', false);
242
    }
243
244
    /**
245
     * Add filters form to current datatable.
246
     *
247
     * @param string $template
248
     * @return string
249
     * @throws \Exception
250
     * @throws \Throwable
251
     */
252 8
    protected function addFilter($template = 'datatable-builder::form.components.datatable.filter')
253
    {
254 8
        $this->form = FormBuilder::plain();
255 8
        $this->filters();
256
257 8
        $filter_content = view($template, [
0 ignored issues
show
Bug introduced by
The method render does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

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...
258 8
            'form' => $this->form,
259 8
        ])->render();
260
261 8
        return $filter_content;
262
    }
263
264
    /**
265
     * Add default options to current datatable.
266
     *
267
     * @return array
268
     */
269 8
    protected function addOptions()
270
    {
271 8
        if (! array_key_exists('order', $this->datatableOptions) && ! empty($this->defaultOrder)) {
272 8
            if (is_array($this->defaultOrder)) {
273 8
                foreach ($this->defaultOrder as $keyOrder => $order) {
274 8
                    if (is_string($order[0])) {
275
                        foreach ($this->colomns as $key => $colomn) {
276
                            if (is_array($colomn)) {
277
                                $colomn = $colomn[0];
278
                            }
279
                            if ($colomn == $order[0]) {
280
                                $this->defaultOrder[$keyOrder][0] = $key;
281
                            }
282
                        }
283
                        if (is_string($this->defaultOrder[$keyOrder][0])) {
284 8
                            $this->defaultOrder[$keyOrder][0] = 0;
285
                        }
286
                    }
287
                }
288 8
                $this->datatableOptions['order'] = $this->defaultOrder;
289
            }
290
        }
291
292 8
        $nonOrderableColumns = [];
293 8
        foreach (array_values($this->columnsOrderable) as $index => $orderableState) {
294 8
            if (!$orderableState) {
295 8
                $nonOrderableColumns[] = $index;
296
            }
297
        }
298 8
        if (!empty($nonOrderableColumns)) {
299 8
            $this->datatableOptions['columnDefs'] = [
300 8
                ['orderable' => false, 'targets' => $nonOrderableColumns],
301
            ];
302
        }
303
304 8
        return $this->datatableOptions;
305
    }
306
307
    /**
308
     * Get controller name for action based on current route.
309
     *
310
     * @return string
311
     */
312 8
    protected function getControllerNameForAction()
313
    {
314 8
        $action = explode('@', Route::currentRouteAction());
315
316 8
        return '\\' . $action[0];
317
    }
318
319
    /**
320
     * Add the fields filters form here.
321
     *
322
     * @return void
323
     */
324
    public function filters()
325
    {
326
        //
327
    }
328
329
    /**
330
     * Apply filters by default on each fields of setted model.
331
     *
332
     * @return void
333
     */
334 4
    public function applyFilters()
335
    {
336 4
        $columns = Schema::getColumnListing($this->table);
337
338 4
        $allInput = Request::all();
339 4
        foreach ($allInput as $name => $input) {
340 4
            if (in_array($name, $columns) && ($input != '')) {
341 4
                $this->model = $this->model->where($this->table . '.' . $name, '=', $input);
342
            }
343
        }
344
    }
345
346
    /**
347
     * Compile all added columns and build datatable.
348
     *
349
     * @return void
350
     */
351
    abstract public function build();
352
}