当前位置: 首页 > 后端技术 > PHP

Laravel深度学习6-应用架构:解耦事件处理器

时间:2023-03-29 17:49:47 PHP

声明:本文非博主原创,而是来自《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然不是原文翻译,可以保证90%%原创,由于是看懂翻译,难免有错误,欢迎指正。欢迎转载,转载请注明出处,谢谢!应用程序架构:解耦事件处理程序简介现在我们已经介绍了使用Laravel4构建健壮应用程序的许多功能,让我们深入了解更多细节。本章我们将讨论队列、事件等诸多事件处理器的解耦,以及类似于“类事件”结构的路由过滤。不要阻塞传输层大多数“事件处理程序”都被视为传输层组件。换句话说,队列处理、事件触发器或传入请求用于调用某些调用处理。像对待控制器一样对待这些事件处理程序,并避免在其中放置过多的业务逻辑。解耦事件处理程序在开始这个命题之前,让我们举一个例子。想象一个用于向用户发送SMS消息的队列处理程序。发送消息后,处理程序将发送的消息记录到历史记录中,以便我们知道哪些用户收到了它们。代码实现如下:classSendSMS{publicfunctionfire($job,$data){$twilio=newTwilio_SMS($apiKey);$twilio->sendTextMessage(array('to'=>$data['user']['phone_number'],'message'=>$data['message'],));$user=User::find($data['user']['id']);$user->messages()->create(array('to'=>$data['user']['phone_number'],'message'=>$data['message'],));$工作->删除();}}only在测试这段代码的时候,你可能会遇到一些问题。首先,测试难度大。Twilio_SMS类是在fire方法中实例化的,这意味着我们不能使用注入来模拟服务。其次,在handler中我们直接使用了Eloquent模型,这给测试带来了另一个问题,我们必须在方法中进行真正的数据库访问。最后,我们不能在队列之外进行SMS消息传递。我们的SMS消息发送逻辑完全集成在Laravel队列中。通过将逻辑提取到“服务”中,我们可以将应用程序中的短信发送逻辑与Laravel的队列服务解耦。这允许将消息发送到应用程序中的任何位置。当我们进行这种解耦的时候,这种重构也让我们的代码更容易测试。让我们修改代码:classUserextendsEloquent{/***向用户发送短信**@paramSmsCourierInterface$courier*@paramstring$message*@returnSmsMessage*/publicfunctionsendSmsMessage(SmsCourierInterface$courier,$message){$courier->sendMessage($this->phone_number,$message);返回$this->sms()->create(array('to'=>$this->phone_number,'message'=>$message,));在这个重构代码示例中,我们将消息发送逻辑提取到用户模型中。同时在这个方法中注入SmsCourierInterface接口实现逻辑,这样我们就可以更好的测试各方面的逻辑。重构短信发送逻辑后,重构队列:classSendSMS{publicfunction__construct(UserRepository$users,SmsCourierInterface$courier){$this->users=$users;$this->courier=$courier;}publicfunctionfire($job,$data){$user=$this->users->find($data['user']['id']);$user->sendSmsMessage($this->courier,$data['message']);$工作->删除();}}在重构的例子中,可以看到队列服务已经足够轻量了。它非常符合队列和我们的_真实_应用程序逻辑之间的传输层概念。竖起大拇指!这意味着我们可以轻松地从队列中发送消息。最后,让我们写一些测试代码:$relation=Mockery::mock('StdClass');$courier=Mockery::mock('SmsCourierInterface');$user->shouldReceive('sms')->once()->andReturn($relation);$relation->shouldReceive('create')->once()->with(array('to'=>'555-555-5555','message'=>'Test',));$courier->shouldReceive('sendMessage')->once()->with('555-555-5555','Test');/***行动...*/$user->sms_number='555-555-5555';$user->sendMessage($courier,'测试');}}其他事件处理程序我们可以在这种类型的“事件处理程序”上做很多改进。将它们限制在一个简单的“传输层”可以很好地将复杂的业务逻辑组织和解耦到框架之外。为了巩固这个想法,让我们采用路由过滤器并使用它来验证用户是我们的“高级”订阅者。Route::filter('premium',function(){returnAuth::user()&&Auth::user()->plan=='premium';});乍一看,好像没什么问题。这么小的代码有什么问题?然而,在这么小的过滤器中,也有可能意识到我们正在暴露应用程序的实现细节。请注意,我们检查过滤器中的计划属性。“级别”检测逻辑层紧密集成到路由和传输层中。如果我们将“高级”订阅者的包存储在数据库或用户模型中,我们的路由过滤器必须在这里再次修改!相应地,做一些小改动:Route::filter('premium',function(){returnAuth::user()&&Auth::user()->isPremium();});这么小的适配带来的效果是明显的,付出的代价也是小的。通过判断用户是否是模型中的高级订阅者,我们解耦了路由中的检测逻辑。我们的过滤器不再负责检测用户订阅级别。相反,它只是询问用户模型。现在,如果订阅级别的判断存储在数据库中,路由过滤器不需要改任何代码!谁是负责的人?我们再次讨论了_responsibility_的概念。记住,一个类的职责是什么,他参与的范围是明确的。尽量避免在事件处理程序中掺杂过多的业务逻辑。