Widgets for Laravel

Overview

Latest Stable Version Total Downloads Build Status Scrutinizer Quality Score

Widgets for Laravel

A powerful alternative to view composers. Asynchronous widgets, reloadable widgets, console generator, caching - everything that you can imagine.

Installation

Run composer require arrilot/laravel-widgets

Laravel >=5.5 uses Package Auto-Discovery, so you don't need to manually add the ServiceProvider and Facades

Usage

Let's consider that we want to make a list of recent news and reuse it in several views.

First of all we can create a Widget class using the artisan command provided by the package.

php artisan make:widget RecentNews

This command generates two files:

  1. resources/views/widgets/recent_news.blade.php is an empty view.

Add "--plain" option if you do not need a view.

  1. app/Widgets/RecentNews is a widget class.
<?php

namespace App\Widgets;

use Arrilot\Widgets\AbstractWidget;

class RecentNews extends AbstractWidget
{
    /**
     * The configuration array.
     *
     * @var array
     */
    protected $config = [];

    /**
     * Treat this method as a controller action.
     * Return view() or other content to display.
     */
    public function run()
    {
        //

        return view('widgets.recent_news', [
            'config' => $this->config,
        ]);
    }
}

Note: You can use your own stubs if you need. Publish config file to change paths.

The last step is to call the widget. There are several ways to do so.

@widget('recentNews')

or

{{ Widget::run('recentNews') }}

or even

{{ Widget::recentNews() }}

There is no real difference between these. The choice is up to you.

Passing variables to widget

Via config array

Let's carry on with the "recent news" example.

Imagine that we usually need to show five news, but in some views we need to show ten. This can be easily achieved by:

class RecentNews extends AbstractWidget
{
    ...
    protected $config = [
        'count' => 5
    ];
    ...
}

...
@widget('recentNews') // shows 5
@widget('recentNews', ['count' => 10]) // shows 10

['count' => 10] is a config array that can be accessed by $this->config.

Config array is available in every widget method so you can use it to configure placeholder and container too (see below)

Note: Config fields that are not specified when you call a widget aren't overridden:

class RecentNews extends AbstractWidget
{
    ...
    protected $config = [
        'count' => 5,
        'foo'   => 'bar'
    ];
    
    ...
}

@widget('recentNews', ['count' => 10]) // $this->config['foo'] is still 'bar'

