开源 [轮子] Laravel 项目架构扩展包

laravel框架开发项目架构扩展包

前言

开发项目过程中由于分层比较多,便于后期维护,所以导致每次都需要手动创建文件和方法。 为了省略这些麻烦的操作,所以和小伙伴开发了一个架构扩展包,后面这些重复的机械操作可以用命令进行替代, 快速的进入到开发阶段。 如果觉得不错希望大家给个star,请大家帮忙多提意见,想参与开源的维护请联系我们。

功能

项目架构分层,代码生成器,拆分职责降低耦合。借鉴开源项目

扩展包地址:phpno1-architecture

目录

安装

使用要求

  • laravel >= 5.5
  • php >= 7.1

composer

执行以下命令获取包的最新版本:

    composer require phpno1/architecture

laravel

生成配置文件

    php artisan vendor:publish --provider "Phpno1\Architecture\Providers\ArchitectureServiceProvider"

注册到服务容器

说明:用命令生成仓储文件时(phpno1:entity || phpno1:repository),会自动生成ArchitectureServiceProvider文件。

    # 在config/app.php中
    'providers' => [
        // ......
        App\Providers\ArchitectureServiceProvider::class,
    ];

配置

    //architecture.php
    'pagination' => [//默认分页数量
        'limit' => 20
    ],

    'cache'      => [//缓存时间
        'enabled'    => true,
        'minutes'    => 10,
    ],

    'order' => [//排序字段
        'o',
    ],

    'generator'  => [//代码生成器命名空间
        'root_namespace' => 'App\\',
        'namespace' => [
            'controller'          => 'Backend',
            'repository_eloquent' => 'Repository\\Eloquent',
            'repository'          => 'Repository\\Contracts',
            'criteria'            => 'Architecture\\Criterias',
            'provider'            => 'Providers\\ArchitectureServiceProvider',
            'service'             => 'Services',
            'model'               => 'Models',
            'response'            => 'Http\\Responses',
            'filter'              => 'Repository\\Filters'
        ]
    ]

命令

说明:使用命令创建仓储文件时(phpno1:entity和phpno1:repository),会自动绑定接口与实现类关系。

生成组合配置

    //@params   {name}        生成文件名称
    //@params   {--resource}  生成资源方法(参照laravel控制器 资源控制器)
    php artisan phpno1:entity {name} {--resource}

生成控制器

    //@params  {name}          控制器名称
    //@params  {--resource}    生成资源方法(参照laravel控制器 资源控制器)
    php artisan phpno1:controller {name} {--resource}

生成业务处理类

    @params    {name}          文件名称 
    @params    {--resource}    生成资源方法 
    php artisan phpno1:service {name} {--resource}

生成扩展全局限制类

    @params    {name}          限制类名称
    php artisan phpno1:criteria {name}

生成过滤类

    @params    {name}          过滤类名称
    @params    {--prefix=}     命名空间
    @params    {--sort}        排序方法
    php artisan phpno1:filter {name} {--prefix=} {--sort}

生成模型类

    @params    {name}          模型名称
    php artisan phpno1:model {name}

生成服务器提供者,用于接口绑定实体类

    php artisan phpno1:provider

生成仓库类

    @params    {name}          仓库名称    
    php artisan phpno1:repository {name}

生成校验类

    @params    {name}          文件名称 
    @params    {--dir=}        生成目录
    php artisan phpno1:request {name} {--dir=}

生成响应类

    @params    {name}          文件名称 
    @params    {--dir=}        生成目录
    php artisan phpno1:response {name} {--dir=}

生成种子文件

    @params    {name}          文件名称 
    php artisan phpno1:seeder {name}

快速使用

1.执行以下命令:

    php artisan phpno1:entity User --resource

