Passed
Push — master ( b3e37a...994985 )
by Evgeniy
02:42
created

Breadcrumbs::homeItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Widgets;
6
7
use InvalidArgumentException;
8
use JsonException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Html\Html;
11
use Yiisoft\Widget\Widget;
12
13
use function array_key_exists;
14
use function implode;
15
use function is_array;
16
use function strtr;
17
18
/**
19
 * Breadcrumbs displays a list of links indicating the position of the current page in the whole site hierarchy.
20
 *
21
 * For example, breadcrumbs like "Home / Sample Post / Edit" means the user is viewing an edit page for the
22
 * "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home" to return to the homepage.
23
 *
24
 * To use Breadcrumbs, you need to configure its {@see Breadcrumbs::items()} method,
25
 * which specifies the links to be displayed. For example:
26
 *
27
 * ```php
28
 * // $this is the view object currently being used
29
 * echo Breadcrumbs::widget()
30
 *     -> itemTemplate() => "<li><i>{link}</i></li>\n", // template for all links
31
 *     -> links() => [
32
 *         [
33
 *             'label' => 'Post Category',
34
 *             'url' => 'post-category/view?id=10',
35
 *             'template' => "<li><b>{link}</b></li>\n", // template for this link only
36
 *         ],
37
 *         ['label' => 'Sample Post', 'url' => 'post/edit?id=1',
38
 *         'Edit',
39
 *     ];
40
 * ```
41
 *
42
 * Because breadcrumbs usually appears in nearly every page of a website, you may consider placing it in a layout view.
43
 * You can use a view common parameter (e.g. `$this->getCommonParameter('breadcrumbs')`) to configure the links in
44
 * different views. In the layout view, you assign this view parameter to the {@see Breadcrumbs::items()} method
45
 * like the following:
46
 *
47
 * ```php
48
 * // $this is the view object currently being used
49
 * echo Breadcrumbs::widget()
50
 *     ->links($this->hasCommonParameter('breadcrumbs') ? $this->getCommonParameter('breadcrumbs') : []);
51
 * ```
52
 */
