michele-angioni/support 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

michele-angioni/support

Composer 安装命令:

composer require michele-angioni/support

包简介

Support is a Laravel package which promotes the use of best practices and design patterns.

README 文档

README

License Latest Stable Version Build Status SensioLabsInsight

Introduction

Support consists of a series of useful classes to ease development and the use of best practices and design patterns with Laravel 5.

Part of this package has been highly inspired by the culttt.com blog, which I highly recommend to both new and experienced developers since it focuses on a wide range of topics with always interesting point of views and discussions. I have personally learned much from it.

Support requires PHP 7.1.3+ and several 5.8 Illuminate components in order to work.

In order to use Support with Laravel or Lumen 5.4 - 5.7, check version 3.x.

Installation

Support can be installed through Composer, just include "michele-angioni/support": "^4.0" to your composer.json and run composer update or composer install.

Then add the Support Service Provider in the Laravel app.php config file, under the providers array

MicheleAngioni\Support\SupportServiceProvider::class

and the Helpers facade in the aliases array

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

Laravel 5.4 - 5.7

If you are looking for the Laravel 5.3 - 5.7 compatible version, check the 3.x branch and its documentation.

Laravel 5.0 - 5.3

If you are looking for the Laravel 5.0 - 5.3 compatible version, check the 2.x branch and its documentation.

Laravel 4

If you are looking for the Laravel 4 version, check the 1.0 branch and its documentation.

Lumen

At the moment, only partial and unstable support for Lumen is guaranteed.

First of all load the Service Provider in your bootstrap file

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

and set the needed config key

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

Modules summary

Support comes bundled of the following features: Repositories, Cache, Presenters, Semaphores, an Helpers class and new custom validators. In addition Support provides several new custom exceptions.

Configuration

Support does not need any configuration to work. However, you may publish the configuration file through the artisan command php artisan vendor:publish that will add the ma_support.php file in your config directory.
You can then edit this file to customize Support behaviour.

In order to access to the file keys in your code, you can use config('ma_support.key'), where key is one of the file keys.

Repositories Usage

The abstract class AbstractEloquentRepository consists of a model wrapper with numerous useful queries to be performed over the Laravel models. This way implementing the repository pattern becomes straightforward.

As an example let's take a Post model. First of all we shall create a common Repository Interface for all models of our application that extends the package RepositoryInterface

 <?php

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

Then let's define a Post Repository Interface which will be injected in the constructor of the classes we need and that extends the common RepositoryInterface we have just created. Let's define the PostRepositoryInterface as

 <?php

 interface PostRepositoryInterface extends RepositoryInterface {}

We need now an implementation. The easiest way to create a Post repository is to define a class as such

<?php

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

class EloquentPostRepository extends AbstractEloquentRepository implements PostRepositoryInterface
{
    protected $model;

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

Now we need to bind the implementation to the interface, which can be done by adding

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

to an existing Laravel Service Provider. Or we can create a brand new one

<?php

use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider 
{

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

and add it to the config/app.php file in the providers array

RepositoryServiceProvider::class,

Suppose that now we need the Post repo in our PostController. We simply inject our PostRepositoryInterface in the controller which gets resolved thanks to the Laravel IoC Container

<?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
    }
}

The AbstractEloquentRepository empowers automatically our repositories of the following public methods:

  • 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 = [])

The $where array can have both format ['key' => 'value'] and ['key' => [<operator>, 'value']], where <operator> can be =, < or >.

The Repository module also supports xml repositories. Suppose we have a staff.xml file. We need to define a StaffXMLRepositoryInterface

<?php

interface StaffXMLRepositoryInterface {}

then we can create our xml repository as follows

<?php

use MicheleAngioni\Support\Repos\AbstractSimpleXMLRepository;

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

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

