容器和依赖注入

依赖注入

  1. 手册对依赖注入比较严谨的说明,具体如下:

依赖注入其实本质上是指对类的依赖通过构造器完成自动注入,例如在控制器架构方法和操作方法中一旦对参数进行对象类型约束则会自动触发依赖注入,由于访问控制器的参数都来自于URL请求,普通变量就是通过参数绑定自动获取,对象变量则是通过依赖注入生成.

  1. 先看一个小例子,了解一下依赖注入的写法,创建一个模型;
namespace app\model;
use think\Model;
class One extends Model
{
    public $username = 'Mr.Lee';
}
  1. 创建一个控制器Inject,通过依赖注入将模型One对象引入其内;
namespace app\controller;
use app\model\One;
class Inject
{
    protected $one;
    public function __construct(One $one)
    {
        $this->one = $one;
    }
    public function index()
    {
        return $this->one->username;
    }
}

QQ截图20200803144946.png

  1. 依赖注入:即允许通过类的方法传递对象的能力,并且限制了对象的类型(约束);
  2. 而传递的对象背后的那个类被自动绑定并且实例化了,这就是依赖注入;

容器

  1. 依赖注入的类统一由容器管理的,大多数情况下是自动绑定和自动实例化的;
  2. 如果想手动来完成绑定和实例化,可以使用bind()app()助手函数来实现;
class Inject
{
    public function index()
    {
        bind('one', 'app\model\One');
        return app('one')->username;
    }
}

QQ截图20200803145630.png

  1. bind('one','...')绑定类库标识,这个标识具有唯一性,以便快速调用;
  2. app('one')快速调用,并自动实例化对象,标识严格保持一致包括大小写;
  3. 自动实例化对象的方式,是采用单例模式实现,如果想重新实例化一个对象,则:
//每次调用总是会重新实例化
bind('one', 'app\model\One');
$one = app('one',[], true);
return $one->username;

QQ截图20200803152021.png

  1. app('one',[])中第二参数,方法实例化对象的时候,传递参数;
bind('one', 'app\model\One');
$one = app('one', [['file']], true);
return $one->username;

QQ截图20200803152641.png

  1. 当然,你也可以直接通过app()绑定一个类到容器中并自动实例化;
return app('app\model\One')->username;

QQ截图20200803152718.png

  1. 使用bind([])可以实现批量绑定,只不过系统有专门提供批量绑定的文件;
bind(['one' => 'app\model\One','user' => 'app\model\User']);
return app('one')->username;

bind(['one' => \app\model\One::class,'user' => \app\model\User::class]);
return app('one')->username;

QQ截图20200803153005.png

  1. ::class模式,不需要单引号,而且需要在最前面加上\\,之前的加不加都行;
  2. 系统提供了provider.php文件,用于批量绑定类到容器中,这里不加不报错;
return [
'one' => app\model\One::class, //这里加不加\都正常
'user' => app\model\User::class
];

QQ截图20200803153350.png

  1. 系统内置了很多常用的类库,以便开发者快速调用,具体如下:

QQ截图20200803153457.png

  1. 也就是说,实现同一个效果可以由容器的bind()app()实现,也可以使用依赖注入实现,还有Facade实现,以及助手函数实现;

门面Facade

创建静态调用

  1. Facade即门面设计模式,为容器的类提供了一种静态的调用方式;
  2. 在之前的很多课程中,我们大量的引入Facade类库,并且通过静态调用;
  3. 比如请求Request::?,路由Route::?,数据库Db::?等等,均来自Facade;
  4. 下面我们手工来创建一个自己的静态调用类库,来了解一下流程;
  5. 首先,在应用目录下创建common公共类库文件夹,并创建Test.php;
namespace app\common;
class Test
{
    public function hello($name)
    {
        return 'Hello, '.$name;
    }
}
  1. 再在同一个目录下创建facade文件夹,并创建Test.php,用于生成静态调用;
namespace app\facade;
use think\Facade;
class Test extends Facade
{
    protected static function getFacadeClass()
    {
        return 'app\common\Test';
    }
}
  1. 然后在控制器端,就可以和之前系统提供的静态调用一样调用了;
return Test::hello('Mr.Lee!');

QQ截图20200803155414.png

核心类库

  • 以下是系统提供的常用 Facade 核心类库表;

QQ截图20200803155501.png

请求对象和信息

请求对象

  1. 使用构造方法注入请求,如下:
namespace app\controller;
use think\Request;
class Rely
{
    protected $request;
    public function __construct(Request $request)
    {
        $this->request = $request;
    }
    public function index()
    {
        return $this->request->param('username');
    }
}

QQ截图20200803163824.png

  1. Request请求对象拥有一个param方法,传入参数username,可以得到相应的值;
  2. 也可以在普通方法下直接使用,如下:
