caffeinated / menus

:pushpin: Menu generator package for the Laravel framework
https://caffeinatedpackages.com
132 stars 59 forks source link

Placement of the menu code and the alternatives. #50

Open piperone opened 9 years ago

piperone commented 9 years ago

I appreciate that you suggest a home for the menu-specific code in ones app. However, I'm still a bit perplexed.

Your wiki's quick-start section states that it's best, by convention, to place the code in a service provider. That is all well and good... Until you want to use the filter-method to filter out menu-items based on e.g. the role of the logged in user. Session data is unavailable from a provider, so you can't really do that without a workaround of some sort. At least this was the case, last time I looked into it.

The "Basics of Menus"-section contradicts the quick-start section by stating that the ideal place for the menu-code is a middleware. I find that a middleware is more appropriate, but still not without some minor problems. For instance, if you define the menu-middleware as global and your app has some pages without the menu, like a login-page, your package will still attempt to build the menu. Basically, the route decides if the menu is created or not.

A third option, not mentioned in the docs, is a dedicated view composer. First, you need a provider to bind views and composers together. This means you get to decide, in a clean way, when a given view composer should be used. In the composer itself, you also have access to the currently logged in user, which is nice. More importantly, the view is the deciding factor for whether the menu is created or not, giving you much more granular control than the middleware approach.

How about a dedicated section in your wiki that talks about the alternatives? Laravel itself is all about the freedom to choose for oneself, after all :-)

Edit: Wording and typos.

kaidesu commented 9 years ago

Excellent points, I'm definitely up for any suggestions to improve the documentation.

Seems I forgot to update the quick start page in regards to the service provider suggestion. I'll definitely be adding a dedicated page to go over the possible locations one can register menus/menu items in - will be easier to maintain none the less!

I'll keep this issue open in the mean time until a page is added~

hcancelik commented 9 years ago

@fredrkr you can also define a route middleware and use it for certain routes. This way you can decide which pages you want to build the menu.

piperone commented 9 years ago

@hcancelik Sure, that's an option, too :-) What I said about the middleware-way still applies, though. That is, if the "menu-middleware" is defined as global, the code will be executed whether you output the menu, or not.

My personal favorite of the bunch is using a view-composer, though. It just resonates better within the vast empty space that is my head. Menus are inexorably related to views, after all. That said, there is nothing wrong with using the other approaches either :-)

ghost commented 8 years ago

A problem with View Composers: they are called multiple times during the life-cycle of Laravel. I'm thinking now on a way to avoid this.

piperone commented 8 years ago

That has never been a problem for me. I always use one top-level layout-view that all other non-partial views extend. It's this view that I hook up to the view composer that houses the Menu::make()-call. I haven't seen that code executed more than once for each request.

Under which circumstances do you think the view composer-approach would be problematic?

ghost commented 8 years ago

I had a problem with this yesterday and i'll try to explain:

Obs: Sorry for the giant text, at the end, plz tell me if you understood the approach

I'm making a submenu to be rendered on multiple views, automatically. To "construct" the structure of this menu, i created the middleware "MySubMenuMiddleware". This middleware has the following responsibilities:

  1. Init this Menu package with:

    $this->submenu = \Caffeinated\Menus\Menu::make('submenu', function($menu){ })

  2. Identify all items from this specific menu from a source. (i can use Database, external files with JSON's, Arrays, etc, it doesn't matter, on condition that i have those itens with a "Title" and an optional "URL")
  3. Add each item on the initial Menu: $this->submenu->add(SOURCE_TITLE, SOURCE_URL)

The menu built by this middleware is the default submenu to be rendered on all my views, so, my top level layout holds a partial.submenu.blade.php file. It something almost like the example from the wiki https://github.com/caffeinated/menus/wiki/Blade-Bootstrap-Template

Nice, it's working. But now comes the problem:

Imagine that my default submenu has only two items: Do Action 1 and Do Action 2. Besides those default submenu items, i need to ADD a new item, BUT ONLY for a specific page. (Let's call it custompage.blade.php).

To be able to do that, i need get the menu and modify him before the partial.submenu.blade.php be rendered.

Here i have two options (please notify me if we have others possibilities):

If i use the Controller everything works fine, only one CUSTOM_TITLE item is add:

class CustomPageController extends Controller
{
    public function index()
    {
        $submenu = \Caffeinated\Menus\Menu::get('submenu'); // remember, that's the name we use above to Make this menu
        $submenu->add('CUSTOM_TITLE', 'CUSTOM URL');
        return view('custompage');
    }
 }

But if i use the ViewComposer, whenever the CustomPageViewComposer is fired, my code will add this custom item. ( and this will depend on how many partials you have, cause it will be fired whenever a view is called).

class AppServiceProvider extends ServiceProvider
{
    public function boot(ViewFactory $view)
    {
        $view->composer('otherpages::*', 'App\Http\ViewComposers\CustomItemsComposer');
    }
}
class CustomItemsComposer
{
    public function compose(View $view)
    {
        // the same code as the controller
        $submenu = \Caffeinated\Menus\Menu::get('submenu');
        $submenu->add('CUSTOM_TITLE', 'CUSTOM URL');
    }
}