Completed
Push — master ( c8a423...a23ea0 )
by Song
02:21
created

src/Tree.php (1 issue)

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 Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Tree\Tools;
7
use Illuminate\Contracts\Support\Renderable;
8
use Illuminate\Database\Eloquent\Model;
9
10
class Tree implements Renderable
11
{
12
    /**
13
     * @var array
14
     */
15
    protected $items = [];
16
17
    /**
18
     * @var string
19
     */
20
    protected $elementId = 'tree-';
21
22
    /**
23
     * @var Model
24
     */
25
    protected $model;
26
27
    /**
28
     * @var \Closure
29
     */
30
    protected $queryCallback;
31
32
    /**
33
     * View of tree to render.
34
     *
35
     * @var string
36
     */
37
    protected $view = [
38
        'tree'   => 'admin::tree',
39
        'branch' => 'admin::tree.branch',
40
    ];
41
42
    /**
43
     * @var \Closure
44
     */
45
    protected $callback;
46
47
    /**
48
     * @var null
49
     */
50
    protected $branchCallback = null;
51
52
    /**
53
     * @var bool
54
     */
55
    public $useCreate = true;
56
57
    /**
58
     * @var bool
59
     */
60
    public $useSave = true;
61
62
    /**
63
     * @var bool
64
     */
65
    public $useRefresh = true;
66
67
    /**
68
     * @var array
69
     */
70
    protected $nestableOptions = [];
71
72
    /**
73
     * Header tools.
74
     *
75
     * @var Tools
76
     */
77
    public $tools;
78
79
    /**
80
     * Menu constructor.
81
     *
82
     * @param Model|null $model
83
     */
84
    public function __construct(Model $model = null, \Closure $callback = null)
85
    {
86
        $this->model = $model;
87
88
        $this->path = \request()->getPathInfo();
0 ignored issues
show
The property path does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
89
        $this->elementId .= uniqid();
90
91
        $this->setupTools();
92
93
        if ($callback instanceof \Closure) {
94
            call_user_func($callback, $this);
95
        }
96
97
        $this->initBranchCallback();
98
    }
99
100
    /**
101
     * Setup tree tools.
102
     */
103
    public function setupTools()
104
    {
105
        $this->tools = new Tools($this);
106
    }
107
108
    /**
109
     * Initialize branch callback.
110
     *
111
     * @return void
112
     */
113
    protected function initBranchCallback()
114
    {
115
        if (is_null($this->branchCallback)) {
116
            $this->branchCallback = function ($branch) {
117
                $key = $branch[$this->model->getKeyName()];
118
                $title = $branch[$this->model->getTitleColumn()];
119
120
                return "$key - $title";
121
            };
122
        }
123
    }
124
125
    /**
126
     * Set branch callback.
127
     *
128
     * @param \Closure $branchCallback
129
     *
130
     * @return $this
131
     */
132
    public function branch(\Closure $branchCallback)
133
    {
134
        $this->branchCallback = $branchCallback;
135
136
        return $this;
137
    }
138
139
    /**
140
     * Set query callback this tree.
141
     *
142
     * @return Model
143
     */
144
    public function query(\Closure $callback)
145
    {
146
        $this->queryCallback = $callback;
147
148
        return $this;
149
    }
150
151
    /**
152
     * Set nestable options.
153
     *
154
     * @param array $options
155
     *
156
     * @return $this
157
     */
158
    public function nestable($options = [])
159
    {
160
        $this->nestableOptions = array_merge($this->nestableOptions, $options);
161
162
        return $this;
163
    }
164
165
    /**
166
     * Disable create.
167
     *
168
     * @return void
169
     */
170
    public function disableCreate()
171
    {
172
        $this->useCreate = false;
173
    }
174
175
    /**
176
     * Disable save.
177
     *
178
     * @return void
179
     */
180
    public function disableSave()
181
    {
182
        $this->useSave = false;
183
    }
184
185
    /**
186
     * Disable refresh.
187
     *
188
     * @return void
189
     */
190
    public function disableRefresh()
191
    {
192
        $this->useRefresh = false;
193
    }
194
195
    /**
196
     * Save tree order from a input.
197
     *
198
     * @param string $serialize
199
     *
200
     * @return bool
201
     */
202
    public function saveOrder($serialize)
203
    {
204
        $tree = json_decode($serialize, true);
205
206
        if (json_last_error() != JSON_ERROR_NONE) {
207
            throw new \InvalidArgumentException(json_last_error_msg());
208
        }
209
210
        $this->model->saveOrder($tree);
211
212
        return true;
213
    }
214
215
    /**
216
     * Build tree grid scripts.
217
     *
218
     * @return string
219
     */
220
    protected function script()
221
    {
222
        $trans = [
223
            'delete_confirm'    => trans('admin.delete_confirm'),
224
            'save_succeeded'    => trans('admin.save_succeeded'),
225
            'refresh_succeeded' => trans('admin.refresh_succeeded'),
226
            'delete_succeeded'  => trans('admin.delete_succeeded'),
227
            'confirm'           => trans('admin.confirm'),
228
            'cancel'            => trans('admin.cancel'),
229
        ];
230
231
        $nestableOptions = json_encode($this->nestableOptions);
232
233
        return <<<SCRIPT
234
235
        $('#{$this->elementId}').nestable($nestableOptions);
236
237
        $('.tree_branch_delete').click(function() {
238
            var id = $(this).data('id');
239
            swal({
240
                title: "{$trans['delete_confirm']}",
241
                type: "warning",
242
                showCancelButton: true,
243
                confirmButtonColor: "#DD6B55",
244
                confirmButtonText: "{$trans['confirm']}",
245
                showLoaderOnConfirm: true,
246
                cancelButtonText: "{$trans['cancel']}",
247
                preConfirm: function() {
248
                    return new Promise(function(resolve) {
249
                        $.ajax({
250
                            method: 'post',
251
                            url: '{$this->path}/' + id,
252
                            data: {
253
                                _method:'delete',
254
                                _token:LA.token,
255
                            },
256
                            success: function (data) {
257
                                $.pjax.reload('#pjax-container');
258
                                toastr.success('{$trans['delete_succeeded']}');
259
                                resolve(data);
260
                            }
261
                        });
262
                    });
263
                }
264
            }).then(function(result) {
265
                var data = result.value;
266
                if (typeof data === 'object') {
267
                    if (data.status) {
268
                        swal(data.message, '', 'success');
269
                    } else {
270
                        swal(data.message, '', 'error');
271
                    }
272
                }
273
            });
274
        });
275
276
        $('.{$this->elementId}-save').click(function () {
277
            var serialize = $('#{$this->elementId}').nestable('serialize');
278
279
            $.post('{$this->path}', {
280
                _token: LA.token,
281
                _order: JSON.stringify(serialize)
282
            },
283
            function(data){
284
                $.pjax.reload('#pjax-container');
285
                toastr.success('{$trans['save_succeeded']}');
286
            });
287
        });
288
289
        $('.{$this->elementId}-refresh').click(function () {
290
            $.pjax.reload('#pjax-container');
291
            toastr.success('{$trans['refresh_succeeded']}');
292
        });
293
294
        $('.{$this->elementId}-tree-tools').on('click', function(e){
295
            var action = $(this).data('action');
296
            if (action === 'expand') {
297
                $('.dd').nestable('expandAll');
298
            }
299
            if (action === 'collapse') {
300
                $('.dd').nestable('collapseAll');
301
            }
302
        });
303
304
305
SCRIPT;
306
    }
307
308
    /**
309
     * Set view of tree.
310
     *
311
     * @param string $view
312
     */
313
    public function setView($view)
314
    {
315
        $this->view = $view;
316
    }
317
318
    /**
319
     * Return all items of the tree.
320
     *
321
     * @param array $items
322
     */
323
    public function getItems()
324
    {
325
        return $this->model->withQuery($this->queryCallback)->toTree();
326
    }
327
328
    /**
329
     * Variables in tree template.
330
     *
331
     * @return array
332
     */
333
    public function variables()
334
    {
335
        return [
336
            'id'         => $this->elementId,
337
            'tools'      => $this->tools->render(),
338
            'items'      => $this->getItems(),
339
            'useCreate'  => $this->useCreate,
340
            'useSave'    => $this->useSave,
341
            'useRefresh' => $this->useRefresh,
342
        ];
343
    }
344
345
    /**
346
     * Setup grid tools.
347
     *
348
     * @param Closure $callback
349
     *
350
     * @return void
351
     */
352
    public function tools(Closure $callback)
353
    {
354
        call_user_func($callback, $this->tools);
355
    }
356
357
    /**
358
     * Render a tree.
359
     *
360
     * @return \Illuminate\Http\JsonResponse|string
361
     */
362
    public function render()
363
    {
364
        Admin::script($this->script());
365
366
        view()->share([
367
            'path'           => $this->path,
368
            'keyName'        => $this->model->getKeyName(),
369
            'branchView'     => $this->view['branch'],
370
            'branchCallback' => $this->branchCallback,
371
        ]);
372
373
        return view($this->view['tree'], $this->variables())->render();
374
    }
375
376
    /**
377
     * Get the string contents of the grid view.
378
     *
379
     * @return string
380
     */
381
    public function __toString()
382
    {
383
        return $this->render();
384
    }
385
}
386