生成文件 (注:以下命令生成文件路径可以通过配置修改architecture.php)

  • app\Http\Controllers\Backend\UserController
  • app\Services\UserService
  • app\Repository\Eloquent\UserRepositoryEloquent
  • app\Repository\Contracts\UserRepository
  • app\Http\Requests\User\StoreRequest
    \UpdateRequest
  • app\Http\Responses\User\IndexResponse
    \ShowResponse
  • app\Models\UserModel
  • database\factories\UserFactory
  • database\migrations\UserTable
  • database\seeds\UserSeeder

2.修改代码

1).控制器

<?php

namespace App\Http\Controllers\Backend;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\UserService;
use App\Http\Responses\User\IndexResponse;

class UserController extends Controller
{

    private $user;

    public function __construct(UserService $userService)
    {
        $this->user=$userService;
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
        $result=$this->user->getUsers();//这里调用是业务层资源方法
        return new IndexResponse($result);
    }

2).响应(数据映射)


<?php

namespace App\Http\Responses\User;

use Illuminate\Contracts\Support\Responsable;
use App\Traits\ResponseTrait;

class UserIndexResponse implements Responsable
{
    use ResponseTrait;

    protected $result;

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

    public function toResponse($request)
    {
        $data = $this->transform();

        return $data;
    }

    protected function transform()
    {
        //分页数据例子
        $this->result->getCollection()->transform(function ($user) {
            return [
                'id'           => $user->id,
                'name'         => $user->name,
            ];
        });

        //集合数据例子   
        $this->result->transform(function ($user) {
             return [
                 'id'           => $user->id,
                 'name'         => $user->name,
             ];
        });
        return $this->result;
    }
}

方法介绍

  • Contracts : 仓储业务抽象接口。
  • Criterias : 全局通用业务抽取以及准对某一类业务的Scope。
  • Eloquent : 仓储业务具体实现。
  • Exceptions : 仓储异常处理。
  • Filters   : 根据参数自动过滤和排序。
  • Traits     : Trait封装

Phpno1\Repository\Contracts\IRepository;

  • function entity();
  • function all();
  • function find(int $id);
  • function first();
  • function count();
  • function findWhere(...$condition);
  • function findWhereFirst(...$condition);
  • function findWhereCount(...$condition);
  • function paginate(int $perPage = 10);
  • function create(array $properties);
  • function update(int $id, array $properties);
  • function delete(int $id);
  • function withCriteria(...$criteria);
  • function toEntity();
  • function toRepository(Builder $entity);

Phpno1\Repository\Eloquent;

    // 获取所有记录
    $this->repository->all();
    // 根据id查询单条记录
    $this->repository->find(int $id);
    // 获取第一条记录
    $this->repository->first();
    // 获取总记录数
    $this->repository->count();
    // 根据一个或多个 AND WHERE 条件查询。得到一个结果集
    // 单个条件写法:->findWhere('name', 'tome');
    // 多个条件写法:->findWhere(['name', 'tome'], ['age', '>', 20]);
    $this->repository->findWhere(...$condition);
    // 根据一个或多个 AND WHERE 条件查询。得到一条记录。
    $this->repository->findWhereFirst(...$condition);
    // 根据一个或多个 AND WHERE 条件获取记录数
    $this->repository->findWhereCount(...$condition);
    // 获取分页数据
    $this->repository->paginate(int $perPage = 10);
    // 插入记录
    $this->repository->create(array $properties);
    // 修改记录
    $this->repository->update(int $id, array $properties);
    // 根据id删除
    $this->repository->delete(int $id);
    // 用于加载预设的自定义标准操作 (Criterias目录中的类),后面会详细介绍
    $this->repository->withCriteria(...$criteria);
    // 把Repository转换成Model或者Build对象,后续可以使用框架提供的ORM操作
    $this->repository->toEntity();
    // 把Build对象转回Repository对象
    $this->repository->toRepository(Builder $entity);

初始化加载

    //请重写boot方法 不要重写构造方法
    function boot()
    {
    }

限制条件

编写Criteria类

namespace App\Repositories\Criterias;

class ByCreateTime implements ICriteria
{
    public function apply($entity)
    {
        return $entity->latest();   
    }
}

