Support 是一个 Laravel 扩展包,旨在推广最佳实践和设计模式的使用。

v4.0.1 2019-03-09 13:23 UTC

README

License Latest Stable Version Build Status SensioLabsInsight

介绍

Support 包含一系列有用的类,简化了与 Laravel 5 的最佳实践和设计模式的使用。

本包的部分灵感来源于 culttt.com 博客,强烈推荐给新、老开发者,因为它涵盖了许多主题,并且总是有有趣的观点和讨论。我从中学到了很多。

Support 需要 PHP 7.1.3+ 以及几个 5.8 Illuminate 组件才能运行。

为了使用 Support 与 Laravel 或 Lumen 5.4 - 5.7,请检查 3.x 版本

安装

Support 可以通过 Composer 安装,只需在 composer.json 中包含 "michele-angioni/support": "^4.0" 并运行 composer updatecomposer install

然后在 Laravel 的 app.php 配置文件中添加 Support 服务提供者到 providers 数组

MicheleAngioni\Support\SupportServiceProvider::class

并在 aliases 数组中添加 Helper 布尔门

'Helpers' => MicheleAngioni\Support\Facades\Helpers::class

Laravel 5.4 - 5.7

如果您需要与 Laravel 5.3 - 5.7 兼容的版本,请检查 3.x 分支 及其文档。

Laravel 5.0 - 5.3

如果您需要与 Laravel 5.0 - 5.3 兼容的版本,请检查 2.x 分支 及其文档。

Laravel 4

如果您需要 Laravel 4 版本,请检查 1.0 分支 及其文档。

Lumen

目前,只保证了对 Lumen 的部分和 不稳定 支持。

首先在您的引导文件中加载 Service Provider

$app->register('MicheleAngioni\Support\SupportServiceProvider');

并设置所需的配置密钥

config(['ma_support.cache_time' => 10]); // Default number of minutes the repositories will be cached

模块概要

Support 包含以下功能:仓库、缓存、展示者、信号量、Helper 类以及新的自定义验证器。此外,Support 还提供了一些新的自定义异常。

配置

Support 不需要任何配置即可运行。但是,您可以通过 artisan 命令 php artisan vendor:publish 发布配置文件,这将添加 ma_support.php 文件到您的配置目录。
然后您可以编辑此文件以自定义 Support 的行为。

为了在代码中访问文件密钥,您可以使用 config('ma_support.key'),其中 key 是文件中的一个密钥。

仓库使用

AbstractEloquentRepository 抽象类包含了一个模型包装器,具有许多执行 Laravel 模型的有用查询。这样,实现仓库模式就变得简单直接。

例如,让我们以一个 Post 模型为例。首先,我们应该为我们的应用程序中的所有模型创建一个通用的 Repository 接口,该接口扩展了包的 RepositoryInterface

 <?php

 interface RepositoryInterface extends \MicheleAngioni\Support\Repos\RepositoryInterface {}

接下来,我们定义一个Post仓库接口,该接口将被注入到需要且扩展了我们刚刚创建的common RepositoryInterface的类的构造函数中。让我们定义PostRepositoryInterface如下:

 <?php

 interface PostRepositoryInterface extends RepositoryInterface {}

现在我们需要一个实现。创建Post仓库的最简单方法是将一个类定义为如下:

<?php

use MicheleAngioni\Support\Repos\AbstractEloquentRepository;
use Post;

class EloquentPostRepository extends AbstractEloquentRepository implements PostRepositoryInterface
{
    protected $model;

    public function __construct(Post $model)
    {
        $this->model = $model;
    }
}

现在我们需要将实现绑定到接口,可以通过添加以下内容来实现:

$this->app->bind(
    PostRepositoryInterface::class,
    EloquentPostRepository::class
);

到一个现有的Laravel Service Provider中。或者我们可以创建一个新的

<?php

use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider 
{

    public function register()
    {
        $this->app->bind(
            PostRepositoryInterface::class,
            EloquentPostRepository::class
        );
    }
}

