Laravel

作为开发者,我们一直在尝试通过使用设计模式和尝试新的健壮型框架来寻找新的方式来编写设计良好且健壮的代码。在本篇文章中,我们将通过 Laravel 的 IoC 组件探索依赖注入设计模式,并了解它如何改进我们的设计。

依赖注入

依赖注入一词是由 Martin Fowler 提出的术语,它是将组件注入到应用程序中的一种行为。就像 Ward Cunningham 说的:

依赖注入是敏捷架构中关键元素。

让我们看一个例子:

class UserProvider{

protected $connection;

public function __construct(){

$this->connection = new Connection;

}

public function retrieveByCredentials( array $credentials ){

$user = $this->connection

->where( 'email', $credentials['email'])

->where( 'password', $credentials['password'])

->first();

return $user;

}

}

如果你要测试或者维护这个类,你必须访问数据库的实例来进行一些查询。为了避免必须这样做,你可以将此类与其他类进行 解耦 ,你有三个选项之一,可以将 Connection 类注入而不需要直接使用它。

将组件注入类时,可以使用以下三个选项之一:

构造方法注入

class UserProvider{

protected $connection;

public function __construct( Connection $con ){

$this->connection = $con;

}

...

Setter 方法注入

同样,我们也可以使用 Setter 方法注入依赖关系:

class UserProvider{

protected $connection;

public function __construct(){

...

}

public function setConnection( Connection $con ){

$this->connection = $con;

}

...

接口注入

interface ConnectionInjector{

public function injectConnection( Connection $con );

}

class UserProvider implements ConnectionInjector{

protected $connection;

public function __construct(){

...

}

public function injectConnection( Connection $con ){

$this->connection = $con;

}

}

当一个类实现了我们的接口时,我们定义了 injectConnection 方法来解决依赖关系。

优势

现在,当测试我们的类时,我们可以模拟依赖类并将其作为参数传递。每个类必须专注于一个特定的任务,而不应该关心解决它们的依赖性。这样,你将拥有一个更专注和可维护的应用程序。

如果你想了解更多关于 DI 的信息,Alejandro Gervassio 在 本系列 文章中对其进行了广泛而专业的介绍,所以一定要去读这些文章。那么,什么又是 IoC 呢?IoC (控制反转)不需要使用依赖注入,但它可以帮助你有效的管理依赖关系。

控制反转

Ioc 是一个简单的组件,可以更加方便地解析依赖项。你可以将对象形容为容器,并且每次解析类时,都会自动注入依赖项。

Laravel Ioc

当你请求一个对象时, Laravel Ioc 在解决依赖关系的方式上有些特殊:

image

我们使用一个简单的例子,将在本文中改进它。

SimpleAuth 类依赖于 FileSessionStorage ,所以我们的代码可能是这样的:

class FileSessionStorage{

public function __construct(){

session_start();

}

public function get( $key ){

return $_SESSION[$key];

}

public function set( $key, $value ){

$_SESSION[$key] = $value;

}

}

class SimpleAuth{

protected $session;

public function __construct(){

$this->session = new FileSessionStorage;

}

}

//创建一个 SimpleAuth

$auth = new SimpleAuth();

这是一种经典的方法,让我们从使用构造函数注入开始。

class SimpleAuth{

protected $session;

public function __construct( FileSessionStorage $session ){

$this->session = $session;

}

}

现在我们创建一个对象:

$auth = new SimpleAuth( new FileSessionStorage() );

现在我想使用 Laravel Ioc 来管理这一切。

因为 Application 类继承自 Container 类,所以你可以通过 App 门面来访问容器。

App::bind( 'FileSessionStorage', function(){

return new FileSessionStorage;

});

bind 方法第一个参数是要绑定到容器的唯一 ID ,第二个参数是一个回调函数每当执行 FileSessionStorage 类时执行,我们还可以传递一个表示类名的字符串,如下所示。

Note: 如果你查看 Laravel 包时,你将看到绑定有时会分组,比如( view, view.finder……)。

假设我们将会话存储转换为 Mysql 存储,我们的类应该类似于:

class MysqlSessionStorage{

public function __construct(){

//...

}

public function get($key){

// do something

}

public function set( $key, $value ){

// do something

}

}

现在我们已经更改了依赖项,我们还需要更改 SimpleAuth 构造函数,并将新对象绑定到容器中!

高级模块不应该依赖于低级模块,两者都应该依赖于抽象对象。

抽象不应该依赖于细节,细节应该取决于抽象。

Robert C. Martin

我们的 SimpleAuth 类不应该关心我们的存储是如何完成的,相反它更应该关注于消费的服务。

因此,我们可以抽象实现我们的存储:

interface SessionStorage{

public function get( $key );

public function set( $key, $value );

}

这样我们就可以实现并请求 SessionStorage 接口的实例:

class FileSessionStorage implements SessionStorage{

public function __construct(){

//...

}

public function get( $key ){

//...

}

public function set( $key, $value ){

//...

}

}

class MysqlSessionStorage implements SessionStorage{

public function __construct(){

//...

}

public function get( $key ){

//...

}

public function set( $key, $value ){

//...

}

}

class SimpleAuth{

protected $session;

public function __construct( SessionStorage $session ){

$this->session = $session;

}

}

如果我们使用 App::make('SimpleAuth') 通过容器解析 SimpleAuth

类,容器将会抛出 BindingResolutionException ,尝试从绑定解析类之后,返回到反射方法并解析所有依赖项。

Uncaught exception 'Illuminate\Container\BindingResolutionException' with message 'Target [SessionStorage] is not instantiable.'

容器正试图将接口实例化。我们可以为该接口做一个具体的绑定。

App:bind( 'SessionStorage', 'MysqlSessionStorage' );

现在每次我们尝试从容器解析该接口时,我们会得到一个 MysqlSessionStorage 实例。如果我们想要切换我们的存储服务,我们只要变更一下这个绑定。

Note: 如果你想要查看一个类是否已经在容器中被绑定,你可以使用 App::bound('ClassName') ,或者可以使用 App::bindIf('ClassName') 来注册一个还未被注册过的绑定。

Laravel Ioc 也提供 App::singleton('ClassName', 'resolver') 来处理单例的绑定。

你也可以使用 App::instance('ClassName', 'instance') 来创建单例的绑定。

如果容器不能解析依赖项就会抛出 ReflectionException ,但是我们可以使用 App::resolvingAny(Closure) 方法以回调函数的形式来解析任何指定的类型。

Note: 如果你为某个类型已经注册了一个解析方式 resolvingAny 方法仍然会被调用,但它会直接返回 bind 方法的返回值。

小贴士

这些绑定写在哪儿:

如果只是一个小型应用你可以写在一个全局的起始文件 global/start.php 中,但如果项目变得越来越庞大就有必要使用 Service Provider 。

测试:

当需要快速简易的测试可以考虑使用 php artisan tinker ,它十分强大,且能帮你提升你的 Laravel 测试流程。

Reflection API:

PHP 的 Reflection API 是非常强大的,如果你想要深入 Laravel Ioc 你需要熟悉 Reflection API ,可以先看下这个 教程 来获得更多的信息。

最后

和往常一样,学习或者了解某些东西最好的方法就是查看源代码。Laravel Ioc 仅仅只是一个文件,不会花费你太多时间来完成所有功能。你想了解更多关于 Laravel Ioc 或者 Ioc 的一般情况吗?那请告诉我们吧!

laravel mysql注入_详解 Laravel 中的依赖注入和 IoC相关推荐

