ListView   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 271
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 21
eloc 70
c 3
b 0
f 0
dl 0
loc 271
ccs 78
cts 78
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A renderAfterItem() 0 9 2
A itemViewAttributes() 0 6 1
A renderItems() 0 22 4
A separator() 0 6 1
A beforeItem() 0 6 1
A getWebView() 0 7 2
A renderBeforeItem() 0 9 2
A afterItem() 0 6 1
A webView() 0 6 1
A itemView() 0 6 1
A viewParams() 0 6 1
A renderItem() 0 32 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\DataView;
6
7
use Closure;
8
use InvalidArgumentException;
9
use Throwable;
10
use Yiisoft\Html\Tag\Div;
11
use Yiisoft\View\Exception\ViewNotFoundException;
12
use Yiisoft\View\WebView;
13
use Yiisoft\Yii\DataView\Exception\WebViewNotSetException;
14
15
/**
16
 * The ListView widget is used to display data from data provider. Each data model is rendered using the view specified.
17
 */
18
final class ListView extends BaseListView
19
{
20
    private Closure $afterItem;
21
    private Closure $beforeItem;
22
    /** @var callable|string|null */
23
    private $itemView = null;
24
    private array $itemViewAttributes = [];
25
    private string $separator = "\n";
26
    private array $viewParams = [];
27
    private WebView|null $webView = null;
28
29
    /**
30
     * Return new instance with afterItem closure.
31
     *
32
     * @param Closure $value an anonymous function that is called once after rendering each data.
33
     *
34
     * It should have the same signature as {@see beforeItem}.
35
     *
36
     * The return result of the function will be rendered directly.
37
     *
38
     * Note: If the function returns `null`, nothing will be rendered after the item.
39
     *
40
     * {@see renderAfterItem}
41
     */
42 2
    public function afterItem(Closure $value): self
43
    {
44 2
        $new = clone $this;
45 2
        $new->afterItem = $value;
46
47 2
        return $new;
48
    }
49
50
    /**
51
     * Return new instance with beforeItem closure.
52
     *
53
     * @param Closure $value an anonymous function that is called once before rendering each data.
54
     *
55
     * It should have the following signature:
56
     *
57
     * ```php
58
     * function ($data, $key, $index, $widget)
59
     * ```
60
     *
61
     * - `$data`: The current data being rendered.
62
     * - `$key`: The key value associated with the current data.
63
     * - `$index`: The zero-based index of the data in the array.
64
     * - `$widget`: The list view object.
65
     *
66
     * The return result of the function will be rendered directly.
67
     *
68
     * Note: If the function returns `null`, nothing will be rendered before the item.
69
     *
70
     * {@see renderBeforeItem}
71
     */
72 2
    public function beforeItem(Closure $value): self
73
    {
74 2
        $new = clone $this;
75 2
        $new->beforeItem = $value;
76
77 2
        return $new;
78
    }
79
80 6
    public function getWebView(): WebView
81
    {
82 6
        if ($this->webView === null) {
83 1
            throw new WebViewNotSetException();
84
        }
85
86 5
        return $this->webView;
87
    }
88
89
    /**
90
     * Return new instance with itemView closure.
91
     *
92
     * @param Closure|string $value the name of the view for rendering each data item, or a callback (e.g. an anonymous
93
     * function) for rendering each data item. If it specifies a view name, the following variables will be available in
94
     * the view:
95
     *
96
     * - `$data`: The data model.
97
     * - `$key`: The key value associated with the data item.
98
     * - `$index`: The zero-based index of the data item in the items array.
99
     * - `$widget`: The list view widget instance.
100
     *
101
     * Note that the view name is resolved into the view file by the current context of the {@see view} object.
102
     *
103
     * If this property is specified as a callback, it should have the following signature:
104
     *
105
     * ```php
106
     * function ($data, $key, $index, $widget)
107
     * ```
108
     */
109 9
    public function itemView(string|Closure $value): self
110
    {
111 9
        $new = clone $this;
112 9
        $new->itemView = $value;
113
114 9
        return $new;
115
    }
116
117
    /**
118
     * return new instance with the HTML attributes for the container of item view.
119
     *
120
     * @param array $values Attribute values indexed by attribute names.
121
     */
122 2
    public function itemViewAttributes(array $values): self
123
    {
124 2
        $new = clone $this;
125 2
        $new->itemViewAttributes = $values;
126
127 2
        return $new;
128
    }
129
130
    /**
131
     * Return new instance with the separator between the items.
132
     *
133
     * @param string $separator the HTML code to be displayed between any two consecutive items.
134
     */
135 3
    public function separator(string $separator): self
136
    {
137 3
        $new = clone $this;
138 3
        $new->separator = $separator;
139
140 3
        return $new;
141
    }
142
143
    /**
144
     * Return new instance with the parameters for the view.
145
     *
146
     * @param array $viewParams additional parameters to be passed to {@see itemView} when it is being rendered.
147
     *
148
     * This property is used only when {@see itemView} is a string representing a view name.
149
     */
150 2
    public function viewParams(array $viewParams): self
151
    {
152 2
        $new = clone $this;
153 2
        $new->viewParams = $viewParams;
154
155 2
        return $new;
156
    }
157
158
    /**
159
     * Return new instance with the WebView object.
160
     *
161
     * @param WebView $value the WebView object.
162
     */
163 7
    public function webView(WebView $value): self
164
    {
165 7
        $new = clone $this;
166 7
        $new->webView = $value;
167
168 7
        return $new;
169
    }
170
171
    /**
172
     * Renders a single data model.
173
     *
174
     * @param array|object $data The data to be rendered.
175
     * @param mixed $key The key value associated with the data.
176
     * @param int $index The zero-based index of the data array.
177
     *
178
     * @throws Throwable|ViewNotFoundException
179
     */
180 8
    protected function renderItem(array|object $data, mixed $key, int $index): string
181
    {
182 8
        $content = '';
183
184 8
        if ($this->itemView === null) {
185 1
            throw new InvalidArgumentException('The "itemView" property must be set.');
186
        }
187
188 7
        if (is_string($this->itemView)) {
189 6
            $content = $this->getWebView()->render(
190 6
                $this->itemView,
191 6
                array_merge(
192 6
                    [
193 6
                        'data' => $data,
194 6
                        'index' => $index,
195 6
                        'key' => $key,
196 6
                        'widget' => $this,
197 6
                    ],
198 6
                    $this->viewParams
199 6
                )
200 6
            );
201
        }
202
203 6
        if ($this->itemView instanceof Closure) {
204 1
            $content = (string) call_user_func($this->itemView, $data, $key, $index, $this);
205
        }
206
207 6
        return Div::tag()
208 6
            ->attributes($this->itemViewAttributes)
209 6
            ->content(PHP_EOL . $content)
210 6
            ->encode(false)
211 6
            ->render();
212
    }
213
214
    /**
215
     * Renders all data models.
216
     *
217
     * @throws Throwable|ViewNotFoundException
218
     */
219 8
    protected function renderItems(): string
220
    {
221 8
        $items = $this->getItems();
222 8
        $keys = array_keys($items);
223 8
        $rows = [];
224
225
        /** @psalm-var array<array-key,array|object> $items */
226 8
        foreach (array_values($items) as $index => $value) {
227 8
            $key = $keys[$index];
228
229 8
            if ('' !== ($before = $this->renderBeforeItem($value, $key, $index))) {
230 1
                $rows[] = $before;
231
            }
232
233 8
            $rows[] = $this->renderItem($value, $key, $index);
234
235 6
            if ('' !== ($after = $this->renderAfterItem($value, $key, $index))) {
236 1
                $rows[] = $after;
237
            }
238
        }
239
240 6
        return implode($this->separator, $rows);
241
    }
242
243
    /**
244
     * Calls {@see afterItem} closure, returns execution result.
245
     *
246
     * If {@see afterItem} is not a closure, `null` will be returned.
247
     *
248
     * @param array|object $data The data to be rendered.
249
     * @param mixed $key The key value associated with the data.
250
     * @param int $index The zero-based index of the data.
251
     *
252
     * @return string call result when {@see afterItem} is not a closure.
253
     *
254
     * {@see afterItem}
255
     */
256 6
    private function renderAfterItem(array|object $data, mixed $key, int $index): string
257
    {
258 6
        $result = '';
259
260 6
        if (!empty($this->afterItem)) {
261 1
            $result = (string) call_user_func($this->afterItem, $data, $key, $index, $this);
262
        }
263
264 6
        return $result;
265
    }
266
267
    /**
268
     * Calls {@see beforeItem} closure, returns execution result.
269
     *
270
     * If {@see beforeItem} is not a closure, `null` will be returned.
271
     *
272
     * @param array|object $data The data to be rendered.
273
     * @param mixed $key The key value associated with the data.
274
     * @param int $index The zero-based index of the data.
275
     *
276
     * @return string call result or `null` when {@see beforeItem} is not a closure.
277
     *
278
     * {@see beforeItem}
279
     */
280 8
    private function renderBeforeItem(array|object $data, mixed $key, int $index): string
281
    {
282 8
        $result = '';
283
284 8
        if (!empty($this->beforeItem)) {
285 1
            $result = (string) call_user_func($this->beforeItem, $data, $key, $index, $this);
286
        }
287
288 8
        return $result;
289
    }
290
}
291