您可以在此处重新定位控制器逻辑:
表单请求:简化数据验证。
在数据库交互期间调整数据。
响应特定的模型事件。
服务类:对您的业务逻辑进行分组。
操作类:处理单独的任务。
作业:管理后台任务。
事件和侦听器:协调应用程序事件和反应。
全局助手:随处可用的便捷功能。
Traits :可以在多个类中使用的代码片段。
基本控制器:具有共享功能的控制器。
存储库类:集中与数据库的交互。
简化您的控制器代码
让我们深入探讨如何组织一个粗壮的控制器并使其更加简洁。这是起始代码:
public function store(Request $request)
公共函数存储(请求$请求)
{
$this->authorize('user_create');
$this->authorize('user_create');
$userData = $request->validate([
$userData = $request->验证([
'name' => 'required',
'姓名' => '必填',
'email' => 'required|unique:users',
'电子邮件' => '必需|唯一:用户',
'password' => 'required',
'密码' => '必填',
]);
$userData['start_at'] = Carbon::createFromFormat('m/d/Y', $request->start_at)->format('Y-m-d');
$userData['start_at'] = Carbon::createFromFormat('m/d/Y', $request->start_at)->format('Ym-d');
$userData['password'] = bcrypt($request->password);
$userData['password'] = bcrypt($request->password);
$user = User::create($userData);
$user->roles()->sync($request->input('roles', []));
$user->roles()->sync($request->input('roles', []));
Project::create(['user_id' => $user->id, 'name' => 'Demo project 1']);
Project::create(['user_id' => $user->id, 'name' => '演示项目 1']);
Category::create(['user_id' => $user->id, 'name' => 'Demo category 1']);
Category::create(['user_id' => $user->id, 'name' => '演示类别 1']);
Category::create(['user_id' => $user->id, 'name' => 'Demo category 2']);
Category::create(['user_id' => $user->id, 'name' => '演示类别 2']);
MonthlyReport::where('month', now()->format('Y-m'))->increment('users_count');
MonthlyReport::where('month', now()->format('Y-m'))->increment('users_count');
$user->sendEmailVerificationNotification();
$用户->sendEmailVerificationNotification();
$admins = User::where('is_admin', 1)->get();
$admins = User::where('is_admin', 1)->get();
Notification::send($admins, new AdminNewUserNotification($user));
通知::发送($admins, new AdminNewUserNotification($user));
return response()->json(['result' => 'success', 'data' => $user], 200);
返回响应()->json(['结果' => '成功', '数据' => $user], 200);
}
注意:请记住,这是关于您感觉正确的事情。以下选项只是建议,您可以自由选择最适合您需求的选项。
使用表单请求简化 Laravel 验证
验证是任何 Laravel 应用程序的关键部分,确保正在处理的数据正确且有用。特别是在处理大量字段时,验证会使您的代码变得混乱。让我们简化一下。
首先,我们将验证规则转移到表单请求中。这不仅涉及数据验证,还涉及权限。
php artisan make:request StoreUserRequest
这会创建一个新文件 app\Http\Requests\StoreUserRequest.php 有两个关键方法:用于权限的authorize()和用于数据验证的rules() 。
您的表单请求应如下所示:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
public function authorize()
{
return Gate::allows('user_create');
}
public function rules()
{
return [
'name' => 'required',
'email' => 'required|unique:users',
'password' => 'required',
];
}
}
在您的控制器中,使用StoreUserRequest而不是默认的Request 。然后,您可以通过从请求中调用validated()方法来直接访问经过验证的数据。
例如,您的控制器可能如下所示:
public function store(StoreUserRequest $request)
{
$userData = $request->validated();
$userData['start_at'] = Carbon::createFromFormat('m/d/Y', $request->start_at)->format('Y-m-d');
$userData['password'] = bcrypt($request->password);
$user = User::create($userData);
$user->roles()->sync($request->input('roles', []));
// ...
}
这个小调整大大简化了控制器,使您的代码更有组织性和可管理性。
使用 Laravel 的 Eloquent 简化数据修改:Mutators 与 Observers
想象一下,您需要在将某些数据保存到数据库之前对其进行调整,例如日期格式或密码加密。 Laravel 的 Eloquent 不是在控制器中执行此操作,而是提供了高效的工具:Mutators 和 Observers。让我们看看它们是如何工作的。
变异器允许您在将模型的属性值保存到数据库之前对其进行处理。根据 Laravel 版本定义 Mutators 有两种主要方法:
从 Laravel 9 开始:
protected function startAt(): Attribute
{
return Attribute::make(
set: fn ($value) => Carbon::createFromFormat('m/d/Y', $value)->format('Y-m-d')
);
}
protected function password(): Attribute
{
return Attribute::make(
set: fn ($value) => bcrypt($value)
);
}
Observers: 观察员:
观察者为您提供在模型生命周期期间触发的方法,例如创建或更新条目时。
要为用户模型生成观察者,请使用:
php artisan make:observer UserObserver --model=User
以下是如何设置观察者以在创建记录之前修改数据的示例:
namespace App\Observers;
class UserObserver
{
public function creating(User $user)
{
$user->start_at = Carbon::createFromFormat('m/d/Y', $user->start_at)->format('Y-m-d');
$user->password = bcrypt($user->password);
}
}
请注意,某些方法(例如creating()可能没有正式记录,这表明它们并不总是推荐的方法。
考虑到易用性和 Laravel 官方指南,Mutators 通常是此类数据修改的首选。 现在,通过在控制器外部处理这些数据修改,代码变得更清晰:
public function store(StoreUserRequest $request)
{
$user = User::create($request->validated());
$user->roles()->sync($request->input('roles', []));
// ...
}
这说明了 Laravel 如何使用正确的工具帮助生成简洁且可维护的代码。
使用 Laravel 中的服务类简化控制器
接下来,在整理控制器的过程中,我们将深入研究负责在数据库中保存数据的核心逻辑。一个专门的服务类将成为我们完成这项任务的盟友。
服务类没有用于创建的特定 artisan 命令,因此我们将手动创建一个。让我们在app/Services目录中开发一个UserService.php文件,其中包含创建用户的逻辑:
namespace App\Services;
use App\Models\User;
class UserService
{
public function create(array $userData): User
{
$user = User::create($userData);
$user->roles()->sync($userData['roles']);
return $user;
}
}
在UserService中,我们引入了一个create()方法,该方法将经过验证的数据作为输入,编排用户创建、同步角色,然后返回新用户。
现在,为了在我们的控制器中利用此服务,我们遇到了多种路径。让我们揭开它们的面纱:
立即在控制器中启动服务,将经过验证的数据传递给我们的create()方法。
$user = (new UserService())->create($request->validated());
使用依赖注入将服务引入到我们的 Controller 方法中,从而实现一种简洁有效的方式来访问我们的服务功能。
public function store(StoreUserRequest $request, UserService $userService)
{
$user = $userService->create($request->validated());
// ...
}
选择服务类方法可以提升代码的组织性,通过保持控制器的精简和专注来提高可读性和可维护性。
Laravel 中的服务类和操作类:了解差异
在 Laravel 庞大的生态系统中,Service 和 Action 类都提供了有组织的方式来管理逻辑。它们看起来很相似,但根据您更喜欢构建应用程序逻辑的方式来使用。让我们简单地阐明这些概念。
Action 类是精确的,通常专用于单个操作。与 Service 类不同,没有内置的 Artisan 命令来生成 Action 类。因此,您将手动创建一个。
考虑一个Action类来管理用户创建:
namespace App\Actions;
use App\Models\User;
class CreateUserAction
{
public function execute(array $userData): User
{
$user = User::create($userData);
$user->roles()->sync($userData['roles']);
return $user;
}
}
要在控制器中实现此Action ,您需要实例化该类并执行其方法,传递必要的数据:
public function store(StoreUserRequest $request)
{
$user = (new CreateUserAction())->execute($request->validated());
// ...
}
比较服务类和操作类,人们可能会问:“是什么让它们与众不同?”它很大程度上取决于您喜欢如何分割应用程序的逻辑:
服务类:这些倾向于包含与特定模型或实体相关的操作,例如UserService或TaskService 。他们可能有多种方法来反映与该实体相关的各种任务。
操作类:它们封装了各个操作或动作,例如CreateUserAction或UpdateTaskAction 。通常,它们有一个主要方法,但它们也可以包含其他私有方法以实现更复杂的逻辑。
总而言之,Service 类和 Action 类之间的选择并不是为了哪个更优越,而是为了哪个更高级。这是关于您希望如何在应用程序中构建和传达操作的问题。这两种范式都旨在促进更干净、更有组织的代码。因此,请选择符合您的编码理念和项目具体要求的一种。
了解 Laravel 中的作业和后台任务
作业:在 Laravel 中,作业是可以在后台运行的任务。它们就像操作,但有一个额外的优点 - 它们可以排队。这意味着您可以延迟它们的执行,以避免应用程序立即加载。
感谢 Laravel 的 Artisan,创建工作非常简单:
php artisan make:job NewUserDataJob
php artisan make :job NewUserDataJob
基本作业如下所示:
use App\Models\Project;
use App\Models\Category;
class NewUserDataJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function handle()
{
Project::create([
'user_id' => $this->user->id,
'name' => 'Sample Project',
]);
Category::create([
'user_id' => $this->user->id,
'name' => 'Sample Category 1',
]);
Category::create([
'user_id' => $this->user->id,
'name' => 'Sample Category 2',
]);
}
}
要在控制器中使用此作业,您只需分派它:
public function store(StoreUserRequest $request)
{
$user = (new CreateUserAction())->execute($request->validated());
NewUserDataJob::dispatch($user);
// ... further code
}
队列:调度作业后,Laravel 通过队列系统处理后台任务。请记住,为了使队列高效工作,您必须设置和管理队列工作线程,这是一个单独的进程。
利用 Laravel 中事件和侦听器的力量
首先创建一个用于注册新用户的事件:
php artisan make:event NewUserRegistered
接下来,创建一个等待NewUserRegistered事件的监听器:
php artisan make:listener MonthlyReportUpdateListener
在您的控制器中,像处理作业一样调度事件:
public function register(RegistrationRequest $request)
{
$user = (new RegisterUserAction())->execute($request->validated());
NewUserDataJob::dispatch($user);
NewUserRegistered::dispatch($user);
//...
}
NewUserRegistered事件接受用户作为参数,因此我们的侦听器可以访问该用户:
class NewUserRegistered
{
public function __construct(public User $user) { }
}
在EventServiceProvider中,将事件链接到其侦听器:
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
NewUserRegistered::class => [
MonthlyReportUpdateListener::class,
// other listeners...
],
];
}
在MonthlyReportUpdateListener内,增加每月用户数:
class MonthlyReportUpdateListener
{
public function handle(NewUserRegistered $event)
{
MonthlyReport::where('month', now()->format('Y-m'))->increment('users_count');
}
}
Laravel 本身使用事件和监听器。例如,电子邮件验证通知由Registered事件自动触发。
您可以进一步扩展它。也许您想在新用户加入时通知管理员:
php artisan make:listener NotifyAdminsOfNewUser
然后,在这个新的侦听器中,通知管理员用户:
class NotifyAdminsOfNewUser
{
public function handle(NewUserRegistered $event)
{
$admins = User::where('is_admin', 1)->get();
Notification::send($admins, new NewUserAdminNotification($event->user));
}
}
您的控制器现在变得精简了:
public function register(RegistrationRequest $request)
{
$user = (new RegisterUserAction())->execute($request->validated());
NewUserDataJob::dispatch($user);
NewUserRegistered::dispatch($user);
return response()->json(['status' => 'success', 'user' => $user], 200);
}
仅通过这些步骤,您就大大减小了控制器的大小,并使您的应用程序更加模块化和事件驱动。
在 Laravel 中使用全局助手
Laravel 中的辅助类非常方便。将它们视为一个工具箱,里面装满了用于特定任务的各种工具(方法)。例如, DateHelper可以帮助进行日期操作,而CurrencyHelper可以帮助进行货币转换。
让我们分解一个实际用例以更好地理解它:
假设您的 User 模型中有一个名为startAt的属性。该属性具有日期格式操作
protected function startAt(): Attribute
{
return Attribute::make(
set: fn ($value) => Carbon::createFromFormat('m/d/Y', $value)->format('Y-m-d');
);
}
为了使此代码可重用,让我们将日期转换逻辑转移给助手。
在app/Helpers/DateHelper.php创建一个新文件。 (Laravel 没有内置命令,因此您需要手动创建它。)
在这个新的DateHelper类中,我们创建一个名为convertToDB的方法:
namespace App\Helpers;
use Carbon\Carbon;
class DateHelper
{
public static function convertToDB($date)
{
return Carbon::createFromFormat('m/d/Y', $date)->format('Y-m-d');
}
}
现在,返回您的 User 模型并更新startAt属性以使用这个新的帮助器:
protected function startAt(): Attribute
{
return Attribute::make(
set: fn ($value) => DateHelper::convertToDB($value);
);
}
现在,您已经在一个集中位置组织了日期操作逻辑。每当您需要将这种格式的日期转换为数据库时,只需调用此帮助程序即可。它使您的代码更整洁、更具可读性且易于维护。
在 Laravel 中管理重复部分:traits 还是 Base Controller?
在使用 Laravel 时,有效地处理重复任务至关重要,尤其是在响应一致性是关键的 API 控制器中。您可以通过使用 Base Controller 或 Traits 来管理重复的部分,如响应,从而简化流程。
一种简单的方法是将 logic 直接合并到 Base Controller ( app/Http/Controllers/Controller.php ) 中。
以下是构建它的方法:
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function respondOk($data)
{
return response()->json([
'result' => 'success',
'data' => $data,
], 200);
}
}
然后,在典型的控制器中,你可以毫不费力地调用这个 response 方法:
public function store(StoreUserRequest $request)
{
$user = (new CreateUserAction())->execute($request->validated());
NewUserDataJob::dispatch($user);
NewUserRegistered::dispatch($user);
return $this->respondOk($user);
}
特征提供了另一种优雅的解决方案。您可以创建一个特征来容纳您的响应逻辑。
Laravel 中是否需要 Repository 类?
在 Laravel 4 和 5 的早期,Repository 模式风靡一时。它充当 Laravel 的 Eloquent ORM(对象关系映射)和控制器之间的中间层。然而,随着时间的推移和 Laravel 的发展,这种方法变得不那么普遍。
为了理解这一点,我们来分解一下 Repository 模式的用途。在一般的编程中,Repository 充当 Controller 和 Database 之间的桥梁。这个概念在没有像 Laravel 的 Eloquent 这样的 ORM 机制的情况下特别有用
Laravel 的 Eloquent ORM 在设计上已经起到了这座桥的作用。它在控制器和数据库之间提供了一个抽象层,将我们的命令无缝地转换为数据库查询。例如:
而不是直接编写 SQL 查询,例如:
使用 Eloquent,我们可以简单地使用:
User::all();
鉴于此,将 Repository 作为 Eloquent 之上的附加层引入可以被视为多余。从本质上讲,您将在已经表现为该图层的内容之上添加一个图层。 总之,虽然 Repository 模式在特定上下文中有其优点,但在 Laravel 的世界中,它可能是一个额外的层,不会增加显着优势。在决定架构模式之前,考虑项目的具体需求和结构始终是必不可少的。