Laravel 框架 重难点

Facade

以数据库访问为实例,如:

1
$users = DB::connection('foo')->select(...);

Facade 实现原理
IOC容器是 Laravel 框架的最最重要的部分。它提供了两个功能,IOC和容器。
IOC(Inversion of Control),也叫控制反转。说白了,就是控制对象的生成,使开发者不用关心对象的如何生成,只需要关心它的使用即可。
而通过IOC机制生成的对象实例需要一个存放的位置,以便之后继续使用,便是它的容器功能。
关于IOC容器,需要记住两点即可:
1、 根据配置生成对象实例;
2、 保存对象实例,方便随时取用;

看下面简化后的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?php
namespace facades;

abstract class Facade
{
protected static $app;

/**
* Set the application instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public static function setFacadeApplication($app)
{
static::$app = $app;
}

/**
* Get the registered name of the component.
*
* @return string
*
* @throws \RuntimeException
*/
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}

/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}

/**
* Resolve the facade root instance from the container.
*
* @param string|object $name
* @return mixed
*/
protected static function resolveFacadeInstance($name)
{
return static::$app->instances[$name];
}

public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();

if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}

switch (count($args)) {
case 0:
return $instance->$method();
case 1:
return $instance->$method($args[0]);
case 2:
return $instance->$method($args[0], $args[1]);
case 3:
return $instance->$method($args[0], $args[1], $args[2]);
case 4:
return $instance->$method($args[0], $args[1], $args[2], $args[3]);
default:
return call_user_func_array([$instance, $method], $args);
}
}
}

其中:
1、 $app中存放的就是一个IOC容器实例,它是在框架初始化时,通过 setFacadeApplication() 这个静态方法设置的
2、 它实现了__callStatic 魔术方法
3、 getFacadeAccessor() 方法需要子类去继承,返回一个string的标识,通过这个标识,IOC容器便能返回它所绑定类(框架初始化或其它时刻绑定)的对象
4、通过 $instance 调用具体的方法

我们尝试创建自定义的 Facade,
第一步,实现自己类的具体业务逻辑

1
2
3
4
5
6
7
8
<?php
class Test1
{
public function hello()
{
print("hello world");
}
}

第二步,实现自定义类的 Facede

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

namespace facades;

/**
* Class Test1
* @package facades
*
* @method static setOverRecommendInfo [设置播放完毕时的回调函数]
* @method static setHandlerPlayer [明确指定下一首时的执行类]
*/
class Test1Facade extends Facade
{
protected static function getFacadeAccessor()
{
return 'test1';
}
}

第三步,使用自定义的 Facade

1
2
3
use facades\Test1Facade;

Test1Facade::hello(); // 这是 Facade 调用

整个过程如下:
1、 facades\Test1Facade 调用静态方法 hello() 时,由于没有定义此方法,会调用callStatic;
2、 在
callStatic 中,首先是获取对应的实例,即 return static::$app->instances[$name];。这其中的 $name,即为 facades\Test1 里的 test1
3、 $app, 即为 IOC 容器,类的实例化过程,就交由它来处理。

路由

1、 路由组

1
2
3
4
5
6
7
8
9
10
Route::group(
[
'prefix'=>'admin',
'namespace'=>'\App\Admin\Controllers',
'middleware'=>'auth:web'
],
function(){
Route::get('user/{id}/{name}', 'UserController@show')->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
}
);

将多个Route合并到一个组中,同个组中的Route共享路由组的属性,如:前缀,中间件和命名空间等
其中:
prefix 前缀:会追加到路由组中每个路由的URI前缀相当于 Route::get(‘/admin/user/{id}/{name}’, ‘UserController@show’);

namespace namespace:省去了组里每个路由都需要加上命名空间,修改时也方便。该路会访问到\App\Admin\Controllers\UserController@show’。有一点需要注意,使用时需要把RouteServiceProvider里面的$namespace置为空,否则该值会追加在你的路由前面。

middleware 中间件:是从路由到控制器中间的一个层,可作数据校验和登陆验证等,web.php中默认有csrf等校验,可通过app/Http/Kernel.php中的$middlewareGroups查看。

参数绑定:参数可以用正则来约束,如果你希望路由参数在全局范围内都遵循一个确定的正则表达式约束,则可以在 RouteServiceProvider 的 boot 方法里定义Route::pattern(‘id’, ‘[0-9]+’);

2、 资源路由

1
Route::resource('user', 'UserController', ['only'=>['index','create','store']]);

用artisan生成控制器时就可以直接生成对应的方法

1
php artisan make:controller UserController --resource

动作 URI 操作 路由名称 含义
GET /users index users.index 获取列表数据
GET /users/create create users.create 创建(显示表单)
POST /users store users.store 保存创建建数据
GET /users/{id} show users.show 获取单个id资源数据
GET /users/{id}/edit edit users.edit 编辑(显示表单)
PUT/PATCH /users/{id} update users.update 保存资源修改数据
DELETE /users/{id} destroy users.destroy 提交删除资源

注意: 由于HTML的form中不支持PUT、TATCH、DELETE提交,所以框架默认将”_method”名字的值作为请求方法,故表单提交时要注意是否需要加入下面元素

1
<input type="hidden" name="_method" value="PUT">