使用Criteria类


public function getUserListByCreateTime()
{
    return $this->repository
        ->withCriteria(
            new ByCreateTime()
            // more...
        )->paginate();
}

过滤条件

在Repository中配置要过滤和排序的映射字段

class UserRepositoryEloquent extends AbstractRepository implements UserRepository
{
    // 设置过滤映射
    protected $filters = [
        'email'    => EmailFilter::class,
        'name'     => NameFilter::class,
    ];

    public function entity()
    {
        return User::class;
    }

    // 过滤和排序在withCriteria中添加过滤和排序操作类FilterRequest
    public function findUserListByPage()
    {
        return $this->withCriteria(
            new FilterRequest($this->filters)
        )->paginate();
    }

    // something...
}

编写要过滤的业务

namespace App\Repositories\Filters\User;

use App\Repositories\Filters\AbstractFilter;

class NameFilter extends AbstractFilter
{
    // 过滤操作
    public function filter($entity, $value)
    {
        return $entity->where('name', $value);
    }
}

编写要排序的业务

注意:如果你需要字段排序,首先需要在生成的配置文件architecture.php中定义你需要的排序接收参数

在过滤类中必须实现接口 "IOrder"!!!

namespace App\Repositories\Filters\Admin;

use App\Repositories\Filters\{
    AbstractFilter,
    IOrder
};

// 需要排序,必须实现排序接口
class NameFilter extends AbstractFilter implements IOrder
{
    // 过滤操作
    public function filter($entity, $value)
    {
        return $entity->where('name', $value);
    }

    // 排序操作
    public function order($entity, $direction)
    {
        return $entity->orderBy('name', $this->resolveOrderDirection($direction));
    }
}

传递参数进行过滤操作

 // 过滤name和email
 http://www.phpno1.com/user?name=Anthony&email=king19800105@163.com

 // 过滤和排序组合。参数"o"表示排序方式。参数"orderable"表示要排序的字段
 http://www.phpno1.com/user?name=Anthony&email=king19800105@163.com&o=desc&orderable=name

scope

这里仓库层中的scope方法是兼容laravel模型的scope方法调用

注意如果调用scope方法请注意,必须先调用再执行withCriteria进行过滤


//模型中定义

class User extends Model
{
    public function scopeUserFields()
   {
       return $this->select(['id','name','email']);
   }
}

//仓库中调用
class UserRepositoryEloquent extends AbstractRepository implements UserRepository
{
    public function getUsers(int $perPage=0)
    {
            return $this->userFields()->withCriteria(
                new FilterRequest($this->filters)
            )->paginate($perPage);
    }
}

自定义


//当Repository提供的method无法满足业务
public function do()
{
    // 使用toEntity() 转换回Model或Build对象
    return $this->repository
        ->toEntity()
        ->where(...)
        ->orWhere(...)
        ->when(...)
        ->get(...);
}

缓存

使用CacheGenerate的trait特性
用途:当使用redis或memcahce做缓存时,方便做数据缓存操作。当然,您也可以使用Laravel框架提供的Cache。

    public function getUserList()
    {
        // 参数1:缓存的key值
        // 缓存中有数据则从缓存中取,没有数据则从数据库取一次放入缓存。
        $this->getOrCache('getUserList', function () {
            return $this->paginate();
        });
    }

注意事项

  • 建议文件生成都使用命令来操作。
  • 过滤必须要继承AbstractFilter (有特殊需求的可以实现IFilter接口) ,过滤必须实现IOrder接口。
  • 过滤和排序都是可选的。
  • 通过重写过滤中的mappings()方法来改变数据库字段和过滤使用的参数映射关系。
  • 通过重写AbstractFilter中的resolveOrderDirection()方法来改变排序方式的映射关系。
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
yourself
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5
bestcyt

呦呦:)

5年前 评论

一楼都被抢了,手慢了

5年前 评论

你大哥永远是你大哥

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!