louisk/artisan-faz-pra-mim

Laravel 5 生成器

dev-master 2019-07-07 05:49 UTC

This package is auto-updated.

Last update: 2024-09-07 16:59:40 UTC


README

待办事项

  • 关联关系,无存储和无更新
  • 修复发现的错误
  • 复制请求类
  • 分析是否值得将基本文件放在 vendor 中

如何使用

确保您的数据库已建模,最好使用英文,并且您的 .env 已正确配置

运行命令
composer require louisk/artisan-faz-pra-mim

如果您的项目是 Laravel 小于 v5.5,请在 config/app.php 中注册

   /*
   * Package Service Providers...
   */
  Louisk\ArtisanFazPraMim\FazPraMimServiceProvider::class,

运行命令以配置您的项目,例如文件夹和您想要的功能,命令将生成 config/faz-pra-mim.php
如果您想要默认项目,则无需配置
php artisan vendor:publish --tag=faz-pra-mim

运行命令以生成您的项目
php artisan faz-pra-mim

完成!

Artisan Faz Pra Mim 项目结构

  • 控制器
  • 服务
  • 规则
  • 资源
  • 观察者
  • JWT
  • 路由
  • 生成器

控制器

目标
  • 控制请求服务的规则
  • 控制请求服务的返回类型
目录
  • app/Http/Controllers/{Guard}
  • 将 {guard} 替换为用户类型,例如 客户或管理员
函数示例
public function index(Request $request): OficinaResource
{
    $data = $request->toCollection();

    $data['ativo'] = true;

    $data['plano_ativo'] = true;

    if (auth()->user()->carroAtual) {
        $data['veiculo'] = auth()->user()->carroAtual->modelo->veiculo_id;
        $data['marca'] = auth()->user()->carroAtual->modelo->veiculo->marca_id;
    }

    $this->oficinaService->relationsCount = ['avaliacoes'];

    $model = $this->oficinaService->index($data);

    return new OficinaResource($model);
}
注意事项
  • 客户 请求 车间 列表时,应仅显示活跃的和有活跃计划的,同时显示其评价数量。
  • 如果客户有一辆车,应寻找该品牌和车型的车间。
  • 通常在控制器中使用 auth() 函数,而不是在服务中使用
  • 获取结果后,如果它是 API,则返回 Resource,如果是网页,则返回 View

服务

目标
  • 服务于任何控制器(任何人都可以调用服务)
  • 调用数据的验证,无论调用者是谁
  • 包含系统的大多数逻辑
  • 如果它不是列表方法(index),则返回 Model,如果是 index 方法,则返回 CollectionLengthAwarePaginator
  • 始终使用 Eloquent,在仪表板的情况下使用 \DB::table()
目录
  • app/Services
