louisk / artisan-faz-pra-mim
Laravel 5 生成器
Requires
- php: ^7.1.3
- illuminate/support: 5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.*
- reliese/laravel: ^0.0.13
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
方法,则返回Collection
或LengthAwarePaginator
- 始终使用
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_SECRET
、JWT_TTL
、JWT_REFRESH_TTL
、JWT_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)
注意事项
- 始终关注数据库建模
- 仔细阅读包的文档