并将其添加到config/app.php文件中的providers数组中

RepositoryServiceProvider::class,

假设现在我们需要在PostController中使用Post仓库。我们只需在控制器中注入我们的PostRepositoryInterface,它将通过Laravel IoC容器得到解析

<?php

use PostRepositoryInterface as PostRepo;

class PostController extends BaseController 
{

    private $postRepo;

    function __construct(PostRepo $postRepo)
    {
        $this->postRepo = $postRepo;
    }

    public function show($idPost)
    {
        $post = $this->postRepo->find($idPost);

        // Use the retrieved post
    }
}

AbstractEloquentRepository会自动为我们的仓库提供以下公共方法

  • all(array $with = [])
  • find($id, array $array)
  • findOrFail($id, array $with = [])
  • first()
  • firstOrFail()
  • firstBy(array $where = [], array $with = [])
  • firstOrFailBy(array $where = [], array $with = [])
  • getBy(array $where = [], array $with = [])
  • getByLimit($limit, array $where = [], array $with = [])
  • getByOrder($orderBy, array $where = [], array $with = [], $order = 'desc', $limit = 0)
  • getIn($whereInKey, array $whereIn = [], $with = [], $orderBy = NULL, $order = 'desc', $limit = 0)
  • getNotIn($whereNotInKey, array $whereNotIn = [], $with = [], $orderBy = NULL, $order = 'desc', $limit = 0)
  • getHas($relation, array $where = [], array $with = [], $hasAtLeast = 1)
  • hasFirst($relation, array $where = [], array $with = [], $hasAtLeast = 1)
  • hasFirstOrFail($relation, array $where = [], array $with = [], $hasAtLeast = 1)
  • whereHas($relation, array $where = [], array $whereHas = [], array $with = [])
  • getByPage($page = 1, $limit = 10, array $where = [], $with = [], $orderBy = NULL, $order = 'desc')
  • insert(array $collection)
  • create(array $inputs = [])
  • update(array $inputs)
  • updateById($id, array $inputs)
  • updateBy(array $where, array $inputs)
  • updateOrCreateBy(array $where, array $inputs = [])
  • destroy($id)
  • destroyFirstBy(array $where)
  • destroyBy(array $where)
  • truncate()
  • count()
  • countBy(array $where = [])
  • countWhereHas($relation, array $where = [], array $whereHas = [])

$where数组可以同时有格式['key' => 'value']['key' => ['', 'value']],其中<operator>可以是=<>

仓库模块还支持xml仓库。假设我们有一个staff.xml文件。我们需要定义一个StaffXMLRepositoryInterface

<?php

interface StaffXMLRepositoryInterface {}

然后我们可以创建我们的xml仓库如下

<?php

use MicheleAngioni\Support\Repos\AbstractSimpleXMLRepository;

class SimpleXMLStaffRepository extends AbstractSimpleXMLRepository implements StaffXMLRepositoryInterface
{
    protected $autoload = false;

    protected $xmlPath = '/assets/xml/staff.xml';
}

$xmlPath属性定义了xml文件的路径(基本路径是/app文件夹),而$autoload属性定义了在实例化类时是否自动加载xml文件。AbstractSimpleXMLRepository包含了我们需要的方法

  • getFilePath() : 返回xml文件路径
  • loadFile() : 加载xml文件以供后续使用
  • getFile() : 如果之前未加载,则加载xml文件,并以SimpleXMLElement实例返回

与“标准”仓库一样,我们需要指导IoC容器。我们可以通过定义以下XMLRepositoryServiceProvider来实现这一点

<?php

use Illuminate\Support\ServiceProvider;

class XMLRepositoryServiceProvider extends ServiceProvider 
{

    public function register()
    {
        $this->app->bind(
            'StaffXMLRepositoryInterface',
            'SimpleXMLStaffRepository'
         );
    }
}

然后我们可以将仓库注入到需要的类中,或者简单地通过Laravel应用程序实例/外观调用它

$xmlStaffRepo = App::make('StaffXMLRepositoryInterface');