文档
  • 关系:[Laravel Eloquent 关系](https://laravel.net.cn/docs/5.7/eloquent-relationships#defining-relationships)
  • 查询:[Laravel 查询](https://laravel.net.cn/docs/5.7/queries)
  • 通过关系查询:[Laravel Eloquent 关系查询](https://laravel.net.cn/docs/5.7/eloquent-relationships#querying-relations)
函数示例
 public function store(Collection $data): Anuncio
    {

        if ($data->get('credito') && (!$data->get('parcelas') || $data->get('parcelas') <= 0)) {
            $data->put('parcelas', 1);
        }

        $this->validateWithArray($data->toArray(), AnuncioRules::store());

        if (auth()->user() instanceof Usuario && auth()->user()->oficina->creditos_anuncios <= 0) {
            throw new Exception('Oficina sem creditos para criar anuncios', Response::HTTP_BAD_REQUEST);
        }

        $data['thumbnail_id'] = Servico::find($data['servico_id'])->thumbnail_id;

        if (!$data->get('validade')) {
            $data['validade'] = now()->addDays(15);
        }

        \DB::beginTransaction();

        $model = Anuncio::create($data->all());

        $modelos = $data->get('modelos', []);
        $veiculos = $data->get('veiculos', []);
        $marcas = $data->get('marcas', []);

        if (count($modelos) > 0) {
            $model->modelos()->sync($modelos);
        }
        if (count($veiculos) > 0) {
            $model->veiculos()->sync($veiculos);
        }
        if (count($marcas) > 0) {
            $model->marcas()->sync($marcas);
        }

        \DB::commit();
        return $this->show($model->getKey());
    }
注意事项
  • 尽可能不使用 auth() 方法
  • 始终在数据库修改中使用 \DB::beginTransaction();\DB::commit();
  • 始终根据模型创建关系
  • 始终使用验证 $this->validateWithArray($data->toArray(), AnuncioRules::store());
  • 始终验证模型状态的变化
  • 如果需要,创建一个函数来验证用户是否可以更改模型(除了使用 auth() 方法之外)

规则(验证)

目标
  • 有静态方法可以在代码的任何地方使用(通常在服务和在其他情况下在控制器中使用)
  • 包含所有 Models 的创建和编辑规则
目录
  • app/Validators
文档
  • 可用的验证:[Laravel 验证规则](https://laravel.net.cn/docs/5.7/validation#available-validation-rules)
  • 自定义验证:[Laravel 自定义验证规则](https://laravel.net.cn/docs/5.7/validation#custom-validation-rules)
函数示例
 public static function store(): array
 {
     return [
         'titulo' => 'required|min:3|string',
         'descricao' => 'required|min:10|string',
         'valor_original' => 'required|numeric|greater_than_field:valor_promocional',
         'valor_promocional' => 'required|numeric|lower_than_field:valor_original',
         'mao_obra' => 'required|numeric',
         'peca_original' => 'nullable|numeric',
         'dinheiro' => 'required|boolean',
         'debito' => 'required|boolean',
         'credito' => 'required|boolean',
         'parcelas' => 'nullable|required_if:credito,true|min:1',
         'validade' => 'nullable|date_format:"Y-m-d"|before:"'.now()->addDays(16)->toDateString().'"',
         'servico_id' => 'required|exists:servicos,id',
         'oficina_id' => 'required|exists:oficinas,id',
         'marcas' => 'array',
         'marcas.*' => 'exists:marcas,id',
         'veiculos' => 'array',
         'veiculos.*' => 'exists:veiculos,id',
         'modelos' => 'array',
         'modelos.*' => 'exists:modelos,id',
         'raio' => 'required|integer|min:1',
     ];
 }
注意事项
  • 尽可能验证所有类型的字段
  • 在数组字段中验证字段,例如 'modelos' => 'array''modelos.*' => 'exists:modelos,id'
  • 如有必要,向规则发送参数进行特定检查
  • 始终返回一个数组
  • 要创建自定义验证,有两种方法,即使用 Validator::extend() 或在 app/Rules 中创建一个 Rule
  • 当使用 Validator::extend() 时,创建一个提供者并将所有内容放在那里,不要忘记在 config/app.php 中注册提供者

观察者

目标
  • 管理通知
  • 管理状态更改
  • 方法必须执行的常规操作
目录
  • app/Observers
文档
  • 观察者:[https://laravel.net.cn/docs/5.7/eloquent#observers](https://laravel.net.cn/docs/5.7/eloquent#observers)
函数示例
public function updated(Anuncio $anuncio)
    {
        if ($anuncio->getOriginal('status') !== $anuncio->status) {
            switch ($anuncio->status) {
                case Anuncio::EM_AVALIACAO:

                    break;
                case Anuncio::INATIVO:

                    break;
                case Anuncio::EXPIRADO:
                    $this->notificaOficinaAnuncioExpirado($anuncio);
                    break;
                case Anuncio::ATIVO:
                    if (auth()->user() instanceof Administrador) {
                        $this->notificaOficinaAnuncioAprovado($anuncio);
                    }
                    break;
                case Anuncio::REPROVADO:
                    $anuncio->oficina->adicionarCreditos();
                    $this->notificaOficinaAnuncioReprovado($anuncio);
                    break;
                case Anuncio::REVISAO:
                    $this->notificaOficinaAnuncioRevisao($anuncio);
                    break;
            }
        }
    }
注意事项
  • 始终检查原始字段(旧字段)与当前字段(更新后的字段)是否不同
  • 发送通知
  • 可用的函数有:retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored
  • 如果可能,创建一个提供者来注册观察者,并在 config/app.php 中注册
  • 为了注册,使用 User::observe(UserObserver::class); 在一个提供者中,例如 config/app.php

资源

目标
  • 为客户端构建响应
  • 管理返回类型
目录
  • app/Http/Resources
文档
  • 资源API:[https://laravel.net.cn/docs/5.7/eloquent-resources](https://laravel.net.cn/docs/5.7/eloquent-resources)
函数示例
public function toResource($resource)
{
    $data = [
        'id' => $resource->getKey(),
        'status' => (int) !$resource->trashed(),
        'nome' => $resource->nome,
        'tipo' => $resource->tipo,
        'meses' => $resource->meses,
        'quilometros' => $resource->quilometros,
        'quilometros_aviso' => $resource->quilometros_aviso,
        'meses_aviso' => $resource->meses_aviso,
        'thumbnail' => new ThumbnailResource($resource->thumbnail),
        'aviso_normal' => $resource->aviso_normal,
        'aviso_manutencao' => $resource->aviso_manutencao,
    ];
    if (request()->has('especialidade')) {
        $data['especialidade'] = new EspecialidadeResource($resource->especialidade);
    }

    return $data;
}

public function toCollection($collection)
{
    if(!request()->has('groupEspecialidade')){
        return parent::toCollection($collection);
    }else{
        $especialidades = [];
        foreach($collection as $value){
            if(!isset($especialidades[$value->especialidade->id])){
                $especialidades[$value->especialidade->id]['nome'] = $value->especialidade->nome;
            }
            $especialidades[$value->especialidade->id]['servicos'][] = $this->toItemOfCollection($value);

        }
        return $especialidades;
    }
}
注意事项
  • 始终确保在资源中不进行数据库查询
  • 始终保持响应的标准化
  • 更改资源时要小心,因为可能会为客户端生成异常
  • 根据设计界面进行

JWT

目标
  • 通过守卫管理令牌
  • 管理令牌刷新
目录
  • app/Http/Middleware/CheckToken.php
  • 如果没有此文件,从 [https://github.com/keviinlouis/ProjetoPadraoLaravel/blob/master/app/Http/Middleware/CheckToken.php](https://github.com/keviinlouis/ProjetoPadraoLaravel/blob/master/app/Http/Middleware/CheckToken.php) 复制
文档
  • 包:[https://github.com/tymondesigns/jwt-auth](https://github.com/tymondesigns/jwt-auth)
描述
  • 为了认证使用了JWT,当进行登录时会返回一个令牌,并且需要通过header的Authorization类型为Bearer传递此令牌

  • 示例:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwMDAvYXBpL2xvZ2luIiwiaWF0IjoxNTE2Mjc5MTcyLCJleHAiOjE1MTYyODI3NzIsIm5iZiI6MTUxNjI3OTE3MiwianRpIjoib2h0a0VxVUtIdkkzakxMdSIsInN1YiI6MSwicHJ2IjoiZjkzMDdlYjVmMjljNzJhOTBkYmFhZWYwZTI2ZjAyNjJlZGU4NmY1NSJ9.cOjyx6-x3_KHwGgfSh9bSX0g90hNOcyFGB4sMBoMRCI

  • 每个生成的令牌都会在 .env 中记录的时间后过期,因此当它过期时,将在response的header中返回一个新的参数new_token,以替换旧的令牌。

  • 为此,每个response都应被拦截以检查是否存在header中的new_token。

  • 要在路由中使用,请在 app/Http/Kernel.php 中注册middleware为jwt,并在路由中注册middleware为jwt:{guard}(例如:jwt:cliente

  • config/auth.php 中注册guard为jwt

注意事项
  • 始终在.env中注册变量JWT_SECRETJWT_TTLJWT_REFRESH_TTLJWT_BLACKLIST_GRACE_PERIOD
  • 运行命令 php artisan jwt:secret
路由中的函数示例
Route::group(['middleware' => 'jwt:admin'], function(){
    ...
});
Kernel.php中的示例
protected $routeMiddleware = [
    ...
    'jwt' => CheckToken::class
    ...
];
守卫示例
'guards' => [
    ...
    'admin' => [
        'driver' => 'jwt',
        'provider' => 'admin'
    ],
    ...
],
中间件示例
public function handle($request, \Closure $next, $guard = null)
{
    $this->checkForToken($request); // Ver se tem o token

    if(!$guard){
        throw new \Exception('Guard inválido', 500);
    }

    Config::set('auth.defaults.guard', $guard);

    try {
        $user = $this->auth->parseToken()->getPayload()['sub'];
        if (!$this->checkModels($user->class, $guard) || !auth()->guard($guard)->onceUsingId($user->id)) { // Check user not found. Check token has expired.
            throw new UnauthorizedHttpException('jwt-auth', 'Usuario não encontrado'); //Se der problema com o token, virá um header chamado WWW-Authenticate com o valor de jwt-auth
        }
        return $next($request); // Se o usuario for autenticado e logado com token válido, continua request

    } catch (TokenExpiredException $t) { // Token expirado, usuario não logado
        $payload = $this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray(); // Pega os dados do token para autenticação

        $refreshed = JWTAuth::refresh(JWTAuth::getToken()); // Faz refresh do token
        $user = $payload['sub'];

        auth()->guard($guard)->onceUsingId($user->id); // Autentica pelo ID

        $response = $next($request); // Pega a request

        $response->header('new_token', $refreshed); // Adiciona o header com o novo token

        return $response; // Responde com o novo token no header

    } catch (JWTException $e) {
        throw new UnauthorizedHttpException('jwt-auth', 'Token Inválido', $e, Response::HTTP_UNAUTHORIZED); //Se der problema com o token, virá um header chamado WWW-Authenticate com o valor de jwt-auth
    }
}

public function getClassBybGuard($guard)
{
    $provider = config('auth.guards.'.$guard.'.provider');
    $class = config('auth.providers.'.$provider.'.model');

    return $class;

}

public function checkModels($class, $guard)
{
   $classAuth = $this->getClassBybGuard($guard);

   return $classAuth == $class;
}

路由

目标
  • 为每个守卫创建一个路由文件
  • 管理系统中的可登录用户
  • app/Providers/RouteServiceProvider 中注册文件
目录
  • routes
文档
  • 路由:[https://laravel.net.cn/docs/5.7/routing](https://laravel.net.cn/docs/5.7/routing)
路由中的函数示例
Route::post('/login', 'AdminController@login');
Route::post('/solicitar-redefinir-senha', 'AdminController@solicitarRedefinirSenha');
Route::post('/redefinir-senha', 'AdminController@redefinirSenha')->name('redefinir-senha');

Route::group(['middleware' => 'jwt:admin'], function(){
    Route::get('/dashboard', 'DashboardController@dashboard');
    Route::get('/dashboard/oficinas/{cidade?}', 'DashboardController@getLatLongOficinasPelaCidade');

    Route::get('/me', 'AdminController@me');
    Route::get('/me/logout', 'AdminController@logout');
    Route::put('/me', 'AdminController@update');
    Route::delete('/me', 'AdminController@destroy');

    Route::get('configuracao', 'ConfiguracaoController@index');
    Route::put('configuracao', 'ConfiguracaoController@update');
});

在Service Provider中注册的函数示例

注意事项
  • 如果是API,始终放置jwt的guard
  • 始终使用 Route::group();来分组放置内容

生成器

目标
  • 生成模型
  • 生成迁移
文档
  • 迁移包:[https://github.com/Xethron/migrations-generator](https://github.com/Xethron/migrations-generator)
  • 模型包:[https://github.com/reliese/laravel](https://github.com/reliese/laravel)
注意事项
  • 始终关注数据库建模
  • 仔细阅读包的文档