BreadcrumbsFilter::beforeAction()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
The MIT License (MIT)
5
6
Copyright (c) 2016 yii2-tools <[email protected]>
7
8
Permission is hereby granted, free of charge, to any person obtaining a copy
9
of this software and associated documentation files (the "Software"), to deal
10
in the Software without restriction, including without limitation the rights
11
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
copies of the Software, and to permit persons to whom the Software is
13
furnished to do so, subject to the following conditions:
14
15
The above copyright notice and this permission notice shall be included in all
16
copies or substantial portions of the Software.
17
18
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
SOFTWARE.
25
*/
26
27
namespace yii\tools\filters;
28
29
use Yii;
30
use yii\helpers\Url;
31
use yii\base\UnknownMethodException;
32
use yii\base\UnknownPropertyException;
33
use yii\base\ActionFilter;
34
use yii\di\Instance;
35
use yii\web\Controller as WebController;
36
37
/**
38
 * Filter which automatically append module as breadcrumb item if it exists in requested route
39
 * If module-owner exists in current route, it will be added in breadcrumb widget config
40
 * (default: Yii::$app->controller->view->params['breadcrumbs'])
41
 *
42
 * Usage example:
43
 *
44
 *     public function behaviors()
45
 *     {
46
 *         return array_merge(parent::behaviors(), [
47
 *             'breadcrumbs' => [
48
 *                 'class' => BreadcrumbsFilter::className(),
49
 *             ]
50
 *         ]);
51
 *     }
52
 *
53
 * Example owners in use cases:
54
 *      yii\base\Module (recommended)
55
 *      yii\base\Component, with implemented 'getUniqueId()'
56
 *      Object, with routeCreator as callable function
57
 */
