Closed imanghafoori1 closed 6 years ago
(I'll presume that the controller in your example is named DashboardController).
How about the possibility to execute child-requests, where your view has a @childAction('WidgetController@getWidgetHtml')
?.
Example: https://gist.github.com/sisve/13cb9438ff74eb21f676a8bc3dcd11a0
1) SRP: The Widget is handled by WidgetController, not DashboardController. Only the dashboard view needs to be changed to add the widget, no changes at all to DashboardController. 4) Lazy execution. Nothing widget-related is executed unless that @childAction calls it. 6) Nested widgets.
It does not... 3) Cache the widget. That's something that the WidgetController may do when appropriate. 4) Minify the output. That's something that could be applied on a general level by minifying the blade compilation; see https://github.com/GrahamCampbell/Laravel-HTMLMin It's not something that needs to be done per-render.
I do not understand your reference to the open/closed principle. It's often used for class hierarchies (and inheritance), but I do not see how it works here.
Good questions :
Why should a DashboardController know which different widget that the view optionally uses?
Because some people like to see their controllers and immediately get a bird eye view to the rest of execution flow of their app. Of course, It is absolutely possible to just use the widgets classes right within the views with a static call with minimum change on the base class.
Why should any widget-related code execute at all when running the DashboardController::index()?
We tend to put cheap code in the constructor and expensive code (like DB queries) in the data method.
How would dependency injection work with RecentProductsWidget?
I think it can be done by using the laravel App::call() in the base class. That is easily possible. No big deal.
Cache the widget. That's something that the WidgetController may...
Of course but it quickly leads to duplicate code in the widgets which is better to be in the BaseWidget class and DRYed out.
I do not understand your reference to the open/closed principle. It's often used for class hierarchies (and inheritance), but I do not see how it works here.
Suppose that we have a lot of widgets on the page with a lot of logic and we want to:
Add
yet another one. If we are using the current MVC model we need to write our code in the same controller method that contains the previously written code. but in this approach we just create a brand new widget class from scratch and start to code in it. (While other members of the team are happy to work on their on isolated files and commit their changes without creating conflicts.)
Remove
some widget. then by removing the {!! $myWidget !!} all the database queries will be disabled without need for commenting out the queries in the controller.
Modify
some widget. Then any new comer to our team (when open up the widget class) has to study only with little amount of code that is related specifically to that widget. (in the current MVC model the developer has to idea which piece of controller code is related to which widget on the page so he has to study the whole controller action... )
When to use it ?
So what is our problem ?
Problem 1 :
Problem 2 :
How this concept is going to help us ?
single responsibility principle
) in your controllers (Because each widget class is only responsible for one and only one widget of the page but before you had a single controller method that was responsible for all the widgets. Effectively exploding one controller method into multiple widget classes.)Open-closed principle
. (Because if you want to add a widget on your page you do not need to touch the controller code. Instead you create a new widget class from scratch.)caches the output
of each widget. (which give a very powerful, flexible and easy to use caching opportunity) You can set different cache config for each part of the page. Similar toESI
standard.Lazily
. Meaning the the widget's data methodprotected function data(){
is hit only and only after the widget object is forced to be rendered in the blade file like this:{!! $widgetObj !!}
, So for example if you comment out{!! $widgetObj !!}
from your blade file then all database queries will be disabled automatically. No need to comment out the controller codes anymore...minifies the output
of the widget. Removing the white spaces.nested widgets
tree structure. (use can inject and use widgets within widgets)Usage:
Guideline:
How to create a Widget?
Sample widget class :
==============
views/widgets/recentProducts.blade.php
Ok, Now it's done! We have a ready to use widget. let's use it...
How to use a Widget?
In your typical controller method we should instanciate our widget class and pass the object to our view:
And then you can render it in your view (home.blade.php) like this:
=============
In order to understand what's going on here... Think of
{!! $recentProductsWidget !!}
as@include('widgets.recentProductsWidget')
but more sophisticated. The actual result is the same piece of HTML, which is the result of rendering the partial.Pro tip: After
{!! $myWidget('param1') !!}
is executed in your view file, thedata
method is called on your widget class with the corresponding parameters.But only if it is Not already cached
or theprotected $cacheLifeTime
is set to 0. If the widget HTML output is already in the cache it prints out the HTML with out executingdata
method (hence performing database queries) or even rendering the blade file.===============
BaseWidget class code (This class is only user tested in production.) https://github.com/imanghafoori1/laravel-widgetize https://github.com/imanghafoori1/laravel-widgetize https://github.com/imanghafoori1/laravel-widgetize