use think\Request;
class Rely
{
    public function index(Request $request)
    {
        return $request->param('username');
    }
}

QQ截图20200803163933.png

  1. 使用Facade方式应用于没有进行依赖注入时使用Request对象的场合;
use think\facade\Request;
class Rely
{
    public function index()
    {
        return Request::param('username');
    }
}

QQ截图20200803164003.png

  1. 使用助手函数request()方法也可以应用在没有依赖注入的场合;
class Rely
{
    public function index()
    {
        return request()->param('username');
    }
}

QQ截图20200803164028.png

请求信息

  1. Request对象除了param方法外,还有一些请求的固定信息,如表:

QQ截图20200803164117.png

  1. 上表的调用方法,直接调用即可,无须传入值,只有极个别如果传入true获取完整URL的功能;
// 获取完整 URL 地址 不带域名
Request::url();
// 获取完整 URL 地址 包含域名
Request::url(true);
// 获取当前 URL(不含 QUERY_STRING) 不带域名
Request::baseFile();
// 获取当前 URL(不含 QUERY_STRING) 包含域名
Request::baseFile(true);
// 获取 URL 访问根地址 不带域名
Request::root();
// 获取 URL 访问根地址 包含域名
Request::root(true);

QQ截图20200803164440.png

  1. 还可以获取当前控制器和操作方法的名称:::controller()::action();
return Request::controller().'|'.Request::action();

QQ截图20200803164511.png

请求变量

请求变量

  1. Request对象支持全局变量的检测,获取和安全过滤,支持$_GET,$_POST等;
  2. 为了方便演示,这里一律使用Facade的静态调用模式;
  3. 使用has()方法,可以检测全局变量是否已经设置:
Request::has('id', 'get');
Request::has('username', 'post');

QQ截图20200803165228.png

  1. Request支持的所有变量类型方法,如下表:

QQ截图20200803165300.png

  1. param()变量方法是自动识别GET,POST等的当前请求,推荐使用;
//获取请求为 name 的值,过滤
Request::param('name');
//获取所有请求的变量,以数组形式,过滤
Request::param();
//获取所有请求的变量(原始变量),不包含上传变量,不过滤
Request::param(false);
//获取部分变量
Request::param(['name','age']);

QQ截图20200803165825.png

  1. 默认情况下,并没有配置字符过滤器,可在app\Request.php配置;
protected $filter = ['htmlspecialchars'];

QQ截图20200803170033.png

  1. 如果没有设置字符过滤器,或者局部用别的字符过滤器,可以通过第三参数;
Request::param('name', '', 'htmlspecialchars');
Request::param('name', '', 'strip_tags,strtoupper');

QQ截图20200803170305.png

  1. 如果设置了全局字符过滤器,但又不想某个局部使用,可以只用 null 参数;
Request::param('name', '', null);

QQ截图20200803171530.png

  1. 如果获取不到值,支持请求的变量设置一个默认值;
Request::param('name', '默认值');

QQ截图20200803171602.png

  1. 如果采用的是路由URL,也可以获取到变量,但param::get()不支持路由变量;
Request::param('id');
Request::route('id');
Request::get('id'); //路由参数,get 获取不到

QQ截图20200803171742.png

  1. 使用 only()方法,可以获取指定的变量,也可以设置默认值;
Request::only(['id','name']);
Request::only(['id'=>1,'name'=>'默认值']);

QQ截图20200803171855.png

  1. 使用 only()方法,默认是 param 变量,可以在第二参数设置GET,POST等;
Request::only(['id','name'], 'post');

QQ截图20200803171953.png

  1. 相反的 except()方法,就是排除指定的变量;
Request::except(['id','name']);
Request::except(['id'=>1,'name'=>'默认值']);
Request::except(['id','name'], 'post');

QQ截图20200803172156.png

  1. 使用变量修饰符,可以将参数强制转换成指定的类型;
  2. /s(字符串),/d(整型),/b(布尔),/a(数组),/f(浮点);
Request::param('id/d');

QQ截图20200803172257.png

助手函数

  1. 为了简化操作,Request 对象提供了助手函数;
input('?get.id'); //判断 get 下的 id 是否存在
input('?post.name'); //判断 post 下的 name 是否存在
input('param.name'); //获取 param 下的 name 值
input('param.name', '默认值'); //默认值
input('param.name', '', 'htmlspecialchars'); //过滤器
input('param.id/d'); //设置强制转换

QQ截图20200803172351.png

请求类型和HTTP头信息

请求类型

  1. 有时,我们需要判断Request的请求类型,比如GET,POST等等;
  2. 可以使用method()方法来判断当前的请求类型,当然,还有很多专用的请求判断;
  3. 使用普通表单提交,通过method()方法获取类型;
$html = <<<EOF
    <form action="http://tp6/datatest" method="post">
        <input type="text" name="name" value="Lee">
        <input type="submit" value="提交">
        </form>
