zanysoft/laravel-widgets

异步小部件、可重载小部件、控制台生成器、缓存——你所能想到的一切。

1.3 2022-08-03 12:24 UTC

This package is auto-updated.

Last update: 2024-09-16 09:33:02 UTC


README

Latest Stable Version Total Downloads Build Status Scrutinizer Quality Score

Laravel小部件

视图构建器的强大替代品。异步小部件、可重载小部件、控制台生成器、缓存——你所能想象的一切。

安装

运行 composer require zanysoft/laravel-widgets

Laravel >=5.5 使用包自动发现,因此你不需要手动添加 ServiceProvider 和 Facades

  1. 如果未自动发现,在 app.php 配置文件中注册服务提供者
<?php

'providers' => [
    ...
    ZanySoft\Widgets\ServiceProvider::class,
],
?>
  1. 如果未自动发现,也可以在这里添加一些门面。
<?php

'aliases' => [
    ...
    'Widget'       => ZanySoft\Widgets\Facade::class,
    'AsyncWidget'  => ZanySoft\Widgets\AsyncFacade::class,
],
?>

用法

让我们假设我们想要创建一个最近新闻的列表,并在多个视图中重用它。

首先,我们可以使用该包提供的 artisan 命令创建一个 Widget 类。

php artisan make:widget RecentNews

此命令生成两个文件

  1. resources/views/widgets/recent_news.blade.php 是一个空视图。

如果您不需要视图,请添加 "--plain" 选项。

  1. app/Widgets/RecentNews 是一个小部件类。
<?php

namespace App\Widgets;

use ZanySoft\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,
        ]);
    }
}

注意:如果需要,您可以使用自己的占位符。发布配置文件以更改路径。

最后一步是调用小部件。有几种方法可以做到这一点。

@widget('recentNews')

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

或甚至

{{ Widget::recentNews() }}

它们之间没有真正的区别。选择权在你。

向小部件传递变量

通过配置数组

让我们继续使用“最近新闻”的例子。

想象一下,我们通常需要显示 五个 新闻,但在某些视图中我们需要显示 十个。这可以很容易地实现:

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

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

['count' => 10] 是一个配置数组,可以通过 $this->config 访问。

配置数组在所有小部件方法中都可用,因此您可以使用它来配置占位符和容器(见下文)

注意:调用小部件时未指定的配置字段不会被覆盖

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

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

注意2:您可能想要(但可能不需要)创建自己的 BaseWidget 并从它继承。这没问题。唯一的边缘情况是从父级和子级合并配置默认值。在这种情况下,请执行以下操作

  1. 不要向子级添加 protected $config = [...] 行。

  2. 相反,添加默认值如下:

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

    parent::__construct($config);
}

直接

您还可以选择直接向 run() 方法传递额外的参数。

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

run() 方法是通过服务容器解析的,因此方法注入也在这里可用。

命名空间

默认情况下,该包尝试在 App\Widgets 命名空间中查找您的 widget。

您可以通过发布包配置(php artisan vendor:publish --provider="ZanySoft\Widgets\ServiceProvider")并设置 default_namespace 属性来覆盖此设置。

尽管使用默认命名空间非常方便,但在某些情况下,您可能希望有更大的灵活性。例如,如果您有数十个小部件,那么在命名空间文件夹中分组它们是有意义的。

没问题,有几种方法可以调用这些 widget

  1. default_namespace(基本上是 App\Widgets)中的完整 widget 名称传递给 run 方法。
@widget('News\RecentNews', $config)
  1. 使用点表示法。
@widget('news.recentNews', $config)
  1. FQCN 也是一个选项。
@widget('\App\Http\Some\Namespace\Widget', $config)

异步小部件

在某些情况下,使用 AJAX 加载小部件内容可能非常有用。

幸运的是,这可以非常容易地实现!您需要做的就是更改门面或 blade 指令 - Widget:: > AsyncWidget::@widget > @asyncWidget