the $xmlPath property defines the path to the xml file (base path is the /app folder) while the $autoload property defines whether the xml file is automatically loaded when instantiating the class. The AbstractSimpleXMLRepository contains the methods we need:

  • getFilePath() : return the xml file path
  • loadFile() : load the xml file for later use
  • getFile() : load the xml file if not previously loaded and return it as an SimpleXMLElement instance

As done with "standard" repositories, we need to instruct the IoC Container. We can achieve that by defining the following XMLRepositoryServiceProvider

<?php

use Illuminate\Support\ServiceProvider;

class XMLRepositoryServiceProvider extends ServiceProvider 
{

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

We can then inject the repo in the class we need or simply call it through the Laravel application instance / facade

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

!!Warning!!

The AbstractEloquentRepository and AbstractSimpleXMLRepository classes do NOT provide any input validation!

Cache Usage

The Cache module can be used to give Cache capabilities to our repositories, through the use of the decorator pattern. We can then continue our previous example of a Post model and its repo. We define a CachePostRepoDecorator as follows

<?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);
    }
}

The section property can be used to define a Cache section and it is used when generating the Cache keys.

This class implements the PostRepositoryInterface, so that it is recognized as the Post repo, of which it is nothing but a wrapper. It also extends the AbstractCacheRepositoryDecorator where all magic happens. The AbstractCacheRepositoryDecorator implements the RepositoryCacheableQueriesInterface, which is a very basic interface which instructs our system which repo methods are going to be cached.

Default methods are all(), find() and findOrFail(), but you can define your own interface and abstract cache decorator with more methods.

The AbstractCacheRepositoryDecorator constructor needs a repository implementing the RepositoryCacheableQueriesInterface, a Cache manager implementing the CacheInterface and a Key Manager implementing the KeyManagerInterface. Laravel has out of the box a very good Cache manager, so the LaravelCache class can be used as Cache Manager. This package comes with a KeyManager class as the default Key Manager. It supports a solid Cache key generator, but you can define your own.

In fact, all you need to use the Cache is to edit your RepositoryServiceProvider instructing the Laravel IoC Container to use the caching repo

<?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);
            }
        );
    }
}

Now you can use the Post repository as before, but when calling the all(), find() or findOrFail() methods the result will be cached (default time: 10 minutes It can be modified in the configuration file).

Hint

Want to manually delete a cache result? In your Post model define a flush() method as follows

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

You can then call it when editing or deleting a Post model you that your clients don't get outdated results.

The Cache module comes with xml handlers too. Let's take the staff.xml class we used before. All we need to provide cache is to define the caching xml repo as follows

<?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);
    }
}

and update the 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'])
            );
        });
    }
}

Presenters Usage

A Presenter is a particular kind of decorator, used to decorate an object before sending it to the view. The most common uses include date and numbers formatting, text validation and formatting, obscuration of sensible data.

Support provides an easy way to decorate Eloquent models. Let's continue to use our Post model and suppose we want to escape its text attribute before passing the model to the view.

First of all define the PostDecorator by extending MicheleAngioni\Support\Presenters\AbstractPresenter and implementing MicheleAngioni\Support\Presenters\PresentableInterface. The AbstractPresenter will allow to access al model's attributes through the use of PHP magic method __GET. It also implements ArrayAccess interface so that we can keep to access our attributes both as an object and as array.

<?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));
    }
}

Now we have to couple our presenter to the Post model with the help of the MicheleAngioni\Support\Presenters\Presenter class, for example directly in the 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);
    }
}

In the above Controller we have decorated a single model in the show method and an entire collection in the index method, thus passed them to the view.

In the view we will have our decorated models, instances of PostPresenter, and the text attribute will be already escaped. Through the presenter we can also add brand new functionality to our models: in this example we added a capitalText attribute.

Semaphores Usage

The semaphores module consists of a single class, the SemaphoresManager. Its constructor needs a Cache Manager and a Key Manager. The Support package provides both of them, so we can bind them to the SemaphoresManager in a service provider

<?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);
            });
    }
}