EOF;
echo $html;
return Request::method();

QQ截图20200804141805.png

  1. 在表单提交时,我们也可以设置请求类型伪装,设置隐藏字段_method;
  2. 而在判断请求,使用method(true)可以获取原始请求,否则获取伪装请求;
<input type="hidden" name="_method" value="PUT">
Request::method(true);
  • 伪装请求

QQ截图20200804141840.png

  • 原始请求

QQ截图20200804141909.png

  1. 如果想更改请求伪装变量类型的名称,可以在app/Request.php中更改;
protected $varMethod = '_m';
  1. AJAX/PJAX伪装,使用?_ajax=1?_pjax=1,并使用isAjax()isPjax();
$html = <<<EOF
    <form action="http://tp6/datatest?_ajax=1" method="post">
        <input type="text" name="name" value="Lee">
        <input type="hidden" name="_method" value="PUT">
        <input type="submit" value="提交">
        </form>
EOF;
echo $html;
dump(Request::isAjax());

QQ截图20200804142221.png

  1. 这里需要用isAjax()isPjax()来判断,用method无法判断是否为a(p)jax;
  2. app.php也可以更改ajaxpjax的名称;
protected $varAjax = '_a';
protected $varPjax = '_p';

HTTP头信息

  1. 使用header()方法可以输出HTTP头信息,返回是数组类型,也可单信息获取;
Request::header();
Request::header('host');

QQ截图20200804142312.png

伪静态,参数绑定,请求缓存

伪静态

  1. 可以通过route.php修改伪静态的后缀,比如修改成shtml,xml等;
'url_html_suffix' => 'html', 
  1. 如果地址栏用后缀访问成功后,可以使用Request::ext()方法得到当前伪静态;
return Request::ext();
  1. 配置文件伪静态后缀,可以支持多个,用竖线隔开;
'url_html_suffix' => 'shtml|xml|pdf', 
  1. 直接将伪静态配置文件设置为false,则关闭伪静态功能;
'url_html_suffix' => false, 

参数绑定

  1. 参数绑定功能:即URL地址栏的数据传参,我们一直在使用的功能;
public function get($id)
{
    return 'get:'.$id;
}
  1. 操作方法URL:/get,而带上id参数后,则为:/get/id/5;
  2. 如果缺少了/5或者缺少了/id/5,则都会报错方法参数错误:id;
  3. 那么解决方案,就是给$id = 0一个默认值,防止URL参数错误;
  4. 如果设置了两个参数,那么参数传递的执行顺序可以设置,比如;
public function get($id, $name)
{
    return 'get:'.$id.','.$name;
}
  1. 不管是:/id/5/name/lee,还是:/name/lee/id/5,都不会产生错误;

请求缓存

  1. 请求缓存仅对GET请求有效,可以在全局和局部设置缓存;
  2. 如果要设置全局请求缓存,在中间件文件middleware.php中设置;
'think\middleware\CheckRequestCache',
  1. 然后在route.php中设置缓存的声明周期即可;
'request_cache_expire' => 3600, 
  1. 当第二次访问时,会自动获取请求缓存的数据响应输出,并发送304状态码;
  2. 如果要对路由设置一条缓存,直接使用cache(3600)方法;
Route::get('get/:id', 'Rely/get')->cache(3600);

响应输出和重定向

响应操作

  1. 响应输出,有好几种:包括return,json()view()等等;
  2. 默认输出方式是以html格式输出,如果你发起json请求,则输出json;
  3. 而背后是response对象,可以用response()输出达到相同的效果;
return response($data);

QQ截图20200804145140.png

  1. 使用response()方法可以设置第二参数,状态码,或调用code()方法;
return response($data, 201);
return response($data)->code(202);

QQ截图20200804145319.png

  1. 使用json(),view()方法和response()返回的数据类型不同,效果一样;
return json($data, 201);
return json($data)->code(202);

QQ截图20200804145411.png

  1. 不但可以设置状态码,还可以设置header()头文件信息;
return json($data)->code(202)->header(['Cache-control' => 'no-cache,must-revalidate']);

QQ截图20200804145449.png

重定向

  1. 使用redirect()方法可以实现页面重定向,需要return执行;
return redirect('http://www.baidu.com');
  1. 站内重定向,直接输入路由地址或相对地址即可,第二参数状态码;
return redirect('ds/5');
return redirect('/address/details/id/5', 201);
  1. 使用url自动生成跳转地址,普通地址或路由地址;
return redirect(url('address/index'));
  1. 附加session信息,并跳转重定向;
return redirect(url('address/index'))->with('name', 'Mr.Lee');
  1. 重定向还提供了,记住上一次的url,和跳转到上一次url的功能;
// 记住当前url后跳转
return redirect()->remember();
// 跳转到上次记住的url
return redirect()->restore();