小部件参数默认情况下会被加密并通过ajax调用隐式发送。因此,请期望它们在之后被 json_encoded()json_decoded()

注意:您可以通过将 public $encryptParams = false; 设置为false来关闭特定小部件的加密。但是,此操作将使小部件参数公开访问,因此请确保不要留下任何漏洞。例如,如果您通过小部件参数传递用户ID并关闭加密,则需要在小部件内部添加一个额外的访问检查。

注意:您可以在配置文件中将 use_jquery_for_ajax_calls 设置为 true,以便在使用ajax调用时使用它。

默认情况下,在ajax调用完成之前不会显示任何内容。

这可以通过向小部件类添加一个 placeholder() 方法来自定义。

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

旁白:如果您需要使用用于加载异步小部件的路由包(例如,您在子文件夹中运行应用程序http://site.com/app/),则需要将 ZanySoft\Widgets\ServiceProvider 复制到您的应用程序中,根据您的需求对其进行修改,并在Laravel中注册它,而不是之前的那个。

可重新加载的小部件

您甚至可以更进一步,每N秒自动重新加载小部件。

只需设置小部件类的 $reloadTimeout 属性即可完成。

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

同步和异步小部件都可以成为可重新加载的。

您应该谨慎使用此功能,因为它可能会在超时太低时很容易地用ajax调用使您的应用程序变得垃圾邮件。考虑也使用web sockets,但它们的设置要复杂得多。

容器

异步和可重新加载的小部件都需要一些DOM交互,因此它们将所有小部件输出包装在一个HTML容器中。该容器由 AbstractWidget::container() 方法定义,也可以进行自定义。

/**
 * 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="zanysoft-widget-container"',
    ];
}

注意:不支持嵌套异步或可重新加载的小部件。

缓存

还有一个简单内置的方法可以缓存整个小部件输出。只需在您的类中设置 $cacheTime 属性即可。

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

默认情况下未开启缓存。缓存键依赖于小部件名称和每个小部件参数。如果需要调整,请覆盖 cacheKey 方法。

缓存标签

当支持标签时(请参阅Laravel缓存文档)和为了简化缓存清除,默认情况下将 widgets 标签分配给所有小部件。您可以在小部件类中设置 $cacheTags 属性来定义一个或多个附加标签。示例

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'];
}

对于此示例,如果需要清除

// 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();

小部件组(额外内容)

在大多数情况下,Blade是一个设置小部件位置和顺序的完美工具。然而,有时您可能会发现以下方法很有用

// 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() 可以从链中省略。

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

等于

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

您可以为组中的小部件设置一个分隔符,它将在小部件之间显示。 Widget::group('sidebar')->setSeparator('<hr>')->...;

您也可以使用 wrap 方法将组中的每个小部件都包裹起来。

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>";
})->...;

从组中删除小部件

在已经将小部件添加到组之后,有几种方法可以从组中删除小部件/小部件。

  1. 通过其唯一的 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. 删除具有特定名称的所有小部件
Widget::group('sidebar')->addWidget('files');
Widget::group('sidebar')->addAsyncWidget('files');
Widget::group('sidebar')->removeByName('files'); // Widget group is empty now
  1. 删除放置在特定位置的所有小部件。
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. 一次删除所有小部件。
Widget::group('sidebar')->addWidget('files');
Widget::group('sidebar')->addAsyncWidget('files');
Widget::group('sidebar')->removeAll(); // Widget group is empty now

检查组的状态

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

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

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

第三方包的命名空间(额外内容)

在某些情况下,将小部件与自己的包一起提供可能很有用。例如,如果你的包允许你管理新闻,那么拥有立即可配置的小部件,可以直接与你的包一起提供,将非常方便。

为了避免每次都必须使用完全限定的类名,你可以在你的包提供者中设置一个小部件命名空间。这样,来自你包的小部件可以更容易地被识别,尤其是语法将更短。

要做到这一点,你只需要在你的包服务提供者中注册命名空间。

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

之后,你可以在你的视图中使用这个命名空间。

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

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