58
class BreadcrumbsFilter extends ActionFilter
59
{
60
    /**
61
     * Param name of current controller's View
62
     * which will be passed as input array for breadcrumb widget
63
     *
64
     * i.e. $view->params[$breadcrumbsParam] = [breadcrumbs config array ...]
65
     *
66
     * @var string
67
     */
68
    public $breadcrumbsParam = 'breadcrumbs';
69
70
    /**
71
     * Empty as default value, means what native php array numering used
72
     * If defined, breadcrumb config array becomes like that:
73
     *
74
     * [
75
     *     $breadcrumbsKey => [
76
     *         'label' => ...,
77
     *         'url' => ...
78
     *     ]
79
     * ]
80
     *
81
     * Useful if you have some override logic in your views, example:
82
     *
83
     * { // View context
84
     *     $this->params['breadcrumbs']['module'] = ...
85
     * }
86
     *
87
     * @var string
88
     */
89
    public $breadcrumbsKey = '';
90
91
    /**
92
     * Component field, used as label for breadcrumb widget record
93
     * @var string
94
     */
95
    public $labelParam = 'id';
96
97
    /**
98
     * If presents, used instead of $labelParam for constructing breadcrumb
99
     * @var string
100
     */
101
    public $label = '';
102
103
    /**
104
     * If default route matches current route
105
     * breadcrumb will not be active (Url parameter in config will be omitted)
106
     *
107
     * How it works:
108
     *      /news/default/create => Home / News / Create
109
     *      /news/default/index  => Home / News
110
     *                                instead of:
111
     *                              Home / News / Index
112
     *
113
     * Override this property to FALSE if you don't need such behavior
114
     * and want to always see full detailed breadcrumbs navigation
115
     * even if it's default module page
116
     * @var string
117
     */
118
    public $defaultRoute = 'default/index';
119
120
    /**
121
     * Array of strings-routes what should be ignored in breadcrumb navigation
122
     * Example: ['site/default/index', 'index', 'ind']
123
     * Comparation of current route with $exceptRoutes performs by strpos() function
124
     * ['*'] stands for "don't show me in breadcrumbs completely"
125
     * @var array
126
     */
127
    public $exceptRoutes = [];
128
129
    /**
130
     * Used for Url generation as second parameter of breadcrumb widget config
131
     * May be a callable which received $filter as arguments:
132
     *
133
     * function ($filter) {
134
     *     return $filter->owner->getUniqueId();
135
     * }
136
     *
137
     * Callable function should return full route to filter's owner, example:
138
     * Module 'users' is a submodule of 'admin', callable returns:
139
     *
140
     * 'admin/users' or null
141
     *
142
     * Returning null means what this breadcrumb shouldn't contain link,
143
     * what makes it active by widget
144
     *
145
     * @var string|callable
146
     */
147
    public $routeCreator = 'getUniqueId';
148
149
    /**
150
     * Identifies that breadcrumbs for this owner is already registered (for current request).
151
     * @var bool
152
     */
153
    private $ready = false;
154
155
    /**
156
     * @param \yii\base\Action $action
157
     * @return bool
158
     */
159
    public function beforeAction($action)
160
    {
161
        if (Yii::$app->controller instanceof WebController && !$this->ready) {
162
            $this->buildBreadcrumbs();
163
            $this->ready = true;
164
        }
165
166
        return parent::beforeAction($action);
167
    }
168
169
    /**
170
     * Appends owner's breadcrumb for current controller's view
171
     */
172
    protected function buildBreadcrumbs()
173
    {
174
        if (!empty($this->exceptRoutes) && $this->reject()) return;
175
        $params = $this->getBreadcrumbs();
176
        if (!empty($this->breadcrumbsKey)) {
177
            Yii::$app->controller->getView()->params[$this->breadcrumbsParam][$this->breadcrumbsKey] = $params;
178
        } else {
179
            Yii::$app->controller->getView()->params[$this->breadcrumbsParam][] = $params;
180
        }
181
    }
182
183
    /**
184
     * You can call this method directly, for example:
185
     *
186
     * ```
187
     * $breadcrumbItem = $module->getBreadcrumbs();
188
     * ```
189
     *
190
     * Note: this method returns breadcrumbs for owner
191
     * without checking property $exceptRoutes
192
     * @return array
193
     */
194
    public function getBreadcrumbs()
195
    {
196
        $breadcrumb = [
197
            'label' => $this->buildBreadcrumbLabel()
198
        ];
199
        if ($this->isActiveBreadcrumb()) {
200
            // for compapability with custom breadcrumbs widgets.
201
            $breadcrumb['options'] = ['class' => 'active'];
202
        } else {
203
            $breadcrumb['url'] = $this->buildBreadcrumbUrl();
204
        }
205
206
        return $breadcrumb;
207
    }
208
209
    /**
210
     * @return string
211
     * @throws \yii\base\UnknownPropertyException
212
     */
213
    protected function buildBreadcrumbLabel()
214
    {
215
        if (!empty($this->label)) {
216
            return $this->label;
217
        }
218
        if (!$this->owner->hasProperty($this->labelParam)) {
219
            throw new UnknownPropertyException("BreadcrumbsFilter's owner should provide property '"
220
                . $this->labelParam . "'");
221
        }
222
223
        return $this->owner->{$this->labelParam};
224
    }
225
226
    /**
227
     * @return null|string
228
     * @throws \yii\base\UnknownMethodException
229
     * @throws \yii\base\UnknownPropertyException
230
     */
231
    protected function buildBreadcrumbUrl()
232
    {
233
        return ($route = $this->buildBreadcrumbRoute()) ? Url::to(['/' . $route]) : null;
234
    }
235
236
    protected function isActiveBreadcrumb()
237
    {
238
        if ($this->defaultRoute === false) return false;
0 ignored issues
show
introduced by
The condition $this->defaultRoute === false is always false.
Loading history...
239
        if ($this->owner->id === Yii::$app->controller->module->id) {
240
            $relativeRoute = Yii::$app->controller->id . '/' . Yii::$app->controller->action->id;
241
242
            return strpos($this->defaultRoute, $relativeRoute) !== false;
243
        }
244
245
        return false;
246
    }
247
248
    protected function buildBreadcrumbRoute()
249
    {
250
        if (is_string($this->routeCreator)) {
251
            if (!$this->owner->hasMethod('getUniqueId')) {
252
                throw new UnknownMethodException("BreadcrumbsFilter's owner should provide method 'getUniqueId'");
253
            }
254
255
            return $this->owner->{$this->routeCreator}();
256
        }
257
        if (is_callable($this->routeCreator)) {
258
            return call_user_func($this->routeCreator, $this);
259
        }
260
261
        throw new UnknownPropertyException("BreadcrumbsFilter's should be configured with valid routeCreator"
262
            . " (owner's method name or callable)");
263
    }
264
265
    /**
266
     * Rejects breadcrumbs creation for current route
267
     * Performs by sequentually checks of $exceptRoutes array
268
     */
269
    protected function reject()
270
    {
271
        foreach ($this->exceptRoutes as $route) {
272
            if ($route === '*' || strpos(Yii::$app->requestedRoute, $route) !== false) {
273
                return true;
274
            }
275
        }
276
277
        return false;
278
    }
279
}