如何仅向管理员创建的用户发送凭据邮件?哪种实现方案更优?
问题背景
我的User模型如下:
class User extends Authenticatable implements MustVerifyEmail { use HasApiTokens; use HasFactory; use Notifiable; use TwoFactorAuthenticatable; protected $fillable = [ 'name', 'email', 'password', 'card_id', 'assigned_to', 'user_type',//4 = admin 3 = manager 2 = business 1 = courier 0 = client 'password_changed_at', 'email_verified_at', ]; }
用户创建规则:
- 用户创建时基于
user_type分配对应角色 user_type为1(快递员)、2(商家)、3(经理)的用户由管理员创建,需要给这些用户发送包含登录凭据的邮件- 当前实现是在控制器的
createUser方法中直接创建用户并发送邮件:
public function createUser(){ $this->validate(); $randomPass = Str::random(8); $userCreds = ['email' => $this->email, 'password' => $randomPass]; try{ User::firstOrCreate([ 'name' => $this->name, 'email' => $this->email, 'password' => Hash::make($randomPass), 'email_verified_at' => Carbon::now(), 'user_type' => (int)$this->selectedUser, 'assigned_to' => (int)$this->selectedAssigned, 'doctor_type' => $this->selectTypeDoc, ]); Mail::to($this->email)->send(new SendUserMail($userCreds)); $this->reset(); $this->emitUp('refreshParent'); $this->dispatchBrowserEvent('swal-insert'); }catch(\Exception $e){ $this->dispatchBrowserEvent('createUser-error'); } $this->closeModal(); }
想请教:是否有更优的实现方式?使用EventServiceProvider还是控制器更合适?
更优实现方案分析
直接在控制器实现的问题
当前控制器代码把用户创建和邮件发送逻辑强耦合在一起,后续如果要调整邮件规则(比如新增需发邮件的用户类型、修改邮件内容),或者添加其他后续操作(比如记录操作日志、触发业务通知),都要修改控制器代码,违反了单一职责原则,不利于长期维护。
推荐使用事件驱动实现
用Laravel的事件系统(EventServiceProvider)是更优选择,核心优势:
- 解耦业务逻辑:用户创建的核心逻辑和后续邮件发送、日志记录等操作分离,控制器只负责处理请求、创建用户,后续动作交给事件监听者处理
- 扩展性更强:后续需要给管理员创建的用户添加其他操作时,只需新增监听者,无需修改原有创建用户的代码
- 代码更清晰:每个组件只负责单一职责,便于维护和单元测试
具体实现步骤
1. 创建用户创建完成的事件
// app/Events/UserCreatedByAdmin.php namespace App\Events; use App\Models\User; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class UserCreatedByAdmin { use Dispatchable, InteractsWithSockets, SerializesModels; public $user; public $plainPassword; /** * 创建事件实例 * * @param \App\Models\User $user * @param string $plainPassword * @return void */ public function __construct(User $user, string $plainPassword) { $this->user = $user; $this->plainPassword = $plainPassword; } }
2. 创建邮件发送监听者
// app/Listeners/SendAdminCreatedUserCredentials.php namespace App\Listeners; use App\Events\UserCreatedByAdmin; use App\Mail\SendUserMail; use Illuminate\Support\Facades\Mail; class SendAdminCreatedUserCredentials { /** * 处理事件 * * @param \App\Events\UserCreatedByAdmin $event * @return void */ public function handle(UserCreatedByAdmin $event) { $userCreds = [ 'email' => $event->user->email, 'password' => $event->plainPassword ]; Mail::to($event->user->email)->send(new SendUserMail($userCreds)); } }
3. 在EventServiceProvider中注册事件与监听者
// app/Providers/EventServiceProvider.php namespace App\Providers; use App\Events\UserCreatedByAdmin; use App\Listeners\SendAdminCreatedUserCredentials; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider { protected $listen = [ UserCreatedByAdmin::class => [ SendAdminCreatedUserCredentials::class, ], ]; /** * 注册任何应用事件 * * @return void */ public function boot() { parent::boot(); } }
4. 修改控制器代码,创建用户并触发事件
public function createUser(){ $this->validate(); $randomPass = Str::random(8); $userData = [ 'name' => $this->name, 'email' => $this->email, 'password' => Hash::make($randomPass), 'email_verified_at' => Carbon::now(), 'user_type' => (int)$this->selectedUser, 'assigned_to' => (int)$this->selectedAssigned, 'doctor_type' => $this->selectTypeDoc, ]; try{ $user = User::firstOrCreate($userData); // 仅当用户是管理员创建的类型时触发事件 if(in_array($user->user_type, [1,2,3])){ event(new \App\Events\UserCreatedByAdmin($user, $randomPass)); } $this->reset(); $this->emitUp('refreshParent'); $this->dispatchBrowserEvent('swal-insert'); }catch(\Exception $e){ $this->dispatchBrowserEvent('createUser-error'); } $this->closeModal(); }
额外优化点
- 将用户类型定义为模型常量:避免硬编码数字,提升可读性和维护性
// 在User模型中添加常量与判断方法 class User extends Authenticatable implements MustVerifyEmail { const USER_TYPE_CLIENT = 0; const USER_TYPE_COURIER = 1; const USER_TYPE_BUSINESS = 2; const USER_TYPE_MANAGER = 3; const USER_TYPE_ADMIN = 4; // ... 其他代码 public function isCreatedByAdmin() { return in_array($this->user_type, [self::USER_TYPE_COURIER, self::USER_TYPE_BUSINESS, self::USER_TYPE_MANAGER]); } }
控制器中的判断可改为:
if($user->isCreatedByAdmin()){ event(new \App\Events\UserCreatedByAdmin($user, $randomPass)); }
- 使用队列发送邮件:避免邮件发送阻塞请求响应,提升用户体验,只需修改监听者中的邮件发送代码:
Mail::to($event->user->email)->queue(new SendUserMail($userCreds));
注意需确保队列服务已配置并启动。
总结
事件驱动方式更符合Laravel的设计理念,代码解耦性强、扩展性好,后续维护迭代更便捷。如果是极简单的单一场景,控制器实现也能工作,但从长期维护角度,事件系统是更优选择。
内容的提问来源于stack exchange,提问作者cdBreak
相关产品推荐
相关产品推荐