We can them simply inject the SemaphoresManager in a constructor to be resolver by the IoC Container and it is ready to use through the following methods:

  • setLockingTime(int $minutes) : set the semaphores locking time in minutes
  • lockSemaphore($id, string $section) : lock a semaphore with input id belonging to input section
  • unlockSemaphore($id, string $section) : unlock the semaphore with input id belonging to input section
  • checkIfSemaphoreIsLocked($id, string $section) : check if the semaphore with input id belonging to input section is actually locked
  • getSemaphoreKey($id, string $section) : return the cache key used by the semaphore with input id belonging to input section is actually locked

Helpers Usage

The helpers class provides several useful methods which simplify php development. Support has also an Helpers facade which can be registered in the app.php file under the aliases array as

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

The available methods are:

  • isInt($int, int $min = null, int $max = null) : check if input $int is an integer. Examples: int(4), string '4', float(4), 0x7FFFFFFF will return true. int(4.1), string '1.2', string '0x8', float(1.2) will return false. min and max allowed values can be inserted.
  • randInArray(array $array) : return a random value out of an array
  • checkDate(string $date, string $format = 'Y-m-d') : check if input date is a valid date based of input format
  • checkDatetime(string $datetime) : check if input datetime is a valid 'Y-m-d H:m:s' datetime
  • splitDates(string $firstDate, string $second_Date, int $maxDifference = 0) : split two 'Y-m-d'-format dates into an array of dates
  • daysBetweenDates(string $date1, string $date2) : return the number of days between the two input 'Y-m-d' or 'Y-m-d X' (X is some text) dates
  • getRandomValueUrandom(int $min = 0, int $max = 0x7FFFFFFF) : return a random value between input $min and $max values by using the MCRYPT_DEV_URANDOM source
  • getUniqueRandomValues(int $min = 0, int $max, int $quantity = 1) : return $quantity UNIQUE random value between $min and $max

Removed in 3.0 version

  • divideCollectionIntoGroups(): use Collection's split method instead
  • compareDates(): use Carbon instead
  • getTodayDay(): use Carbon instead
  • getDate(): use Carbon instead
  • getTime(): use Carbon instead
  • getRandomValueUrandom(): use PHP's random_int instead

Custom validators

Just after registering the SupportServiceProvider, the following new custom validators will be available:

  • alpha_complete : The following characters are allowed: letters, numbers, spaces, slashes, pipes and several punctuation characters | = # _ ! . , : / ; ? & ( ) [ ] { }
  • alpha_space : The following characters are allowed: letters, numbers and spaces
  • alpha_underscore : The following characters are allowed: letters, numbers and underscores
  • alpha_names : The following characters are allowed: letters, menus, apostrophes, underscores and spaces
  • alphanumeric_names : The following characters are allowed: letters, numbers, menus, apostrophes, underscores and spaces
  • alphanumeric_dotted_names : The following characters are allowed: letters, numbers, menus, apostrophes, underscores, dots and spaces

Custom Exceptions

The following new custom exceptions will be available:

  • DatabaseException : thought to be used where a query error arises
  • DbClientRequestException : can be thrown when an entity required by a client is not available
  • PermissionsException : a general permission exception

API Docs

You can browse the Support API Documentation.

Contribution guidelines

Support follows PSR-1, PSR-2 and PSR-4 PHP coding standards, and semantic versioning.

Pull requests are welcome.

License

Support is free software distributed under the terms of the MIT license.

michele-angioni/support 适用场景与选型建议

michele-angioni/support 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.45k 次下载、GitHub Stars 达 18, 最近一次更新时间为 2015 年 01 月 05 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「helpers」 「cache」 「support」 「laravel」 「repos」 「semaphores」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 michele-angioni/support 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 michele-angioni/support 我们能提供哪些服务?
定制开发 / 二次开发

基于 michele-angioni/support 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 1.45k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 18
  • 点击次数: 24
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 18
  • Watchers: 1
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2015-01-05