Note 2: You may want (but you probably don't) to create your own BaseWidget and inherit from it. That's fine. The only edge case is merging config defaults from a parent and a child. In this case do the following:

  1. Do not add protected $config = [...] line to a child.

  2. Instead add defaults like this:

public function __construct(array $config = [])
{
    $this->addConfigDefaults([
        'child_key' => 'bar'
    ]);

    parent::__construct($config);
}

Directly (works only for Laravel versions below 7)

You can also choose to pass additional parameters to run() method directly.

@widget('recentNews', ['count' => 10], 'date', 'asc')
...
public function run($sortBy, $sortOrder) { }
...

run() method is resolved via Service Container, so method injection is also available here.

Namespaces

By default the package tries to find your widget in the App\Widgets namespace.

You can override this by publishing package config (php artisan vendor:publish --provider="Arrilot\Widgets\ServiceProvider") and setting default_namespace property.

Although using the default namespace is very convenient, in some cases you may wish to have more flexibility. For example, if you've got dozens of widgets it makes sense to group them in namespaced folders.

No problem, there are several ways to call those widgets:

  1. Pass a full widget name from the default_namespace (basically App\Widgets) to the run method.
@widget('News\RecentNews', $config)
  1. Use dot notation.
@widget('news.recentNews', $config)
  1. FQCN is also an option.
@widget('\App\Http\Some\Namespace\Widget', $config)

Asynchronous widgets

In some situations it can be very beneficial to load widget content with AJAX.

Fortunately, this can be achieved very easily! All you need to do is to change facade or blade directive - Widget:: => AsyncWidget::, @widget => @asyncWidget

Widget params are encrypted (by default) and sent via ajax call under the hood. So expect them to be json_encoded() and json_decoded() afterwards.

Note: You can turn encryption off for a given widget by setting public $encryptParams = false; on it. However, this action will make widget params publicly accessible, so please make sure to not leave any vulnerabilities. For example, if you pass something like user_id through widget params and turn encryption off, you do need to add one more access check inside the widget.

Note: You can set use_jquery_for_ajax_calls to true in the config file to use it for ajax calls if you want to, but you need to manually add jquery to your page in this case.

By default nothing is shown until ajax call is finished.

This can be customized by adding a placeholder() method to the widget class.

public function placeholder()
{
    return 'Loading...';
}

Side note: If you need to do something with the routes package used to load async widgets (e.g. you run app in a subfolder http://site.com/app/) you need to copy Arrilot\Widgets\ServiceProvider to your app, modify it according to your needs and register it in Laravel instead of the former one.

Reloadable widgets

You can go even further and automatically reload widget every N seconds.

Just set the $reloadTimeout property of the widget class and it is done.

class RecentNews extends AbstractWidget
{
    /**
     * The number of seconds before each reload.
     *
     * @var int|float
     */
    public $reloadTimeout = 10;
}

Both sync and async widgets can become reloadable.

You should use this feature with care, because it can easily spam your app with ajax calls if timeouts are too low. Consider using web sockets too but they are way harder to set up.

Container

Async and Reloadable widgets both require some DOM interaction so they wrap all widget output in a html container. This container is defined by AbstractWidget::container() method and can be customized too.

/**
 * Async and reloadable widgets are wrapped in container.
 * You can customize it by overriding this method.
 *
 * @return array
 */
public function container()
{
    return [
        'element'       => 'div',
        'attributes'    => 'style="display:inline" class="arrilot-widget-container"',
    ];
}

Note: Nested async or reloadable widgets are not supported.

Caching

There is also a simple built-in way to cache entire widget output. Just set $cacheTime property in your widget class and you are done.

class RecentNews extends AbstractWidget
{
    /**
     * The number of minutes before cache expires.
     * False means no caching at all.
     *
     * @var int|float|bool
     */
    public $cacheTime = 60;
}

No caching is turned on by default. A cache key depends on a widget name and each widget parameter. Override cacheKey method if you need to adjust it.

Cache tagging

When tagging is supported (see the Laravel cache documentation) and to simplify cache flushing, a tag widgets is assigned by default to all widgets. You can define one or more additional tags to your widgets by setting the values in the $cacheTags property in your widget class. Example :

class RecentNews extends AbstractWidget
{
    /**
     * Cache tags allow you to tag related items in the cache 
     * and then flush all cached values that assigned a given tag.
     *
     * @var array
     */
    public $cacheTags = ['news', 'frontend'];
}

For this example, if you need to flush :

// Clear widgets with the tag news
Cache::tags('news')->flush();

// Clear widgets with the tag news OR backend
Cache::tags(['news', 'frontend'])->flush();

// Flush all widgets cache
Cache::tags('widgets')->flush();

Widget groups (extra)

In most cases Blade is a perfect tool for setting the position and order of widgets. However, sometimes you may find useful the following approach:

// add several widgets to the 'sidebar' group anywhere you want (even in controller)
Widget::group('sidebar')->position(5)->addWidget('widgetName1', $config1);
Widget::group('sidebar')->position(4)->addAsyncWidget('widgetName2', $config2);

// display them in a view in the correct order
@widgetGroup('sidebar')
// or 
{{ Widget::group('sidebar')->display() }}

position() can be omitted from the chain.

Widget::group('sidebar')->addWidget('files');

equals

Widget::group('sidebar')->position(100)->addWidget('files');

You can set a separator that will be display between widgets in a group. Widget::group('sidebar')->setSeparator('<hr>')->...;

You can also wrap each widget in a group using wrap method like that.

Widget::group('sidebar')->wrap(function ($content, $index, $total) {
    // $total is a total number of widgets in a group.
    return "<div class='widget-{$index}'>{$content}</div>";
})->...;

Removing widgets from a group

There is a couple of ways to remove widget/widgets from a group after they've been already added.

  1. Remove one widget by its unique id
$id1 = Widget::group('sidebar')->addWidget('files');
$id2 = Widget::group('sidebar')->addAsyncWidget('files');
Widget::group('sidebar')->removeById($id1); // There is only second widget in the group now
  1. Remove all widgets with specific name
Widget::group('sidebar')->addWidget('files');
Widget::group('sidebar')->addAsyncWidget('files');
Widget::group('sidebar')->removeByName('files'); // Widget group is empty now
  1. Remove all widgets that are placed on a specific position.
Widget::group('sidebar')->position(42)->addWidget('files');
Widget::group('sidebar')->position(42)->addAsyncWidget('files');
Widget::group('sidebar')->removeByPosition(42); // Widget group is empty now
  1. Remove all widgets at once.
Widget::group('sidebar')->addWidget('files');
Widget::group('sidebar')->addAsyncWidget('files');
Widget::group('sidebar')->removeAll(); // Widget group is empty now

Checking the state of a group

Widget::group('sidebar')->isEmpty(); // bool

Widget::group('sidebar')->any(); // bool

Widget::group('sidebar')->count(); // int

Namespaces for third party packages (extra)

In some cases, it may be useful to deliver widgets with your own packages. For example, if your package allows you to manage news, it would be convenient to have immediately configurable widgets, ready for display, directly delivered with your package.

To avoid having to use the fqcn each time, you can set a widget namespace into your package provider. This way the widgets from your package can be more easily identified, and especially the syntax will be shorter.

To do that, all you have to do is to register the namespace in your package service provider :

public function boot() 
{
    app('arrilot.widget-namespaces')->registerNamespace('my-package-name', '\VendorName\PackageName\Path\To\Widgets');
}

After that you can use the namespace in your views :

@widget('my-package-name::foo.bar')

// is equivalent to
@widget('\VendorName\PackageName\Path\To\Widgets\Foo\Bar')
Comments
  • Passing variables to widget

    Passing variables to widget

    Why is not works?

    <?php
    
    namespace App\Widgets;
    
    use Arrilot\Widgets\AbstractWidget;
    use Illuminate\Support\Facades\Log;
    
    class CustomMenu extends AbstractWidget
    {
        /**
         * The configuration array.
         *
         * @var array
         */
        protected $config = [];
    
        public function __construct()
        {
            $this->addConfigDefaults([
                'page' => null,
            ]);
        }
    
        /**
         * Treat this method as a controller action.
         * Return view() or other content to display.
         */
        public function run()
        {
            Log::info($this->config['page']);
        }
    }
    

    @widget('customMenu',['page' => 'company'])

    Result: Log.info: array('page' => null)

    opened by AlexThousand 8
  • Publish 2 widgets

    Publish 2 widgets

    I created 2 widgets, and when I try to display them, I got only one, for example:

    {{ Widget::widgetOne() }}
    {{ Widget::widgetTwo() }}
    

    It shows only widgetOne , And when I delete the widgetOne I can see widgetTwo

    Why ?

    opened by kossa 8
  • Auth::user() is null for AsyncWidgets

    Auth::user() is null for AsyncWidgets

    Hi. First of all - thanks for that cool package.

    One question. When I'm using @asyncWidget my Auth::user is null, if I use @widget - all works fine.

    Is there are a way to make Auth::user working for @asyncWidget request? Thanks.

    opened by m4a1fox 6
  • PHP Fatal error:  Maximum function nesting level of '256' reached, aborting!

    PHP Fatal error: Maximum function nesting level of '256' reached, aborting!

    I am getting a PHP Fatal Error exception when running php artisan make:widget RecentNews, after going through the installation steps as described here.

    PHP Fatal error: Maximum function nesting level of '256' reached, aborting! in .../vendor/laravel/framework/src/Illuminate/Container/Container.php on line 698 PHP Fatal error: Maximum function nesting level of '256' reached, aborting! in .../vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php on line 115 Full command output can be found as an attachment to this post.

    Increase the value of xdebug.max_nesting_level in the php.ini (to 500, f.e.) did not help. It then told me it reached maximum function nesting level '500'.

    php artisan make:model test runs without any issues.

    I am running php 7.0.8-2+deb.sury.org~xenial+1 and using laravel 5.3.

    What am I doing wrong? Or how can I solve this? PHP Fatal error.txt

    opened by IncognitoCore 6
  • [Enhancement] add a path for stubs

    [Enhancement] add a path for stubs

    Is it possible to add stubs to config file ?

    for example

    <?php
    return [
        //'default_namespace' => 'App\Widgets',
        'stubs' => [
             'widget' => app_path() . '/vendor/arrilot/laravel-widgets/src/Console/stubs/widget.stub',
             'widget_plain' => app_path() . '/vendor/arrilot/laravel-widgets/src/Console/stubs/widget_plain.stub',
        ],
        'use_jquery_for_ajax_calls' => false,
    ];
    

    it will allow us to customize easily stubs

    enhancement 
    opened by zorx 6
  • Widget class must extend Arrilot\Widgets\AbstractWidget class

    Widget class must extend Arrilot\Widgets\AbstractWidget class

    I was having trouble getting Widgets work on Laravel 5.1. My app runs throw ErrorException, this is it:

    ErrorException in AbstractWidgetFactory.php line 122:
    Widget class must extend Arrilot\Widgets\AbstractWidget class (View: /home/vagrant/Code/Laravel/resources/views/welcome.blade.php)
    

    My Widget Class was created with artisan command 'artisan make:widget Input' and it extends AbstractWidget from Arrilot\Widgets\AbstractWidget. So here is class:

    <?php namespace App\Widgets;
    
    use Arrilot\Widgets\AbstractWidget;
    
    class Input extends AbstractWidget {
        protected $config = [
            'type' => 'text'
        ];
        public function run()
        {
            return view("widgets.input", [
                'config' => $this->config,
            ]);
        }
    }
    

    What can it be? Thanks!

    opened by bsh314 6
  • Can not pass variable to Widget (via Config Array)

    Can not pass variable to Widget (via Config Array)

    Been using laravel-widgets for months, always used config arrays and had no problems until today ...

    class MyWidget extends Widget
    {
        protected $config = [
            'selection' => 'DEFAULT'
        ];
    }
    

    @widget('Modules\MyModule\Widgets\MyWidget', ['selection' => 'USERSELECTION'])

    $this->config['selection'] is still 'DEFAULT'

    Using 3.13.1 with Laravel 8.46.0 / Apache 2.4.46 with php 7.4.19

    Everything else works just fine, controller returns data to blade as expected without errors, only the config is not being changed and always the default is used.

    I even tried to get the user selection and apply some php string functions to see if the problem was upper/lower case but that did not worked too.

    Any ideas ?

    opened by FatihKoz 5
  • @asyncWidget() causing jQuery errors?

    @asyncWidget() causing jQuery errors?

    Hello,

    When I load an async widget, the rest of the jQuery on my page breaks?

    with:

    Uncaught TypeError: jQuery is not a function

    Why might this be? If I remove @asyncWidget() and replace for @widget() it works fine.

    opened by fredbradley 5
  • widget save in database not executing

    widget save in database not executing

    Hi i am widget code through WYSIWYG editor and saving in database, on front end it show same code as it is not calling widget.

    {{ Widget::test() }} //saved in database

    do i need to render page content saved in database first to display main view?

    opened by umefarooq 5
  • make:widget not generating empty view

    make:widget not generating empty view

    Small issue as i can create the view myself, but it doesn't create the /resources/views/widgets folder or the empty view blade

    Using Laravel 5.5 + laracogs fresh install

    opened by GameFanager 5
  • Last release matcher is broken

    Last release matcher is broken

    I was using the following widget configuration, and it has been broken on the last release: @widget('VideosWidget', ['type' => 'important', 'user' => auth()->user(), 'title' => 'Fontos videók az elmúlt 30 napból:)', 'days' => 30]) after fixing the :)' part, everything was working fine, guess the matcher is not working so well, or I do not know. Fixed code @widget('VideosWidget', ['type' => 'important', 'user' => auth()->user(), 'title' => 'Fontos videók az elmúlt 30 napból', 'days' => 30])

    opened by matevisky 5
  • Add configurations for AJAX URL route

    Add configurations for AJAX URL route

    Now AJAX loading route:

    /arrilot/load-widget
    

    can be configured easy way in laravel-widgets.php

        'url_prefix' => 'lazyapi',
        'url_name' => 'load'
    
    opened by zenn1989 0
  • use jquery from env

    use jquery from env

    Most of the times the only thing user is ever going to override is the jQuery use, have it in the env is very convenient so one can override it dynamically by simply updating value in the file.

    opened by buzzclue 0
  • parseFullWidgetNameFromString breaks namespace with caps and lower case characters

    parseFullWidgetNameFromString breaks namespace with caps and lower case characters

    I have a widget registered under this namespace: dPOS\Widgets\TestWidget

    But running @asyncWidget($widget),

    Generates the following classname: "DPOS\Widgets\TestWidget"

    And hence the exception: Class "DPOS\Widgets\TestWidget" does not exist

    This is generated in: https://github.com/arrilot/laravel-widgets/blob/master/src/Factories/AbstractWidgetFactory.php#L170

    How can we fix this?

    opened by ngugijames 0
  • asyncWidget with chart data not showing anything

    asyncWidget with chart data not showing anything

    I have chartjs chart in my widget , if i called it @widget it works fine but when i try to load it via @asyncWidget it does not work . currently i only have static data in chart

    opened by shakhawatfci 2
  • Add getter of $widgets to WidgetGroup

    Add getter of $widgets to WidgetGroup

    Hello. Sometimes we need to have extra logic with widget groups. For example, make tabs fo them.

                        <ul class="nav nav-tabs" role="tablist">
                            <li class="nav-item">
                                <a class="nav-link active" data-toggle="tab" href="{{ $widget[1] }}" role="tab" aria-controls="home"> {{ $widget["name"] }}</a>
                            </li>
                        </ul>
    <div class="tab-content">
    {{ Widget::group('group')->display() }}
    </div>
    

    So we need to loop over widgets in group. It would be great to have getter or use Macroable trait on Widget group.

    opened by nejtr0n 2
Releases(3.13.1)
Owner
Nekrasov Ilya
Nekrasov Ilya
List of 77 languages for Laravel Framework 4, 5, 6, 7 and 8, Laravel Jetstream , Laravel Fortify, Laravel Breeze, Laravel Cashier, Laravel Nova and Laravel Spark.

Laravel Lang In this repository, you can find the lang files for the Laravel Framework 4/5/6/7/8, Laravel Jetstream , Laravel Fortify, Laravel Cashier

Laravel Lang 6.9k Jan 2, 2023
⚡ Laravel Charts — Build charts using laravel. The laravel adapter for Chartisan.

What is laravel charts? Charts is a Laravel library used to create Charts using Chartisan. Chartisan does already have a PHP adapter. However, this li

Erik C. Forés 31 Dec 18, 2022
Laravel Kickstart is a Laravel starter configuration that helps you build Laravel websites faster.

Laravel Kickstart What is Laravel Kickstart? Laravel Kickstart is a Laravel starter configuration that helps you build Laravel websites faster. It com

Sam Rapaport 46 Oct 1, 2022
Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

null 9 Dec 14, 2022
Laravel Segment is an opinionated, approach to integrating Segment into your Laravel application.

Laravel Segment Laravel Segment is an opinionated, approach to integrating Segment into your Laravel application. Installation You can install the pac

Octohook 13 May 16, 2022
Laravel Sanctum support for Laravel Lighthouse

Lighthouse Sanctum Add Laravel Sanctum support to Lighthouse Requirements Installation Usage Login Logout Register Email Verification Forgot Password

Daniël de Wit 43 Dec 21, 2022
Bring Laravel 8's cursor pagination to Laravel 6, 7

Laravel Cursor Paginate for laravel 6,7 Installation You can install the package via composer: composer require vanthao03596/laravel-cursor-paginate U

Pham Thao 9 Nov 10, 2022
A package that uses blade templates to control how markdown is converted to HTML inside Laravel, as well as providing support for markdown files to Laravel views.

Install Install via composer. $ composer require olliecodes/laravel-etched-blade Once installed you'll want to publish the config. $ php artisan vendo

Ollie Codes 19 Jul 5, 2021
A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic

A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic! concepts like , Hijri Dates & Arabic strings and so on ..

Adnane Kadri 49 Jun 22, 2022
Jetstrap is a lightweight laravel 8 package that focuses on the VIEW side of Jetstream / Breeze package installed in your Laravel application

A Laravel 8 package to easily switch TailwindCSS resources generated by Laravel Jetstream and Breeze to Bootstrap 4.

null 686 Dec 28, 2022
Laravel Jetstream is a beautifully designed application scaffolding for Laravel.

Laravel Jetstream is a beautifully designed application scaffolding for Laravel. Jetstream provides the perfect starting point for your next Laravel application and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management.

The Laravel Framework 3.5k Jan 8, 2023
Laravel Larex lets you translate your whole Laravel application from a single CSV file.

Laravel Larex Translate Laravel Apps from a CSV File Laravel Larex lets you translate your whole Laravel application from a single CSV file. You can i

Luca Patera 68 Dec 12, 2022
A Laravel package that adds a simple image functionality to any Laravel model

Laraimage A Laravel package that adds a simple image functionality to any Laravel model Introduction Laraimage served four use cases when using images

Hussein Feras 52 Jul 17, 2022
A Laravel extension for using a laravel application on a multi domain setting

Laravel Multi Domain An extension for using Laravel in a multi domain setting Description This package allows a single Laravel installation to work wi

null 658 Jan 6, 2023
Example of using abrouter/abrouter-laravel-bridge in Laravel

ABRouter Laravel Example It's a example of using (ABRouter Laravel Client)[https://github.com/abrouter/abrouter-laravel-bridge] Set up locally First o

ABRouter 1 Oct 14, 2021
Laravel router extension to easily use Laravel's paginator without the query string

?? THIS PACKAGE HAS BEEN ABANDONED ?? We don't use this package anymore in our own projects and cannot justify the time needed to maintain it anymore.

Spatie 307 Sep 23, 2022
Laravel application project as Sheina Online Store backend to be built with Laravel and VueJS

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Boas Aditya Christian 1 Jan 11, 2022
Postgis extensions for laravel. Aims to make it easy to work with geometries from laravel models.

Laravel Wrapper for PostgreSQL's Geo-Extension Postgis Features Work with geometry classes instead of arrays. $model->myPoint = new Point(1,2); //lat

Max 340 Jan 6, 2023
laravel - Potion is a pure PHP asset manager for Laravel 5 based off of Assetic.

laravel-potion Potion is a pure PHP asset manager for Laravel based off of Assetic. Description Laravel 5 comes with a great asset manager called Elix

Matthew R. Miller 61 Mar 1, 2022