Laravel 的依赖注入是理解整个框架的核心, 有点时间, 写几个常见的错误和设计技巧:
1. 依赖注入的类, 必须是完整可用的功能类(所有的属性都必须初始化)
这句话怎么理解呢? 是这样的, 比如我们写一个类,
- class Person{
- protected $name;
- public function setName($name){
- $this->name=$name;
- }
- }
这样的一个类, 在其他应用上没有问题, 但是不是一个可用的依赖注入类, 为什么这么说呢? 因为 $name 这个属性在对象生成的时候没有赋值, 而必须用一个 setter 赋值, Laravel 框架在解析这样的类依赖关系的时候, 不知道应用 $name 的时候, 发现空值会报错, 这个类应该改写成如下的类:
- class Person{
- protected $name;
- public function __construct($name){
- $this->name=$name;
- }
- }
这样在对象初始化的时候, 就对属性进行赋值, 这样的类才能进行依赖注入.
所谓完整功能的类, 就是一个类在生成对象的时候, 必须对所有的属性都进行初始化, 这样的类才可以依赖注入.
2. 被依赖注入的类 (也可以称为服务) 中的方法尽可能在最后抛出一个异常
例如, 我们常常会用 Repository 设计模式来处理与数据库的交互动作, 那么, 当数据库操作的方法很多的时候, 在哪个方法出错了, debug 就是一件非常痛苦的事情, 一个良好的做法是, 在 Repository 中间层中正常的操作返回数据, 而如果出错了, 就抛出一个异常, 这样对程序的调试非常有帮助.
例如, 我们在 Repository 设计模式中有一个修改密码的方法, 一般的写法如下:
- public function updatePassword(User $user, $input) : User
- {
- $user->update(['password' => $input['password']]);
- return $user;
- }
上面的方法不利于调试, 我们可以稍作修改:
- public function updatePassword(User $user, $input) : User
- {
- if ($user->update(['password' => $input['password']])) {
- return $user;
- }
- throw new GeneralException(__('exceptions.update_password_error'));
- }
这个异常类可以是自定义的异常对象或标准异常对象, 但是在异常中标注错误方法名, 这样的操作会有目的性. 另外, 如果不是针对公用 API 开发, 而仅仅是做前后端分离的 API 开发, 可以不用去 catch 这些异常的处理, 只要有了这个异常标注后, 交给框架的异常处理机制就好了.
3.Postman 调试 API 时, PUT/PATCH 方法不能携带文件或 body 参数
在使用资源控制器的开发 API 程序的时候, 用 put 方法或 patch 方法修改资源, 用 Postman 调试的话, 无法在参数中携带文件, 这在后端进行调试的时候挺棘手, 一个简单的处理方法, 是在资源路由前面加一个 Post 路由来修改资源, 并将 post 路由指向控制器的 update 方法, 例如这样:
- Route::post('roles/{id}','Api\RoleController@update');
- Route::resource('roles','Api\RoleController',['except'=>['create','edit']]);
这样访问的时候, 在 Postman 中直接用 post 替代 put 就可以正常携带 body 文件了, 这样, 路由可以接收前端用 axios 发的 Ajax 请求, 同时没有开发前端的时候可以用 post 暂时替代(测试后 post 方法也可以直接删除).
4.Repository 设计技巧
现在的 Laravel 设计 web 后台的主要架构, 无过于以下几种:
1. 模型写数据库的关联关系, 控制器写获取数据和展示视图(Web 设计)
2. 模型写数据库关联关系和返回 JSON 数据的方法, 不需要控制器(API 设计)
3. 模型写数据库关联关系, 控制器写获取数据和返回 JSON 数据逻辑(API 设计)
4. 模型写关联关系, 独立抽象层 (Repository) 写数据库操作方法, 控制器写返回数据 / 视图逻辑(Web 和 API)
前三种的方法比较常见, 也是目前培训机构的主要方法, 第四种架构比较少见.
主要原因如下:
1. 前三种设计的主要特点是, 代码量比较少, 不需要设计操作接口, 不需要标准化, 主要针对小型应用, 逻辑关系简单的 Web 应用, 同时灵活性比较低, 代码维护困难. 因为培训机构讲课项目的业务逻辑一般不会很复杂, 所以对这个设计模式应用的比较少.
2. 第四种方法比较不常见, 主要因为这种方法适用于多人协作开发的大型项目, 以下, 我专门针对这种设计模式说一下操作的思路, 您可以自己进行一些尝试, 慢慢就能体会到, 这种模式下, 多写几个接口带来的代码量和它所带来的好处相比, 根本就微不足道.
Repository 设计模式的一般架构:
首先, 您得新建一个 Repository 文件夹, 专门用来放置这个中间层的代码, 位置放哪儿都无所谓, 但是一般建议放在 src 下, 并且我本人不是很推崇自己更改 Laravel 整个项目的核心目录结构, 因为这样的目录逻辑便于大家遵循同一个目录标准, 这样对交流很有好处.
其次, 您可以在 Repository 文件夹下建立一个用于放置接口的文件夹, 比如 Repository/Contract 文件夹, 这个文件夹用来存放中间层的接口文件, 当然, 如果您的数据库操作不是很多, 同时没有多人协作的需求, 就是您一个人单枪匹马的写后台逻辑, 那也可以不用写 Contract, 直接写 Repository 服务就好了, 我们还是按照一般的流程来讲吧.
再次, 在 Contract 目录下写一系列的接口设计文件, 这是整个 Repository 抽象层操作的纲要, 如果您刚接触面向对象一般是写不出来的, 这个工作一般是比较有经验的人来写这个抽象接口, 我拿其中一个接口来做例子:
- interface BaseRepository{
- public function query()
- public function select(array $columns = ['*'])
- public function make(array $attributes = []);
- public function getModelById($modelClass,$id);
- }
这个接口就定义了四个方法, 然后, 可以在 Repository 目录下新建一个目录, 来存放实现这个接口的类(服务):
- class EloquentBaseRepository implements BaseRepository
- {
- protected $model;
- protected $filter=[];
- public function __construct(Model $model)
- {
- $this->model=$model;
- }
- public function query()
- {
- return $this->model->newQuery();
- }
- //...... 省略一堆实现
- }
这样, 就写好了一个接口和一个实现类(服务), 同时接口可以存在继承关系, 不过为了实现类的逻辑和接口一致, 一定要讲实现类目录接口和类继承结构与接口的层级关系保持一致! 也就是说, 接口怎么继承, 实现类就怎么继承, 接口摆哪儿, 实现类就摆哪儿.
然后, 可以把这个接口和实现类 (服务) 的关系注册到服务提供者中, 这里可以直接注册到 App 服务提供者, 也可以创建一个新的服务提供者, 再把服务提供者注册到 App 的 provider 中, 这样就可以在控制器的构造函数中依赖注入接口, 而后通过依赖注入对象直接调用服务中的方法.
由此可见, 使用这种模式设计, 利于讲数据库操作规范化, 同时如果后续要修改, 不用修改控制器中的代码, 只要再写一个服务, 然后注册服务到提供者中绑定, 就可以随时抽换实现的方法, 这也是控制反转带来的好处.
同时, 值得一提的是, Repository 设计模式, 一般会将一个操作标准对应一个模型操作, 同时对应一个控制器操作, 也就是说, 从 Contract 接口 ->EloquentRepository 实现 (实现)-> 控制器 ->模型, 基本保持文件一致, 就是一个接口对应一个实现类, 对应一个控制器类, 对应一个模型, 这样写的逻辑思路会很清晰, 但是从另外一个角度讲, 这种设计模式本身就是针对团队协作的标准化而量身定制的, 如果您是一个人写一个小项目, 就没必要这样设计了, 因为在设计的时候, 改一行代码要来回切换四个文件, 在开发效率或过度开发上来讲, 只要追踪的方法在您的可控范围之内, 就完全没必要这样把一个完整操作拆成四步操作来做.
晚了, 先写这么多吧, 有时间再慢慢写..............
来源: http://www.jianshu.com/p/84267e4fb6e7