Issues (902)

framework/grid/ActionColumn.php (3 issues)

1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\grid;
9
10
use Yii;
11
use yii\helpers\Html;
12
use yii\helpers\Url;
13
14
/**
15
 * ActionColumn is a column for the [[GridView]] widget that displays buttons for viewing and manipulating the items.
16
 *
17
 * To add an ActionColumn to the gridview, add it to the [[GridView::columns|columns]] configuration as follows:
18
 *
19
 * ```php
20
 * 'columns' => [
21
 *     // ...
22
 *     [
23
 *         'class' => ActionColumn::class,
24
 *         // you may configure additional properties here
25
 *     ],
26
 * ]
27
 * ```
28
 *
29
 * For more details and usage information on ActionColumn, see the [guide article on data widgets](guide:output-data-widgets).
30
 *
31
 * @author Qiang Xue <[email protected]>
32
 * @since 2.0
33
 */
34
class ActionColumn extends Column
35
{
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public $headerOptions = ['class' => 'action-column'];
40
    /**
41
     * @var string|null the ID of the controller that should handle the actions specified here.
42
     * If not set, it will use the currently active controller. This property is mainly used by
43
     * [[urlCreator]] to create URLs for different actions. The value of this property will be prefixed
44
     * to each action name to form the route of the action.
45
     */
46
    public $controller;
47
    /**
48
     * @var string the template used for composing each cell in the action column.
49
     * Tokens enclosed within curly brackets are treated as controller action IDs (also called *button names*
50
     * in the context of action column). They will be replaced by the corresponding button rendering callbacks
51
     * specified in [[buttons]]. For example, the token `{view}` will be replaced by the result of
52
     * the callback `buttons['view']`. If a callback cannot be found, the token will be replaced with an empty string.
53
     *
54
     * As an example, to only have the view, and update button you can add the ActionColumn to your GridView columns as follows:
55
     *
56
     * ```php
57
     * ['class' => 'yii\grid\ActionColumn', 'template' => '{view} {update}'],
58
     * ```
59
     *
60
     * @see buttons
61
     */
62
    public $template = '{view} {update} {delete}';
63
    /**
64
     * @var array button rendering callbacks. The array keys are the button names (without curly brackets),
65
     * and the values are the corresponding button rendering callbacks. The callbacks should use the following
66
     * signature:
67
     *
68
     * ```php
69
     * function ($url, $model, $key) {
70
     *     // return the button HTML code
71
     * }
72
     * ```
73
     *
74
     * where `$url` is the URL that the column creates for the button, `$model` is the model object
75
     * being rendered for the current row, and `$key` is the key of the model in the data provider array.
76
     *
77
     * You can add further conditions to the button, for example only display it, when the model is
78
     * editable (here assuming you have a status field that indicates that):
79
     *
80
     * ```php
81
     * [
82
     *     'update' => function ($url, $model, $key) {
83
     *         return $model->status === 'editable' ? Html::a('Update', $url) : '';
84
     *     },
85
     * ],
86
     * ```
87
     */
88
    public $buttons = [];
89
    /**
90
     * @var array button icons. The array keys are the icon names and the values the corresponding html:
91
     * ```php
92
     * [
93
     *     'eye-open' => '<svg ...></svg>',
94
     *     'pencil' => Html::tag('span', '', ['class' => 'glyphicon glyphicon-pencil'])
95
     * ]
96
     * ```
97
     * Defaults to FontAwesome 5 free svg icons.
98
     * @since 2.0.42
99
     * @see https://fontawesome.com
100
     */
101
    public $icons = [
102
        'eye-open' => '<svg aria-hidden="true" style="display:inline-block;font-size:inherit;height:1em;overflow:visible;vertical-align:-.125em;width:1.125em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M573 241C518 136 411 64 288 64S58 136 3 241a32 32 0 000 30c55 105 162 177 285 177s230-72 285-177a32 32 0 000-30zM288 400a144 144 0 11144-144 144 144 0 01-144 144zm0-240a95 95 0 00-25 4 48 48 0 01-67 67 96 96 0 1092-71z"/></svg>',
103
        'pencil' => '<svg aria-hidden="true" style="display:inline-block;font-size:inherit;height:1em;overflow:visible;vertical-align:-.125em;width:1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M498 142l-46 46c-5 5-13 5-17 0L324 77c-5-5-5-12 0-17l46-46c19-19 49-19 68 0l60 60c19 19 19 49 0 68zm-214-42L22 362 0 484c-3 16 12 30 28 28l122-22 262-262c5-5 5-13 0-17L301 100c-4-5-12-5-17 0zM124 340c-5-6-5-14 0-20l154-154c6-5 14-5 20 0s5 14 0 20L144 340c-6 5-14 5-20 0zm-36 84h48v36l-64 12-32-31 12-65h36v48z"/></svg>',
104
        'trash' => '<svg aria-hidden="true" style="display:inline-block;font-size:inherit;height:1em;overflow:visible;vertical-align:-.125em;width:.875em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M32 464a48 48 0 0048 48h288a48 48 0 0048-48V128H32zm272-256a16 16 0 0132 0v224a16 16 0 01-32 0zm-96 0a16 16 0 0132 0v224a16 16 0 01-32 0zm-96 0a16 16 0 0132 0v224a16 16 0 01-32 0zM432 32H312l-9-19a24 24 0 00-22-13H167a24 24 0 00-22 13l-9 19H16A16 16 0 000 48v32a16 16 0 0016 16h416a16 16 0 0016-16V48a16 16 0 00-16-16z"/></svg>'
105
    ];
106
    /** @var array visibility conditions for each button. The array keys are the button names (without curly brackets),
107
     * and the values are the boolean true/false or the anonymous function. When the button name is not specified in
108
     * this array it will be shown by default.
109
     * The callbacks must use the following signature:
110
     *
111
     * ```php
112
     * function ($model, $key, $index) {
113
     *     return $model->status === 'editable';
114
     * }
115
     * ```
116
     *
117
     * Or you can pass a boolean value:
118
     *
119
     * ```php
120
     * [
121
     *     'update' => \Yii::$app->user->can('update'),
122
     * ],
123
     * ```
124
     * @since 2.0.7
125
     */
126
    public $visibleButtons = [];
127
    /**
128
     * @var callable|null a callback that creates a button URL using the specified model information.
129
     * The signature of the callback should be the same as that of [[createUrl()]]
130
     * Since 2.0.10 it can accept additional parameter, which refers to the column instance itself:
131
     *
132
     * ```php
133
     * function (string $action, mixed $model, mixed $key, integer $index, ActionColumn $this) {
134
     *     //return string;
135
     * }
136
     * ```
137
     *
138
     * If this property is not set, button URLs will be created using [[createUrl()]].
139
     */
140
    public $urlCreator;
141
    /**
142
     * @var array html options to be applied to the [[initDefaultButton()|default button]].
143
     * @since 2.0.4
144
     */
145
    public $buttonOptions = [];
146
147
148
    /**
149
     * {@inheritdoc}
150
     */
151 2
    public function init()
152
    {
153 2
        parent::init();
154 2
        $this->initDefaultButtons();
155
    }
156
157
    /**
158
     * Initializes the default button rendering callbacks.
159
     */
160 2
    protected function initDefaultButtons()
161
    {
162 2
        $this->initDefaultButton('view', 'eye-open');
163 2
        $this->initDefaultButton('update', 'pencil');
164 2
        $this->initDefaultButton('delete', 'trash', [
165 2
            'data-confirm' => Yii::t('yii', 'Are you sure you want to delete this item?'),
166 2
            'data-method' => 'post',
167 2
        ]);
168
    }
169
170
    /**
171
     * Initializes the default button rendering callback for single button.
172
     * @param string $name Button name as it's written in template
173
     * @param string $iconName The part of Bootstrap glyphicon class that makes it unique
174
     * @param array $additionalOptions Array of additional options
175
     * @since 2.0.11
176
     */
177 2
    protected function initDefaultButton($name, $iconName, $additionalOptions = [])
178
    {
179 2
        if (!isset($this->buttons[$name]) && strpos($this->template, '{' . $name . '}') !== false) {
180 2
            $this->buttons[$name] = function ($url, $model, $key) use ($name, $iconName, $additionalOptions) {
0 ignored issues
show
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

180
            $this->buttons[$name] = function ($url, $model, /** @scrutinizer ignore-unused */ $key) use ($name, $iconName, $additionalOptions) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $model is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

180
            $this->buttons[$name] = function ($url, /** @scrutinizer ignore-unused */ $model, $key) use ($name, $iconName, $additionalOptions) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
181
                switch ($name) {
182 1
                    case 'view':
183 1
                        $title = Yii::t('yii', 'View');
184 1
                        break;
185 1
                    case 'update':
186 1
                        $title = Yii::t('yii', 'Update');
187 1
                        break;
188 1
                    case 'delete':
189 1
                        $title = Yii::t('yii', 'Delete');
190 1
                        break;
191
                    default:
192
                        $title = ucfirst($name);
193
                }
194 1
                $options = array_merge([
195 1
                    'title' => $title,
196 1
                    'aria-label' => $title,
197 1
                    'data-pjax' => '0',
198 1
                ], $additionalOptions, $this->buttonOptions);
199 1
                $icon = isset($this->icons[$iconName])
200 1
                    ? $this->icons[$iconName]
201
                    : Html::tag('span', '', ['class' => "glyphicon glyphicon-$iconName"]);
202 1
                return Html::a($icon, $url, $options);
203 2
            };
204
        }
205
    }
206
207
    /**
208
     * Creates a URL for the given action and model.
209
     * This method is called for each button and each row.
210
     * @param string $action the button name (or action ID)
211
     * @param \yii\db\ActiveRecordInterface $model the data model
212
     * @param mixed $key the key associated with the data model
213
     * @param int $index the current row index
214
     * @return string the created URL
215
     */
216 1
    public function createUrl($action, $model, $key, $index)
217
    {
218 1
        if (is_callable($this->urlCreator)) {
219 1
            return call_user_func($this->urlCreator, $action, $model, $key, $index, $this);
0 ignored issues
show
It seems like $this->urlCreator can also be of type null; however, parameter $callback of call_user_func() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

219
            return call_user_func(/** @scrutinizer ignore-type */ $this->urlCreator, $action, $model, $key, $index, $this);
Loading history...
220
        }
221
222
        $params = is_array($key) ? $key : ['id' => (string) $key];
223
        $params[0] = $this->controller ? $this->controller . '/' . $action : $action;
224
225
        return Url::toRoute($params);
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     */
231 1
    protected function renderDataCellContent($model, $key, $index)
232
    {
233 1
        return preg_replace_callback('/\\{([\w\-\/]+)\\}/', function ($matches) use ($model, $key, $index) {
234 1
            $name = $matches[1];
235
236 1
            if (isset($this->visibleButtons[$name])) {
237 1
                $isVisible = $this->visibleButtons[$name] instanceof \Closure
238 1
                    ? call_user_func($this->visibleButtons[$name], $model, $key, $index)
239 1
                    : $this->visibleButtons[$name];
240
            } else {
241 1
                $isVisible = true;
242
            }
243
244 1
            if ($isVisible && isset($this->buttons[$name])) {
245 1
                $url = $this->createUrl($name, $model, $key, $index);
246 1
                return call_user_func($this->buttons[$name], $url, $model, $key);
247
            }
248
249 1
            return '';
250 1
        }, $this->template);
251
    }
252
}
253