'items' => [
// Important: you need to specify url as 'controller/action',
// not just as 'controller' even if default action is used.
['label' => 'Home', 'url' => ['site/index']],
// 'Products' menu item will be selected as long as the route is 'product/index'
['label' => 'Products', 'url' => ['product/index'], 'items' => [
['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']],
['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']],
['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],
How about using a MenuItem class instead of putting everything in associative arrays? Arrays like these are not type safe and does not communicate intent. Further more, you can’t use inheritance to make subclasses of different types of menu items. Associative arrays lead to spaghetti code and does not scale well.
Edit: Some links:
Edit 2: One patchy solution to this could be using objects with ArrayAccess implemented.
Edit 3: Remember that our project is HUGE. First commit from 2003, 200+ contributors. It’s not a web page, but a scientific web app.
If you had been around since the very beginning of Yii, when @qiang started writing Yii after having created PRADO, you’d know that there are good reasons for Yii using e.g. arrays for things like this and many other parts of Yii as well. It was intentionally made more lightweight.
If you think about it, it could have abstracted a lot of other arrays as well into classes of different type. But it hasn’t done that, and I think it’s good. There’s no point overengineering.
Instead, you are completely free to create classes for this if you want or need to, and as you mentioned yourself, just make them arrayable. That should solve your problem unless I’m missing something.
The point I’m trying to make is that Yii is a framework that is pragmatic yet nicely designed, but part of this is not overengineering such as in this case. Simply go ahead and extend your menu stuff with classes if you want to. This makes sense in your large project, but it doesn’t make much sense for the tons of smaller projects that have a simpler menu.
Yep, it’s not always easy or obvious which kind of interfaces are correct, or how to design interfaces. In my screenshot above I’m passing an array $options to the constructor. This still has several benefits over associative arrays, like default values, validation, immutability, encapsulation and type-safety (even if it’s mostly a “tag” on a data package). Named arguments would probably make the $options array obsolete.