  1. Java程序员从笨鸟到菜鸟之(一百)sql注入攻击详解(一)sql注入原理详解

    前段时间,在很多博客和微博中暴漏出了12306铁道部网站的一些漏洞,作为这么大的一个项目,要说有漏洞也不是没可能,但其漏洞确是一些菜鸟级程序员才会犯的错误.其实sql注入漏洞就是一个.作为一个菜鸟小程 ...

  2. python中heapq的库是什么_详解Python中heapq模块的用法

    详解Python中heapq模块的用法 来源:中文源码网    浏览: 次    日期:2018年9月2日 [下载文档:  详解Python中heapq模块的用法.txt ] (友情提示:右键点上行t ...

  3. python中get函数是什么意思_详解python中get函数的用法(附代码)_后端开发

    strncmp函数用法详解_后端开发 strncmp函数为字符串比较函数,其函数语法为"int strncmp ( const char * str1, const char * str2, ...

  4. redis watch使用场景_详解redis中的锁以及使用场景

    分布式锁 什么是分布式锁? 分布式锁是控制分布式系统之间同步访问共享资源的一种方式. 为什么要使用分布式锁? ​ 为了保证共享资源的数据一致性. 什么场景下使用分布式锁? ​ 数据重要且要保证一致性 ...

  5. python操作目录_详解python中的文件与目录操作

    详解python中的文件与目录操作 一 获得当前路径 1.代码1 >>>import os >>>print('Current directory is ',os. ...

  6. python3中unicode怎么写_详解python3中ascii与Unicode使用

    这篇文章主要为大家详解python3中ascii与Unicode使用的相关资料,需要的朋友可以参考下# Auther: Aaron Fan ''' ASCII:不支持中文,1个英文占1个字节 Unic ...

  7. python中list[1啥意思_详解Python中list[::-1]的几种用法

    本文主要介绍了Python中list[::-1]的几种用法,分享给大家,具体如下: s = "abcde" list的[]中有三个参数,用冒号分割 list[param1:para ...

  8. java 死锁 内存消耗_详解Java中synchronized关键字的死锁和内存占用问题

    先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并 ...

  9. pythonnamedtuple定义类型_详解Python中namedtuple的使用

    namedtuple是Python中存储数据类型,比较常见的数据类型还有有list和tuple数据类型.相比于list,tuple中的元素不可修改,在映射中可以当键使用. namedtuple: na ...

最新文章

  1. 二分法:search insert position 插入位置
  2. 借助队列解决Josephus问题
  3. matlab欧拉迭代,matlab机械臂正逆运动学求解问题,使用牛顿-欧拉迭代算法
  4. 计算机网络工程课程设计报告,计算机网络工程课程设计报告.doc
  5. python3 scrapy 教程_Scrapy 教程
  6. zz JNI学习(一)、JNI简介和HelloWorld示例
  7. volatile的作用
  8. 大数据Big Data
  9. 键盘接口和七段数码管的控制实验
  10. python 获取本地视频信息_python获取视频文件信息
  11. matplotlib 可视化细节,丰富的可选参数
  12. c语言创建线程代码,如何用C语言实现多线程
  13. Linux电源管理-Suspend/Resume流程
  14. 按键精灵 识别html,【院刊】-【201409期】抓取网页指定内容(资料),获取网页里的图片 _ 学院院刊 - 按键精灵论坛...
  15. 新萝卜家园 Ghost XP SP3 电脑城装机专用版 10.5
  16. intel DQ77KB 主板使用说明书
  17. 人工智能学习路线,文末赠书活动(深度学习Spark机器学习)
  18. Pointer Generator Network 和 PEGASUS
  19. Java Web学习day25------Vue和综合案例
  20. mdf文件和ldf文件ndf是什么,怎么用?如何给SQL server添加数据文件?分离和附加数据库的操作

热门文章

  1. 系统平台如何有效防止店铺降权
  2. JAVA设计模式 之九 装饰模式
  3. 使用阿里云开放搜索服务快速搭建资源搜索网站
  4. BMC 安装操作系统环境配置
  5. iMindMap使用教程
  6. AtCoder Beginner Contest 244 B题(超详讲解)
  7. javaWeb 使用线程池+队列解决订单并发问题
  8. RevitBIM数据库V6.0版插件-橄榄山+住建部BIM数据库:
  9. 《sizeof和strlen进阶笔试常考类型》
  10. 星星之火-59:ETSI与FCC在5GHz非授权频谱LAA上要求的差异