!!警告!!

AbstractEloquentRepositoryAbstractSimpleXMLRepository类不提供任何输入验证!

缓存使用

缓存模块可以通过使用装饰者模式来为我们的存储库添加缓存功能。然后,我们可以继续之前关于Post模型及其存储库的示例。我们定义一个CachePostRepoDecorator如下:

<?php

use MicheleAngioni\Support\Cache\CacheInterface;
use MicheleAngioni\Support\Cache\KeyManager;
use MicheleAngioni\Support\Cache\AbstractCacheRepositoryDecorator;
use MicheleAngioni\Support\Repos\RepositoryCacheableQueriesInterface;

class CachePostRepoDecorator extends AbstractCacheRepositoryDecorator implements PostRepositoryInterface 
{
    /**
     * Section of the Cache the repo belongs to.
     *
     * @var string
     */
    protected $section = 'Forum';

    /**
     * Construct
     *
     * @param  RepositoryCacheableQueriesInterface $repo
     * @param  CacheInterface  $cache
     * @param  KeyManager      $keyManager
     */
    public function __construct(RepositoryCacheableQueriesInterface $repo, CacheInterface $cache, KeyManager $keyManager)
    {
        parent::__construct($repo, $cache, $keyManager);
    }
}

可以使用section属性来定义缓存部分,并在生成缓存键时使用。

此类实现了PostRepositoryInterface,因此它被视为Post存储库,其实质上只是一个包装器。它还扩展了AbstractCacheRepositoryDecorator,所有魔法都在这里发生。AbstractCacheRepositoryDecorator实现了RepositoryCacheableQueriesInterface,这是一个非常基础的接口,指示我们的系统哪些存储库方法将被缓存。

默认方法包括all()、find()和findOrFail(),但你可以定义自己的接口和具有更多方法的抽象缓存装饰器。

AbstractCacheRepositoryDecorator构造函数需要一个实现了RepositoryCacheableQueriesInterface的存储库,一个实现了CacheInterface的缓存管理器,以及一个实现了KeyManagerInterface的键管理器。Laravel自带了一个非常好的缓存管理器,因此可以使用LaravelCache类作为缓存管理器。此包自带一个默认的KeyManager类。它支持一个可靠的缓存键生成器,但你可以定义自己的。

实际上,您只需要编辑您的RepositoryServiceProvider,告诉Laravel IoC容器使用缓存存储库。

<?php

use Illuminate\Support\ServiceProvider;
use MicheleAngioni\Support\Cache\KeyManager;
use MicheleAngioni\Support\Cache\LaravelCache;

class RepositoryServiceProvider extends ServiceProvider {

    public function register()
    {
        $this->app->bind(
            'PostRepositoryInterface', function($app)
            {
                $repo = $app->make('EloquentPostRepository');

                return new CachePostRepoDecorator($repo, new LaravelCache($app['cache']), new KeyManager);
            }
        );
    }
}

现在,您可以像以前一样使用Post存储库,但是在调用all()、find()或findOrFail()方法时,结果将被缓存(默认时间为10分钟,可以在配置文件中修改)。

提示

想要手动删除缓存结果?在您的Post模型中定义一个flush()方法如下:

public function flush()
{
    Cache::tags(get_called_class().'id'.$this->{$this->primaryKey})->flush();
}

然后,在编辑或删除Post模型时,您可以调用它以确保您的客户端不会收到过时的结果。

缓存模块还包含XML处理器。让我们以前面使用的staff.xml类为例。我们需要提供的缓存只是定义缓存XML存储库如下:

<?php

use MicheleAngioni\Support\Cache\CacheInterface;
use MicheleAngioni\Support\Cache\AbstractCacheSimpleXMLRepositoryDecorator;
use MicheleAngioni\Support\Repos\XMLRepositoryInterface;
use StaffXMLRepositoryInterface;

class CacheSimpleXMLStaffRepoDecorator extends AbstractCacheSimpleXMLRepositoryDecorator implements StaffXMLRepositoryInterface {

