thinkphp-容器和依赖注入、门面Facade
容器和依赖注入
依赖注入
依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作方法中一旦对参数进行对象类型约束则会自动触发依赖注入,由于访问控制器的参数都来自于URL请求,普通变量就是通过参数绑定自动获取,对象变量则是通过依赖注入生成。
通过一个例子来辅助理解
首先创建一个model/One.php这个模型
namespace app\model;
use think\Model;
class One extends Model
{
public $username = 'Mr 5';
}
然后再在controller/Inject.php中,调用模型中的数据
namespace app\controller;
use app\model\One;
class Inject
{
protected $one;
// One就是对应model对象
public function __construct(One $one)
{
// 通过依赖注入 这里的$one相当于One这个类
$this->one = $one;
}
public function index()
{
return $this->one->username;
}
}
依赖注入:即允许通过类的方法传递对象的能力,并且限制了对象的类型(约束);例子中就是One,传递的对象背后的那个类被自动绑定并且实例化了,这就是依赖注入
容器
依赖注入的类统一由容器管理的,大多数情况下是自动绑定和自动实例化的,如果想手动来完成绑定和实例化,可以使用bind()和app()助手函数来实现。
在controller/Inject.php这个控制器中,创建新方法实现手动实例化
public function bind()
{
// 手动绑定一个对象,第一个参数随便起名, 第二个参数通过命名空间的路径
bind('one', 'app\model\One');
// 通过app方法进行实例化
return app('one')->username;
}
bind('one','...')绑定类库标识,这个标识具有唯一性,以便快速调用,app('one')快速调用,并自动实例化对象,标识严格保持一致包括大小写;
自动实例化对象的方式,是采用单例模式实现。如果每次都是想重新实例化一个对象:
//每次调用总是会重新实例化
$one = app('one',[], true);
return $one->name;
加一个第三个参数,true,而第二个方法是构造方法的传参。app('one',[])中第二参数,方法实例化对象的时候,传递参数,比如,修改一下模型类:
namespace app\model;
use think\Model;
class One extends Model
{
public $username = 'Mr 5';
public function __construct(array $data = [])
{
parent::__construct($data);
print_r($data);
}
}
控制器中
public function bind()
{
bind('one', 'app\model\One');
// 通过app方法进行实例化,相当于 new One
$one = app('one', ['file'], true);
return $one->username;
}
这里运行就会报错:One::__construct() must be of the type array, string given
这是因为 app('one', ['file'],true); 第二个参数['file']相当于一个字符串!所以正确写法:
$one = app('one', [ ['file'] ], true);
当然,你也可以直接通过app()绑定一个类到容器中并自动实例化;
return app('app\model\One')->username;
使用bind([])可以实现批量绑定
bind([
'one' => 'app\model\One',
'user' => 'app\model\User'
]);
return app('one')->username;
或者使用::class 模式,注意,不需要单引号,而且需要在最前面加上\,如果不加\那么就会提示路径错误!;
bind([
'one' => \app\model\One::class
]);
return app('one')->username;
系统提供了app/provider.php 文件,用于批量绑定类到容器中,这样公共区域就可以用了,上面的方法,只能是在某个控制器或者控制器的方法中使用。 这里app路径前加不加\都正常
<?php
use app\ExceptionHandle;
use app\Request;
// 容器Provider定义文件
return [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
// 上面是系统默认的
// 下面是新添加的 注意 这里的app前面就不需要\了
'one' => app\model\One::class
];
然后在控制器中,直接使用即可
public function bind()
{
return app('one')->username;
}
系统内置了很多常用的类库,以便开发者快速调用,具体如下:
系统类库 | 容器绑定标识 |
---|---|
think\App | app |
think\Cache | cache |
think\Config | config |
think\Cookie | cookie |
thinkConsole | console |
think\Db | db |
think\Debug | debug |
think\Env | env |
think\Event | event |
think\Http | http |
think\Lang | lang |
thinkLog | log |
think\Middleware | middleware |
think\Request | request |
think\Response | response |
think\Filesystem | flesystem |
think\Route | route |
think\Session | session |
tink\Validate | validate |
thinklView | view |
实现同一个效果可以由容器的bind()和app()实现,也可以使用依赖注入实现,还有Facade实现,以及助手函数实现
门面Facade
创建静态调用
Facade,即门面设计模式,为容器的类提供了一种静态的调用方式;比如请求Request::?,路由Route::?,数据库Db::?等等,均来自Facade;
手工来创建一个自己的静态调用类库,来了解一下流程
首先,在应用目录下创建app/common 公共类库文件夹,并创建Test.php;
namespace app\common;
class Test
{
public function hello($name)
{
return "hello, {$name}";
}
}
再在同一个目录下创建app/facade 文件夹,并创建Test.php,用于生成静态调用
namespace app\facade;
use think\Facade;
class Test extends Facade
{
protected static function getFacadeClass()
{
return 'app\common\Test';
}
}
如此就绑定好了,然后在控制器中试验一下:
public function test()
{
return \app\facade\Test::hello('world');
}
如此即调用了app\facade目录下Test.php,然后将其实例化了。
即通过静态方法,实例化了类,然后实现静态调用类中的方法。
或者换一种写法
<?php
namespace app\controller;
use app\facade\Test;
class Url
{
public function test()
{
// return \app\facade\Test::hello('world');
return Test::hello('world');
}
}