Completed
Pull Request — master (#299)
by
unknown
18:21
created

Editable::handleEditableControl()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 10
cp 0
cc 3
eloc 9
nc 2
nop 1
crap 12
1
<?php
2
3
/**
4
 * This file is part of the Grido (http://grido.bugyik.cz)
5
 *
6
 * Copyright (c) 2014 Petr Bugyík (http://petr.bugyik.cz)
7
 *
8
 * For the full copyright and license information, please view
9
 * the file LICENSE.md that was distributed with this source code.
10
 */
11
12
namespace Grido\Components\Columns;
13
14
use Grido\Exception;
15
16
/**
17
 * An inline editable column.
18
 *
19
 * @package     Grido
20
 * @subpackage  Components\Columns
21
 * @author      Jakub Kopřiva <[email protected]>
22
 * @author      Petr Bugyík
23
 *
24
 * @property \Nette\Forms\IControl $editableControl
25
 * @property callback $editableCallback
26
 * @property callback $editableValueCallback
27
 * @property callback $editableRowCallback
28
 * @property bool $editable
29
 * @property bool $editableDisabled
30
 */
31
abstract class Editable extends Column
32 1
{
33
    /** @var bool */
34
    protected $editable = FALSE;
35
36
    /** @var bool */
37
    protected $editableDisabled = FALSE;
38
39
    /** @var \Nette\Forms\IControl Custom control for inline editing */
40
    protected $editableControl;
41
42
    /** @var callback for custom handling with edited data; function($id, $newValue, $oldValue, Editable $column) {} */
43
    protected $editableCallback;
44
45
    /** @var callback for custom value; function($row, Columns\Editable $column) {} */
46
    protected $editableValueCallback;
47
48
    /** @var callback for getting row; function($row, Columns\Editable $column) {} */
49
    protected $editableRowCallback;
50
51
    /**
52
     * Sets column as editable.
53
     * @param callback $callback function($id, $newValue, $oldValue, Columns\Editable $column) {}
54
     * @param \Nette\Forms\IControl $control
55
     * @return Editable
56
     */
57
    public function setEditable($callback = NULL, \Nette\Forms\IControl $control = NULL)
58
    {
59
        $this->editable = TRUE;
60
        $this->setClientSideOptions();
61
62
        $callback && $this->setEditableCallback($callback);
63
        $control && $this->setEditableControl($control);
64
65
        return $this;
66
    }
67
68
    /**
69
     * Sets control for inline editation.
70
     * @param \Nette\Forms\IControl $control
71
     * @return Editable
72
     */
73
    public function setEditableControl(\Nette\Forms\IControl $control)
74
    {
75
        $this->isEditable() ?: $this->setEditable();
76
        $this->editableControl = $control;
77
78
        return $this;
79
    }
80
81
    /**
82
     * Sets editable callback.
83
     * @param callback $callback function($id, $newValue, $oldValue, Columns\Editable $column) {}
84
     * @return Editable
85
     */
86
    public function setEditableCallback($callback)
87
    {
88
        $this->isEditable() ?: $this->setEditable();
89
        $this->editableCallback = $callback;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Sets editable value callback.
96
     * @param callback $callback for custom value; function($row, Columns\Editable $column) {}
97
     * @return Editable
98
     */
99
    public function setEditableValueCallback($callback)
100
    {
101
        $this->isEditable() ?: $this->setEditable();
102
        $this->editableValueCallback = $callback;
103
104
        return $this;
105
    }
106
107
    /**
108
     * Sets editable row callback - it's required when used editable collumn with customRenderCallback
109
     * @param callback $callback for getting row; function($id, Columns\Editable $column) {}
110
     * @return Editable
111
     */
112
    public function setEditableRowCallback($callback)
113
    {
114
        $this->isEditable() ?: $this->setEditable();
115
        $this->editableRowCallback = $callback;
116
117
        return $this;
118
    }
119
120
    /**
121
     * @return Editable
122
     */
123
    public function disableEditable()
124
    {
125
        $this->editable = FALSE;
126
        $this->editableDisabled = TRUE;
127
128
        return $this;
129
    }
130
131
    /**
132
     * @throws Exception
133
     */
134
    protected function setClientSideOptions()
135
    {
136
        $options = $this->grid->getClientSideOptions();
137
        if (!isset($options['editable'])) { //only once
138
            $this->grid->setClientSideOptions(['editable' => TRUE]);
139
            $this->grid->onRender[] = function(\Grido\Grid $grid)
140
            {
141
                foreach ($grid->getComponent(Column::ID)->getComponents() as $column) {
142
                    if (!$column instanceof Editable || !$column->isEditable()) {
143
                        continue;
144
                    }
145
146
                    $colDb = $column->getColumn();
147
                    $colName = $column->getName();
148
                    $isMissing = function ($method) use ($grid) {
149
                        return $grid->model instanceof \Grido\DataSources\Model
150
                            ? !method_exists($grid->model->dataSource, $method)
151
                            : TRUE;
152
                    };
153
154
                    if (($column->editableCallback === NULL && (!is_string($colDb) || strpos($colDb, '.'))) ||
155
                        ($column->editableCallback === NULL && $isMissing('update'))
156
                    ) {
157
                        $msg = "Column '$colName' has error: You must define callback via setEditableCallback().";
158
                        throw new Exception($msg);
159
                    }
160
161
                    if ($column->editableRowCallback === NULL && $column->customRender && $isMissing('getRow')) {
162
                        $msg = "Column '$colName' has error: You must define callback via setEditableRowCallback().";
163
                        throw new Exception($msg);
164
                    }
165
                }
166
            };
167
        }
168
    }
169
170
    /**********************************************************************************************/
171
172
    /**
173
     * Returns header cell prototype (<th> html tag).
174
     * @return \Nette\Utils\Html
175
     */
176
    public function getHeaderPrototype()
177
    {
178
        $th = parent::getHeaderPrototype();
179
180
        if ($this->isEditable()) {
181
            $th->setAttribute('data-grido-editable-handler', $this->link('editable!'));
182
            $th->setAttribute('data-grido-editableControl-handler', $this->link('editableControl!'));
183
        }
184
185
        return $th;
186
    }
187
188
    /**
189
     * Returns cell prototype (<td> html tag).
190
     * @param mixed $row
191
     * @return \Nette\Utils\Html
192
     */
193
    public function getCellPrototype($row = NULL)
194
    {
195
        $td = parent::getCellPrototype($row);
196
197
        if ($this->isEditable() && $row !== NULL) {
198
            if (!in_array('editable', $td->class)) {
199
                $td->class[] = 'editable';
200
            }
201
202
            $value = $this->editableValueCallback === NULL
203
                ? $this->getValue($row)
204
                : call_user_func_array($this->editableValueCallback, [$row, $this]);
205
206
            $td->setAttribute('data-grido-editable-value', $value);
207
        }
208
209
        return $td;
210
    }
211
212
    /**
213
     * Returns control for editation.
214
     * @returns \Nette\Forms\Controls\TextInput
215
     */
216
    public function getEditableControl()
217
    {
218
        if ($this->editableControl === NULL) {
219
            $this->editableControl = new \Nette\Forms\Controls\TextInput;
220
            $this->editableControl->controlPrototype->class[] = 'form-control';
221
        }
222
223
        return $this->editableControl;
224
    }
225
226
    /**
227
     * @return callback
228
     * @internal
229
     */
230
    public function getEditableCallback()
231
    {
232
        return $this->editableCallback;
233
    }
234
235
    /**
236
     * @return callback
237
     * @internal
238
     */
239
    public function getEditableValueCallback()
240
    {
241
        return $this->editableValueCallback;
242
    }
243
244
    /**
245
     * @return callback
246
     * @internal
247
     */
248
    public function getEditableRowCallback()
249
    {
250
        return $this->editableRowCallback;
251
    }
252
253
    /**
254
     * @return bool
255
     * @internal
256
     */
257
    public function isEditable()
258
    {
259
        return $this->editable;
260
    }
261
262
    /**
263
     * @return bool
264
     * @internal
265
     */
266
    public function isEditableDisabled()
267
    {
268
        return $this->editableDisabled;
269
    }
270
271
    /**********************************************************************************************/
272
273
    /**
274
     * @internal
275
     */
276
    public function handleEditable($id, $newValue, $oldValue)
277
    {
278
        $this->grid->onRender($this->grid);
279
280
        if (!$this->presenter->isAjax() || !$this->isEditable()) {
281
            $this->presenter->terminate();
282
        }
283
284
        $success = $this->editableCallback
285
            ? call_user_func_array($this->editableCallback, [$id, $newValue, $oldValue, $this])
286
            : $this->grid->model->update($id, [$this->getColumn() => $newValue], $this->grid->primaryKey);
287
288
        if (is_callable($this->customRender)) {
289
            $row = $this->editableRowCallback
290
                ? call_user_func_array($this->editableRowCallback, [$id, $this])
291
                : $this->grid->model->getRow($id, $this->grid->primaryKey);
292
            $html = call_user_func_array($this->customRender, [$row]);
293
        } else {
294
            $html = $this->formatValue($newValue);
295
        }
296
297
        $payload = ['updated' => (bool) $success, 'html' => (string) $html];
298
        $response = new \Nette\Application\Responses\JsonResponse($payload);
299
        $this->presenter->sendResponse($response);
300
    }
301
302
    /**
303
     * @internal
304
     */
305
    public function handleEditableControl($value)
306
    {
307
        $this->grid->onRender($this->grid);
308
309
        if (!$this->presenter->isAjax() || !$this->isEditable()) {
310
            $this->presenter->terminate();
311
        }
312
313
        $control = $this->getEditableControl();
314
        $control->setValue($value);
315
316
        $this->getForm()->addComponent($control, 'edit' . $this->getName());
0 ignored issues
show
Documentation introduced by
$control is of type object<Nette\Forms\IControl>, but the function expects a object<Nette\ComponentModel\IComponent>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
317
318
        $response = new \Nette\Application\Responses\TextResponse($control->getControl()->render());
319
        $this->presenter->sendResponse($response);
320
    }
321
}
322