Package Data | |
---|---|
Maintainer Username: | imanghafoori1 |
Maintainer Contact: | imanghafoori1@gmail.com (Iman Ghafoori) |
Package Create Date: | 2017-02-04 |
Package Last Update: | 2024-06-15 |
Home Page: | |
Language: | PHP |
License: | MIT |
Last Refreshed: | 2024-12-22 03:16:28 |
Package Statistics | |
---|---|
Total Downloads: | 121,640 |
Monthly Downloads: | 904 |
Daily Downloads: | 3 |
Total Stars: | 902 |
Total Watchers: | 28 |
Total Forks: | 73 |
Total Open Issues: | 4 |
:ribbon::ribbon: "cleaner code" :heavy_plus_sign: "easy caching" :ribbon::ribbon:
:flashlight: Introduction
:wrench: Installation
:earth_africa: Global Configuration
:blue_car: Per Widget Configuration
:bulb: Usage and Example
More readings:
:shipit: Some Theory for Experts
:star: Your Stars Makes Us Do More
This page may look long and boring to read at first, but bear with me!!!
I bet if you read through it you won't get disappointed at the end.So let's Go... :horse_racing:
And to show the information you need.. It is :
out of the box !
This concept (this design pattern) really shines when you want to create crowded web pages with multiple sections (on sidebar, menu, carousels ...) and each widget needs separate sql queries and php logic to be provided with data for its template. Anyway installing it has minimal overhead since surprisingly it is just a small abstract class and Of course you can use it to refactor your monster code and tame it into managable pieces or boost the performance 4x-5x times faster! :dizzy:
You can think of a widget as a blade partial (which know how to provide data for itself.)
Or If you know Drupal's Views
concept, they are very similar.
You can include @widget('myWidget')
within your blade files and it will turn into HTML
!!!
So you can replace @include('myPartial')
with @widget('myWidget')
in our laravel applications.
- It optionally
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.- It optionally
minifies
the output of the widget.- It shows Debug info for your widgets as html title="" attributes.
- php artisan make:widget command
- It helps you to have a dedicated presenter class of each widget to clean up your views.
- It extends the Route facade with
Route::view
,Route::jsonWidget
,Route::widget
Given that we have disabled caching in the widgetize config file...
1 - It first looks for "SomeWidget" class to inspect it.
2 - Then calls the widget's controller method and gets some data from it.
3 - Using that data it "compiles" (in other word "renders") the blade file ($template). (to produce some html)
4 - At last, (If caching is enabled for the widget) it puts a copy of the resulting html in cache, for future use and return it.
You might think that "view composers" are already doing the job, so why "widgets" ?
1- The worst thing about view composers is you never know which composer is attached to a @include not to mention other members of your team.
2- You have no way of passing data to the compose() method from your view. They receive a \Illuminate\View\View object. so they can not be re-used to expose json data. widgetize designed to provide fully freedom and resuability for widget-controllers.
public function compose(View $view)
{
$view->with('count', $this->users->count());
}
3- $view->with('count', $this->users->count()); This exposes the $count to all views not just the partial. And this violates encapsulation.
4- They offer no caching out of the box.
You can use :
php artisan make:widget MyWidget
to make your widget class.
Sample widget class :
namespace App\Widgets;
class MyWidget
{
// The data returned here would be available in widget view file automatically.
public function data($my_param=5)
{
// It's the perfect place to query the database for your widget...
return Product::orderBy('id', 'desc')->take($my_param)->get();
}
}
App\Widgets\MyWidgetView.blade.php :
<ul>
@foreach($data as $product)
<li>
{{ $product->title }}
</li>
@endforeach
Note that it is perfectly ok to use an other widget here
@widget('AnOtherWidget')
</ul>
Ok, Now it's done! We have a ready to use widget. let's use it...
In a normal day to day view (middle-end):
<html>
<head></head>
<body>
<h1>Hello {{ auth()->user()->username }} </h1> <!-- not cached -->
@widget('RecentProductsWidget') <!-- Here we send request to back-end to get HTML -->
<body>
</html>
All of us, more or less have some ajax experience. One scenario is to lazy load a page partial after
the page has been fully loaded.
You can think of @widget() as an ajax call from "middle-end" to the "back-end" to load a piece of HTML
into the page.
composer require imanghafoori/laravel-widgetize
:electric_plug: (For Laravel <=5.4) Next, you must add the service provider to config/app.php
:electric_plug:
'providers' => [
// for laravel 5.4 and below
Imanghafoori\Widgets\WidgetsServiceProvider::class,
];
Publish your config file
php artisan vendor:publish
:fire: And you will be on fire!:fire:
php artisan make:widget MySexyWidget
A lot of docs are included in the generated widget file so it is not needed to memorize or even read the rest of this page. You can jump right-in and start using it.
You can set the variables in "config/widgetize.php" file to globally set some configs for you widgets and override them per widget if needed. Read the docblocks in config/widgetize.php file for more info.
If you do not set it,By default, it refers to app/Widgets folder and looks for the 'widgetNameView.blade.php' (Meaning that if your widget is
app/Widgets/home/recentProducts.php
the default view for that isapp/Widgets/home/recentProductsView.blade.php
) Anyway you can override it to point to any partial in views folder.(For example:public $template='home.footer'
will look for resource/views/home/footer.blade.php) So the entire widget lives in one folder:
| app\Widgets\Homepage\RecentProductsWidget.php
| app\Widgets\Homepage\RecentProductsWidgetView.blade.php
If you do not want to put your data method on your widget class, you can set
public $controller = App\Some\Class\MyController::class
and put yourpublic data
method on a dedicated class.(instead od having it on your widget class)
If you do not want to put your present method on your widget class, you can set
public $presenter = App\Some\Class\MyPresenter::class
and put yourpublic present
method on a dedicated class.The data retured from your controller is first piped to your presenter and then to your view.(So if you specify a presenter your view file gets its data from the presenter and not the controller.)
If you want to override the global cache life time (which is set in your config file).
value | effect :-------|:---------- -1 | forever 'forever' | forever 0 | disable 1 | 1 minute
You can set tags
public $cacheTags = ['tag1','tag2']
to target a group of widgets and flush their cache. using the helper function :
expire_widgets(['someTag', 'tag1']);
This causes all the widgets with 'someTag' or 'tag1' to be refreshed.
Note: Tagging feature works with ALL the laravel cache drivers including 'file' and 'database'.
If you want to explicitly define the cache key used to store the html result of your widget, you can implement this method.
It is important to note that if your final widget HTML output depends on PHP's super global variables and you want to cache it,Then they must be included in the cache key of the widget.
namespace App\Widgets;
class MyWidget
{
public function data()
{
$id = request('order_id'); // here we are using a request parameter to fetch database...
return Product::where('order_id', $id)->get();
}
public function extraCacheKeyDependency()
{
// so the value of this parameter should be considered for caching.
return request()->get('order_id');
}
}
You may want to look at the source code and read the comments for more information.
Tip: If you decide to use some other template engine instead of Blade it would be no problem.
You can Find more information in the article below : It is a 3 minutes read.
Single Responsibility Prinsiple
Route::widget('/some-url', 'MyWidget', 'MyRouteName1'); // <-- exposes HTML
// or
Route::jsonWidget('/my-api','MyWidget', 'MyRouteName2'); // <-- exposes json
A GET
request to /some-url/{a}/{b}
will see the widget.
a and b parameters are passed to widget controller.
jsonWidget
will expose the cached data returned from the widget's controller.
This way you can also expose your data as json for client-side apps.
Route::get('/api/products/{id}', '\App\Widgets\MyWidget@data');
* It is important to put \
before App
when you want to refer to a class outside the Http\Controller
folder.
If you find an issue, or have a better way to do something, feel free to open an issue or a pull request. If you use laravel-widgetize in your open source project, create a pull request to provide it's url as a sample application in the README.md file.
If you discover any security related issues, please email imanghafoori1@gmail.com instead of using the issue tracker.
As always if you found this package useful and you want to encourage us to maintain and work on it. Just press the star button to declare your willing.
:gem: It allows to write expressive code to authorize, validate and authenticate.
:gem: A minimal yet powerful package to give you opportunity to refactor your controllers.
:gem: It allows you login with any password in local environment only.