    /**
     * Section of the Cache the repo belongs to.
     *
     * @var string
     */
    protected $section = 'Forum';

    /**
     * Construct
     *
     * @param XMLRepositoryInterface  $repo
     * @param CacheInterface          $cache
     */
    public function __construct(XMLRepositoryInterface $repo, CacheInterface $cache)
    {
        parent::__construct($repo, $cache);
    }
}

并更新XMLRepositoryServiceProvider

<?php

use Illuminate\Support\ServiceProvider;
use CacheSimpleXMLStaffRepoDecorator;
use MicheleAngioni\Support\Cache\LaravelCache;

class XMLRepositoryServiceProvider extends ServiceProvider {

    public function register()
    {
        $this->app->bind(
            'StaffXMLRepositoryInterface', function($app)
        {
            $repo = $app->make('SimpleXMLStaffRepository');

            return new CacheSimpleXMLStaffRepoDecorator($repo, new LaravelCache($app['cache'])
            );
        });
    }
}

演示者使用

演示者是一种特殊的装饰者,用于在将对象发送到视图之前对其进行装饰。最常见的使用包括日期和数字格式化、文本验证和格式化、敏感数据掩盖。

支持提供了一种装饰Eloquent模型的方式。让我们继续使用我们的Post模型,并假设我们想在将模型传递到视图之前转义其text属性。

首先,通过扩展MicheleAngioni\Support\Presenters\AbstractPresenter并实现MicheleAngioni\Support\Presenters\PresentableInterface来定义PostDecorator。AbstractPresenter将允许通过使用PHP魔术方法__GET来访问所有模型属性。它还实现了ArrayAccess接口,因此我们可以以对象或数组的形式访问我们的属性。

<?php

use MicheleAngioni\Support\Presenters\AbstractPresenter;
use MicheleAngioni\Support\Presenters\PresentableInterface;

class PostPresenter extends AbstractPresenter implements PresentableInterface 
{
    
    public function text()
    {
        return e($this->object->text);
    }

    public function capitalText()
    {
        return e(strtoupper($this->object->text));
    }
}

现在,我们必须通过MicheleAngioni\Support\Presenters\Presenter类将演示者与Post模型配对,例如直接在PostController中。

<?php

use MicheleAngioni\Support\Presenters\Presenter;
use PostRepositoryInterface as PostRepo;

class PostController extends BaseController {

    private $postRepo;
    private $presenter;

    function __construct(PostRepo $postRepo, Presenter $presenter)
    {
        $this->postRepo = $postRepo;
        $this->presenter = $presenter;
    }

    public function index()
    {
        $posts = $this->postRepo->all();
        
        // Pass the post collection to the presenter
        $posts = $this->presenter->collection($posts, new PostPresenter());

        // Pass the post collection to the view
        return View::make('forum')->with('posts', $posts);
    }

    public function show($idPost)
    {
        $post = $this->postRepo->find($idPost);
        
        // Pass the post to the presenter
        $post = $this->presenter->model($post, new PostPresenter());

        // Pass the post to the view
        return View::make('forum')->with('post', $post);
    }
}

在上面的控制器中,我们在show方法中装饰了一个单独的模型,在index方法中装饰了一个整个集合,因此将它们传递到视图中。

在视图中,我们将拥有装饰后的模型,即PostPresenter的实例,其text属性已被转义。通过演示者,我们还可以向模型添加全新的功能:在这个例子中,我们添加了一个capitalText属性。

信号量使用

信号量模块由一个类组成,即SemaphoresManager。其构造函数需要一个缓存管理器和键管理器。支持包提供了这两个,因此我们可以将它们绑定到服务提供者的SemaphoresManager中。

<?php

use Illuminate\Support\ServiceProvider;
use MicheleAngioni\Support\Cache\KeyManager;
use MicheleAngioni\Support\Cache\LaravelCache;
use MicheleAngioni\Support\Semaphores\SemaphoresManager;

