michele-angioni / support
Support 是一个 Laravel 扩展包,旨在推广最佳实践和设计模式的使用。
Requires
- php: >=7.1.3 <8.0.0
- illuminate/cache: 5.8.x
- illuminate/console: 5.8.x
- illuminate/database: 5.8.x
- illuminate/pagination: 5.8.x
- illuminate/support: 5.8.x
- illuminate/validation: 5.8.x
Requires (Dev)
- mockery/mockery: ^1.1
- phpunit/phpunit: ^7.5
- sami/sami: ~3.3|~4.0
This package is auto-updated.
Last update: 2024-09-10 04:07:45 UTC
README
介绍
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 update
或 composer 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' => ['
,其中<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');
!!警告!!
AbstractEloquentRepository
和AbstractSimpleXMLRepository
类不提供任何输入验证!
缓存使用
缓存模块可以通过使用装饰者模式来为我们的存储库添加缓存功能。然后,我们可以继续之前关于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)
: 锁定属于输入部分的信号量,输入idunlockSemaphore($id, string $section)
: 解锁属于输入部分的信号量,输入idcheckIfSemaphoreIsLocked($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' datetimesplitDates(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许可证的条款进行分发。