53
final class Breadcrumbs extends Widget
54
{
55
    private string $tag = 'ul';
56
    private array $options = ['class' => 'breadcrumb'];
57
    private bool $encodeLabels = true;
58
    private bool $withoutHomeItem = false;
59
    private array $homeItem = [];
60
    private array $items = [];
61
    private string $itemTemplate = "<li>{link}</li>\n";
62
    private string $activeItemTemplate = "<li class=\"active\">{link}</li>\n";
63
64
    /**
65
     * Returns a new instance with the specified tag.
66
     *
67
     * @param string $value The tag name.
68
     *
69
     * @return self
70
     */
71 4
    public function tag(string $value): self
72
    {
73 4
        $new = clone $this;
74 4
        $new->tag = $value;
75 4
        return $new;
76
    }
77
78
    /**
79
     * Disables encoding for labels and returns a new instance..
80
     *
81
     * @return self Whether the labels for menu items should be HTML-encoded.
82
     */
83 2
    public function withoutEncodeLabels(): self
84
    {
85 2
        $new = clone $this;
86 2
        $new->encodeLabels = false;
87 2
        return $new;
88
    }
89
90
    /**
91
     * Disables rendering of the home item and returns a new instance.
92
     *
93
     * @return self
94
     */
95 6
    public function withoutHomeItem(): self
96
    {
97 6
        $new = clone $this;
98 6
        $new->withoutHomeItem = true;
99 6
        return $new;
100
    }
101
102
    /**
103
     * Returns a new instance with the specified first item in the breadcrumbs (called home link).
104
     *
105
     * @param array $value Please refer to {@see items()} on the format.
106
     *
107
     * @return self
108
     */
109 2
    public function homeItem(array $value): self
110
    {
111 2
        $new = clone $this;
112 2
        $new->homeItem = $value;
113 2
        return $new;
114
    }
115
116
    /**
117
     * Returns a new instance with the specified list of items.
118
     *
119
     * @param array $value List of items to appear in the breadcrumbs. If this property is empty, the widget will not
120
     * render anything. Each array element represents a single link in the breadcrumbs with the following structure:
121
     *
122
     * ```php
123
     * [
124
     *     'label' => 'label of the link',  // required
125
     *     'url' => 'url of the link',      // optional
126
     *     'template' => 'own template of the item', // optional, if not set $this->itemTemplate will be used
127
     * ]
128
     * ```
129
     *
130
     * If a link is active, you only need to specify its "label", and instead of writing `['label' => $label]`, you may
131
     * simply use `$label`.
132
     *
133
     * Additional array elements for each link will be treated as the HTML attributes for the hyperlink tag.
134
     * For example, the following link specification will generate a hyperlink with CSS class `external`:
135
     *
136
     * ```php
137
     * [
138
     *     'label' => 'demo',
139
     *     'url' => 'http://example.com',
140
     *     'class' => 'external',
141
     * ]
142
     * ```
143
     *
144
     * Each individual link can override global {@see encodeLabels} param like the following:
145
     *
146
     * ```php
147
     * [
148
     *     'label' => '<strong>Hello!</strong>',
149
     *     'encode' => false,
150
     * ]
151
     * ```
152
     *
153
     * @return self
154
     */
155 9
    public function items(array $value): self
156
    {
157 9
        $new = clone $this;
158 9
        $new->items = $value;
159 9
        return $new;
160
    }
161
162
    /**
163
     * @param array $value The HTML attributes for the menu's container tag. The following special options are
164
     * recognized:
165
     *
166
     * - tag: string, defaults to "ul", the tag name of the item container tags. Set to false to disable container tag.
167
     *   See also {@see \Yiisoft\Html\Html::tag()}.
168
     *
169
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
170
     *
171
     * @return self
172
     */
173 5
    public function options(array $value): self
174
    {
175 5
        $new = clone $this;
176 5
        $new->options = $value;
177 5
        return $new;
178
    }
179
180
    /**
181
     * Returns a new instance with the specified item template.
182
     *
183
     * @param string $value The template used to render each inactive item in the breadcrumbs.
184
     * The token `{link}` will be replaced with the actual HTML link for each inactive item.
185
     *
186
     * @return self
187
     */
188 2
    public function itemTemplate(string $value): self
189
    {
190 2
        $new = clone $this;
191 2
        $new->itemTemplate = $value;
192 2
        return $new;
193
    }
194
195
    /**
196
     * Returns a new instance with the specified active item template.
197
     *
198
     * @param string $value The template used to render each active item in the breadcrumbs.
199
     * The token `{link}` will be replaced with the actual HTML link for each active item.
200
     *
201
     * @return self
202
     */
203 4
    public function activeItemTemplate(string $value): self
204
    {
205 4
        $new = clone $this;
206 4
        $new->activeItemTemplate = $value;
207 4
        return $new;
208
    }
209
210
    /**
211
     * Renders the widget.
212
     *
213
     * @throws JsonException
214
     *
215
     * @return string The result of widget execution to be outputted.
216
     */
217 9
    protected function run(): string
218
    {
219 9
        if (empty($this->items)) {
220 1
            return '';
221
        }
222
223 8
        $items = [];
224
225 8
        if ($this->withoutHomeItem === false) {
226 3
            $items[] = $this->renderHomeLink();
227
        }
228
229 8
        foreach ($this->items as $item) {
230 8
            if (!is_array($item)) {
231 7
                $item = ['label' => $item];
232
            }
233
234 8
            if (!empty($item)) {
235 8
                $items[] = $this->renderItem(
236 8
                    $item,
237 8
                    isset($item['url']) ? $this->itemTemplate : $this->activeItemTemplate
238
                );
239
            }
240
        }
241
242 7
        $body = implode('', $items);
243
244 7
        return empty($this->tag)
245 2
            ? $body
246 7
            : Html::tag($this->tag, $body, $this->options)->encode(false)->render()
247
            ;
248
    }
249
250
    /**
251
     * Renders a single breadcrumb item.
252
     *
253
     * @param array $item The item to be rendered. It must contain the "label" element. The "url" element is optional.
254
     * @param string $template the template to be used to rendered the link. The token "{link}" will be replaced by the
255
     * link.
256
     *
257
     * @throws InvalidArgumentException|JsonException if `$item` does not have "label" element.
258
     *
259
     * @return string The rendering result.
260
     */
261 8
    private function renderItem(array $item, string $template): string
262
    {
263 8
        if (!array_key_exists('label', $item)) {
264 1
            throw new InvalidArgumentException('The "label" element is required for each link.');
265
        }
266
267 7
        $encodeLabel = ArrayHelper::remove($item, 'encode', $this->encodeLabels);
268 7
        $label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
269
270 7
        if (isset($item['template'])) {
271
            $template = $item['template'];
272
        }
273
274 7
        if (isset($item['url'])) {
275 2
            $options = $item;
276 2
            unset($options['template'], $options['label'], $options['url']);
277 2
            $item = Html::a($label, $item['url'], $options);
278
        } else {
279 7
            $item = $label;
280
        }
281
282 7
        return strtr($template, ['{link}' => $item]);
283
    }
284
285
    /**
286
     * Renders a home item.
287
     *
288
     * @throws JsonException
289
     *
290
     * @return string The rendering result.
291
     */
292 3
    private function renderHomeLink(): string
293
    {
294 3
        if ($this->homeItem === []) {
295 2
            $this->homeItem = ['label' => 'Home', 'url' => '/'];
296
        }
297
298 3
        return $this->renderItem($this->homeItem, $this->itemTemplate);
299
    }
300
}
301