class SemaphoresServiceProviders extends ServiceProvider 
{
    public function register()
    {
        $this->app->bind(
            'MicheleAngioni\Support\Semaphores\SemaphoresManager', function($app)
            {
                return new SemaphoresManager(new LaravelCache($app['cache']), new KeyManager);
            });
    }
}

然后,我们可以简单地通过构造函数注入SemaphoresManager来由IoC容器解析,并可以通过以下方法使用它。

  • setLockingTime(int $minutes) : 设置信号量的锁定时间(分钟)
  • lockSemaphore($id, string $section) : 锁定属于输入部分的信号量,输入id
  • unlockSemaphore($id, string $section) : 解锁属于输入部分的信号量,输入id
  • checkIfSemaphoreIsLocked($id, string $section) : 检查属于输入部分的信号量是否被锁定
  • getSemaphoreKey($id, string $section) : 返回属于输入部分的信号量使用的缓存键

辅助工具使用

辅助类提供了几个有用的方法,简化了PHP开发。还提供了一个辅助工具外观,可以在app.php文件中的别名数组中注册

'Helpers' => MicheleAngioni\Support\Facades\Helpers::class

可用方法包括

  • isInt($int, int $min = null, int $max = null) : 检查输入的 $int 是否为整数。示例:int(4),字符串 '4',float(4),0x7FFFFFFF 将返回 true。int(4.1),字符串 '1.2',字符串 '0x8',float(1.2) 将返回 false。可以插入允许的最小和最大值。
  • randInArray(array $array) : 从数组中返回一个随机值
  • checkDate(string $date, string $format = 'Y-m-d') : 检查输入的日期是否基于输入的格式是有效的日期
  • checkDatetime(string $datetime) : 检查输入的 datetime 是否是有效的 'Y-m-d H:m:s' datetime
  • splitDates(string $firstDate, string $second_Date, int $maxDifference = 0) : 将两个 'Y-m-d' 格式的日期拆分为日期数组
  • daysBetweenDates(string $date1, string $date2) : 返回两个输入的 'Y-m-d' 或 'Y-m-d X'(X 是一些文本)日期之间的天数
  • getRandomValueUrandom(int $min = 0, int $max = 0x7FFFFFFF) : 通过使用 MCRYPT_DEV_URANDOM 源返回输入的 $min 和 $max 值之间的随机值
  • getUniqueRandomValues(int $min = 0, int $max, int $quantity = 1) : 返回 $quantity 个 UNIQUE 的 $min 和 $max 之间的随机值

在 3.0 版本中已删除

  • divideCollectionIntoGroups():使用 Collection 的 split 方法 代替
  • compareDates():使用 Carbon 代替
  • getTodayDay():使用 Carbon 代替
  • getDate():使用 Carbon 代替
  • getTime():使用 Carbon 代替
  • getRandomValueUrandom():使用 PHP 的 random_int 代替

自定义验证器

注册 SupportServiceProvider 之后,以下新的自定义验证器将可用

  • alpha_complete : 允许以下字符:字母、数字、空格、斜杠、管道和几个标点符号 | = # _ ! . , : / ; ? & ( ) [ ] { }
  • alpha_space : 允许以下字符:字母、数字和空格
  • alpha_underscore : 允许以下字符:字母、数字和下划线
  • alpha_names : 允许以下字符:字母、菜单、撇号、下划线和空格
  • alphanumeric_names : 允许以下字符:字母、数字、菜单、撇号、下划线和空格
  • alphanumeric_dotted_names : 允许以下字符:字母、数字、菜单、撇号、下划线、点和空格

自定义异常

以下新的自定义异常将可用

  • DatabaseException : 适用于查询错误发生的情况
  • DbClientRequestException : 当客户端需要的实体不可用时可以抛出
  • PermissionsException : 通用权限异常

API 文档

您可以通过浏览API 文档来获取支持。

贡献指南

支持遵循PSR-1、PSR-2和PSR-4 PHP编码标准,并采用语义版本控制。

欢迎提交pull request。

许可证

支持是免费软件,根据MIT许可证的条款进行分发。