skybluesofa/laravel-wrapped-facade

Laravel 门面带 before/after 方法功能

1.0.3 2024-01-20 15:23 UTC

This package is auto-updated.

Last update: 2024-09-20 16:51:03 UTC


README

为 Facades 添加 before- 和 after- 方法调用。

目的

这个库创建了一种在调用 Facade 方法时添加前/后方法的能力。你可以在 tests 文件夹中找到一个示例。

我发现最有用的用途是缓存信息而不是执行请求。

Laravel 版本兼容性

此包支持 Laravel v9v10

安装

composer require skybluesofa/laravel-wrapped-facade

设置

安装配置文件

发布配置文件

运行 artisan 命令以发布包的配置文件

php artisan vendor:publish --tag=wrapped-facade-config

复制配置文件

wrapped-facade.php 文件从 vendor/skybluesofa/laravel-wrapped-facade/config 文件夹复制到您的应用程序的 config 文件夹。

使用 Wrapped Facades

Wrapped Facades 默认与标准 (Laravel Facades)[https://laravel.net.cn/docs/10.x/facades] 的工作方式完全相同。

基本 Facade 功能

让我们从基本的 Fruits 类开始

<?php
namespace App\Classes;

class Fruits
{
    protected $data = [
        1 => 'Apple',
        2 => 'Banana',
        3 => 'Carrot',
        4 => 'Durifruit',
        5 => 'Eggplant',
    ];

    public function getAll(): array
    {
        return $this->data;
    }
}

现在我们将创建一个 SuperFruits 门面,尽管如果你愿意,也可以将其命名为 Fruits

<?php
namespace App\Facades;

use App\Fruits;
use SkyBlueSofa\WrappedFacade\Facades\WrappedFacade;

class SuperFruits extends WrappedFacade
{
    public static function getFacadeAccessor()
    {
        return Fruits::class;
    }
}

当你运行门面代码时,你会得到一个包含水果和蔬菜的返回数组。

\App\Classes\SuperFruitsSuperFruits::getAll();

// Returns an array of fruits, just as we'd expect

将侧载功能添加到门面中

区别在于当你希望在方法运行之前和/或之后添加一些新功能时。

示例:缓存结果

我们将添加一个新属性和几个方法

<?php
namespace App\Facades;

use App\Fruits;
use SkyBlueSofa\WrappedFacade\Facades\WrappedFacade;

class SuperFruits extends WrappedFacade
{
    public static function getFacadeAccessor()
    {
        return Fruits::class;
    }

    protected static $cachedFruits = null;

    protected static function preIndex(array $args): ?callable
    {
        $cachedFruits = Cache::get('cachedFruits');
        if (is_null($cachedFruits)) {
            Log::info('Cache has expired');
            return null;
        }

        // If we found the results, then we'll store it here until we're all done
        Log::info('Returning cached results');
        static::$cachedFruits = $cachedFruits;
        return function () use ($cachedFruits) {
            return $cachedFruits;
        };
    }

    protected static function postIndex(array $args, mixed $results): mixed
    {
        if (! static::$cachedFruits) {
            Log::info('Caching fruit data');
            Cache::set('cachedFruits', $results);
            static::$cachedFruits = null;
        } else {
            Log::info('Fruits were already in the cache');
        }
        return $results;
    }
}

现在当你运行门面代码时,你仍然会得到一个包含水果和蔬菜的返回数组,但下一次访问时,它将从缓存中返回。

\App\Classes\SuperFruitsSuperFruits::getAll();

// Returns an array of fruits, just as we'd expect

示例:修改结果

在这个例子中,我们不会缓存结果。相反,我们将根据登录用户的偏好来过滤结果。

我们将把类更改为只有一个 'postIndex' 方法

<?php
namespace App\Facades;

use App\Fruits;
use SkyBlueSofa\WrappedFacade\Facades\WrappedFacade;

class SuperFruits extends WrappedFacade
{
    public static function getFacadeAccessor()
    {
        return Fruits::class;
    }

    protected static function postIndex(array $args, mixed $results): mixed
    {
        // In this example , the user hates 'Banana'
        $hatedFruit = Auth::user()->mostHatedFruit; 

        return array_diff(
            $results,
            [$hatedFruit]
        );
    }
}

现在当你运行门面代码时,你仍然会得到一个包含水果和蔬菜的返回数组,但 'Banana' 已经从数组中移除。

\App\Classes\SuperFruitsSuperFruits::getAll();

// Returns an array of fruits, sans Banana
[
    1 => 'Apple',
    2 => 'Carrot',
    3 => 'Durifruit',
    4 => 'Eggplant',
]

示例:使用多个侧载功能

在这个例子中,我们将在缓存结果之前验证结果。

我们将更新类以包括一个新的 'postIndexValidate' 方法和 'sideloadedMethodOrder' 属性

<?php
namespace App\Facades;

use App\Fruits;
use App\FruitValidator;
use SkyBlueSofa\WrappedFacade\Facades\WrappedFacade;

class SuperFruits extends WrappedFacade
{
    public static function getFacadeAccessor()
    {
        return Fruits::class;
    }

    protected static $sideloadedMethodOrder = [
        'postIndexValidate',
        'postIndex',
    ];

    protected static function postIndexValidate(array $args, mixed $results): mixed
    {
        // This is an example validator. How it works really doesn't matter.
        $fruitValidator = new FruitValidator($results);

        if (! $fruitValidator->validates()) {
            throw new \RuntimeException('Fruit does not validate!');
        }

        return $results;
    }

    protected static function postIndex(array $args, mixed $results): mixed
    {
        // In this example , the user hates 'Banana'
        $hatedFruit = Auth::user()->mostHatedFruit; 

        return array_diff(
            $results,
            [$hatedFruit]
        );
    }
}

我们假设 FruitValidator::validates() 将返回 false。所以现在当你运行门面代码时,将抛出一个 \RuntimeException

\App\Classes\SuperFruitsSuperFruits::getAll();

// RuntimeException('Fruit does not validate!');

请注意,$sideloadedMethodOrder 数组可以有多种格式。所有这些都是有效的

// If you don't care what order things are run in:
// Don't add the $sideloadedMethodOrder property at all, or:
protected static $sideloadedMethodOrder = [];

// If you have only a few sideloaded methods, a simple array might be easiest.
// The array lists the specific sideloaded method names:
protected static $sideloadedMethodOrder = [
    'postIndexValidate',
    'postIndex',
];

// If you have quite a few sideloaded methods, a nested array might be clearer.
// The first level of the array is the sideloaded key (<pre|post> + <methodName>).
// The second level of the array lists the specific sideloaded method names:
protected static $sideloadedMethodOrder = [
    'postIndex' => [
        'postIndexValidate',
        'postIndex',
    ],
];