http路由1
中间件5
控制器5
http请求7
http 响应8
视图9
service providers11
service container12
contracts13
facades14
请求的生命周期15
应用程序结构16
认证20
缓存24
集合26
artisan console26
扩展框架*27
laravel elixir*27
加密27
错误与日志28
事件28
文件系统 / 云存储30
哈希31
辅助方法31
本地化*31
邮件*31
扩展包开发*31
分页*31
队列*31
会话32
blade模板33
单元测试*35
数据验证35
数据库使用基础36
查询构造器38
结构生成器41
迁移和数据填充41
eloquent orm41
http路由
基本路由
定义针对不同http method的路由,如:
route::get('/', function(){
route::post('foo/bar', function(){
route::match(['get', 'post'], '/', function(){ # 多个方法
route::any('foo', function(){ # 所有方法
使用url方法生成url:$url = url('foo');
csrf保护
laravel会自动在每一位用户的session中放置随机的token。verifycsrftoken 中间件将保存在session中的请求和输入的token配对来验证token。除了寻找csrf token 作为「post」参数,中间件也检查x-xsrf-token请求头。
插入csrf token到表单:
_token value=>
在blade模板引擎使用:
_token value={{ csrf_token() }}>
添加到x-xsrf-token请求头中:
csrf-token c/span>csrf_token() }} />
$.ajaxsetup({
headers: {
'x-csrf-token': $('meta[name=csrf-token]').attr('content')
}
});
...
# 这样所有ajax请求中将会带上该头信息:
$.ajax({
url: /foo/bar,
})
方法欺骗
_method value=put>
>
路由参数
route::get('user/{id}', function($id){ # 基础参数
route::get('user/{name?}', function($name = null){ # 可选参数
route::get('user/{name?}', function($name = 'john'){ # 带默认值的参数
可以定义参数的全局模式,在routeserviceprovider的boot方法里定义模式:
$router->pattern('id', '[0-9]+');
之后,会作用在所有使用这个特定参数的路由上:
route::get('user/{id}', function($id)
if ($route->input('id') == 1){ # 在路由外部取得参数
也可以通过依赖注入来取得参数:
use illuminate\http\request;
route::get('user/{id}', function(request $request, $id){
if ($request->route('id')){
路由命名
route::get('user/profile', ['as' => 'profile', function(){
# 为控制器动作指定路由名称
route::get('user/profile', [
'as' => 'profile',
'uses' => 'usercontroller@showprofile'
]);
# 使用命名路由进行重定向
$url = route('profile');
$redirect = redirect()->route('profile');
# 返回当前路由请求的名称
$name = route::currentroutename();
路由群组
将共享属性作为一个数组当做route::group第一个参数:
# 共享中间件
route::group(['middleware' => ['foo', 'bar']], function()
{
route::get('/', function()
{
// has foo and bar middleware
});
route::get('user/profile', function()
{
// has foo and bar middleware
});
});
# 上例中foo和bar为中间件键名。自定义的中间件的键名与类名映射关系需要在kernel.php中添加。
# 共享命名空间
route::group(['namespace' => 'admin'], function()
{
// controllers within the app\http\controllers\admin namespace
route::group(['namespace' => 'user'], function()
{
// controllers within the app\http\controllers\admin\user namespace
});
});
子域名路由
route::group(['domain' => '{account}.myapp.com'], function()
{
route::get('user/{id}', function($account, $id)
{
//
});
});
路由前缀
route::group(['prefix' => 'admin'], function()
{
route::get('users', function()
{
// matches the /admin/users url
});
});
# 在路由前缀中定义参数
route::group(['prefix' => 'accounts/{account_id}'], function()
{
route::get('detail', function($account_id)
{
//
});
});
路由模型绑定
模型绑定提供方便的方式将模型实体注入到路由中:比起注入user id,你可以选择注入符合给定id的user类实体。在routeserviceprovider::boot方法定义模型绑定:
public function boot(router $router)
{
parent::boot($router);
$router->model('user', 'app\user');
}
然后定义一个有 {user} 参数的路由:
route::get('profile/{user}', function(app\user $user){
//
});
请求至profile/1将注入id为1的user实体。若实体不存在,则抛出404。可以传给第三个参数一个闭包,定义找不到时的行为。
抛出404错误
两种方法:
abort(404); # 本质上是抛出了一个带有特定状态码的symfony\component\httpkernel\exception\httpexception 。
或者:手工抛出httpexception
中间件
新建中间件
php artisan make:middleware oldmiddleware # 新建一个中间件
中间件的主要功能在handle()方法中实现:
class oldmiddleware {
public function handle($request, closure $next){
if (xxx){
return redirect('xx');
}
return $next($request);
}
}
分析其结构可以发现,基本上就是执行一个判断,然后依次进行重定向或者继续向前。
全局中间件
若是希望中间件被所有的 http 请求给执行,只要将中间件的类加入到app/http/kernel.php的$middleware 属性清单列表中。
指派中间件给路由
新建中间件后,在app/http/kernel.php的$routemiddleware中添加中间件键名与类名的映射关系,然后就可以在路由中使用这个键名来指派路由:
route::get('admin/profile', ['middleware' => 'auth', function(){
可终止中间件
可终止中间件需要继承自terminablemiddleware,并实现terminate()方法。其用处是在http响应已经被发送到用户端后再执行。可终止中间件需要添加到app/http/kernel.php的全局中间件清单中。
控制器
基础控制器
所有的控制器都应该扩展基础控制器类
use app\http\controllers\controller;
class usercontroller extends controller { # 继承controller
public function showprofile($id) # 动作
{
app\http\controllers\controller的定义如下:
'foocontroller@method', 'as' => 'name']);
# 指向控制器的url
$url = action('app\http\controllers\foocontroller@method');
或者:
url::setrootcontrollernamespace('app\http\controllers');
$url = action('foocontroller@method');
# 获取正在执行的控制器动作名称
$action = route::currentrouteaction();
控制器中间件
两种方式,一是在控制器路由中指定:
route::get('profile', [
'middleware' => 'auth',
'uses' => 'usercontroller@showprofile'
]);
另一种是直接在控制器构造器中指定:
class usercontroller extends controller {
public function __construct(){
$this->middleware('auth');
$this->middleware('log', ['only' => ['fooaction', 'baraction']]);
隐式控制器
隐式控制器实现定义单一路由来处理控制器中的每一项行为:
定义一个路由:
route::controller('users', 'usercontroller');
定义控制器类的实现:
class usercontroller extends basecontroller {
public function getindex(){ # 响应user
public function postprofile(){ # 响应post方式的user/profile
public function anylogin(){ # 响应所有方式的user/login
可以通过使用“-”来支持多个字词的控制器行为:public function getadminprofile() {} # 响应users/admin-profile,不是user/adminprofile。注意动作名中使用的驼峰命名方法
restful资源控制器
其实就是隐式控制器的一个具体应用。
路由缓存
如果应用中只使用了控制器路由,则可以利用路由缓存来提高性能。
php artisan route:cache
缓存路由文件将会被用来代替app/http/routes.php文件
http请求
取得请求
两种方式,一是通过request facade:
use request;
$name = request::input('name');
或者通过依赖注入:在控制器中的构造函数或方法对该类使用类型提示。当前请求的实例将会自动由服务容器注入:
input('name');
若同时还有使用路由参数输入的数据,只需将路由参数置于其他依赖之后:
public function update(request $request, $id){
取得输入数据
$name = request::input('name'); # 取得特定输入数据
$name = request::input('name', 'sally'); # 取得特定输入数据,若没有则取得默认值
if (request::has('name')){ # 确认是否有输入数据
$input = request::all(); # 取得所有输入数据
$input = request::only('username', 'password'); # 取得部分输入数据
$input = request::except('credit_card'); # 取得部分输入数据排除法
$input = request::input('products.0.name'); # 取得数组形式的数据
旧输入数据
request::flash(); # 将当前的输入数据存进 session中
request::flashonly('username', 'email'); # 将部分数据存成session
request::flashexcept('password'); # 将部分数据存成session,排除法
return redirect('form')->withinput(); # 重定向,同时将当期输入数据缓存到session
return redirect('form')->withinput(request::except('password')); # 重定向,同时将当期输入的部分数据缓存到session
$username = request::old('username'); # 取得前一次请求所保存的一次性session
{{ old('username') }} # 在blade模板中显示旧输入数据
cookies
laravel 所建立的 cookie 会加密并且加上认证记号。
$value = request::cookie('name'); # 取得cookie值
# 在响应中添加cookies
$response = new illuminate\http\response('hello world');
$response->withcookie(cookie('name', 'value', $minutes));
$response->withcookie(cookie()->forever('name', 'value')); # 添加永久有效的cookie
# 以队列方式添加cookie,即在实际发送响应之前设置cookie
cookie::queue('name', 'value');
return response('hello world');
上传文件
$file = request::file('photo'); # 取得上传文件
if (request::hasfile('photo')) # 确认文件是否有上传
if (request::file('photo')->isvalid()) # 确认上传的文件是否有效
request::file('photo')->move($destinationpath); # 移动上传的文件
request::file('photo')->move($destinationpath, $filename); # 移动上传的文件,并重命名
其他的请求信息
$uri = request::path(); # 取得请求 uri
if (request::ajax()) # 判断一个请求是否使用了 ajax
# 判断请求的方法
$method = request::method();
if (request::ismethod('post'))
if (request::is('admin/*')) # 确认请求路径是否符合特定格式
$url = request::url(); # 取得请求url
http 响应
基本响应
route::get('/', function(){ # 返回字符串
return 'hello world';
# 返回完整的responses实例,有两种方法
返回responses对象:
use illuminate\http\response;
return (new response($content, $status))
->header('content-type', $value);
或者使用response辅助方法:
return response($content, $status)->header('content-type', $value);
# 返回视图
return response()->view('hello')->header('content-type', $type);
# 添加cookies
return response($content)->withcookie(cookie('name', 'value'));
重定向
return redirect('user/login'); # 使用redirect重定向方法
return redirect('user/login')->with('message', 'login failed'); # 重定向,并将当前数据保存至session
return redirect()->back(); # 重定向至前一个位置
return redirect()->route('login'); # 重定向到特定路由
# 重定向到特定路由,并带参数
return redirect()->route('profile', [1]); # 路由的 uri 为:profile/{id}
return redirect()->route('profile', ['user' => 1]); # 路由的 uri 为:profile/{user}
# 根据控制器动作的重定向
return redirect()->action('app\http\controllers\homecontroller@index');
return redirect()->action('app\http\controllers\usercontroller@profile', ['user' => 1]); # 带参数
其他响应
# 返回json
return response()->json(['name' => 'abigail', 'state' => 'ca']);
# 返回jsonp
return response()->json(['name' => 'abigail', 'state' => 'ca'])
->setcallback($request->input('callback'));
# 文件下载
return response()->download($pathtofile, $name, $headers);
响应宏
# 定义响应宏,通常定义在provider的boot方法内
response::macro('caps', function($value) use ($response){ # php在默认情况下,匿名函数不能调用所在代码块的上下文变量,而需要通过使用use关键字。use会复制一份变量到闭包内,也支持引用形式,如use ( &$rmb )
return $response->make(strtoupper($value));
});
# 调用响应宏
return response()->caps('foo');
视图
基本视图
# 视图定义 文件路径及文件名:resources/views/greeting.php
hello,
# 视图调用
route::get('/', function()
{
return view('greeting', ['name' => 'james']); # 传给视图的参数为一个键值对数组
});
# 子文件夹视图调用 定义位置:resources/views/admin/profile.php
return view('admin.profile', $data);
# 传递数据到视图的其他方法
$view = view('greeting')->with('name', 'victoria'); # 传统方法
$view = view('greeting')->withname('victoria'); # 魔术方法
$view = view('greetings', $data); #直接传数组 $data为一个键值对数组
# 共享数据给所有视图
自定义一个provider,或者直接在appserviceprovider的boot方法内添加:
view()->share('data', [1, 2, 3]);
或者:
view::share('data', [1, 2, 3]);
# 确认视图是否存在
if (view()->exists('emails.customer'))
# 从一个文件路径产生视图
return view()->file($pathtofile, $data);
视图组件
视图组件就是在视图被渲染前,会调用的闭包或类方法。
# 定义视图组件
use view;
use illuminate\support\serviceprovider;
class composerserviceprovider extends serviceprovider {
public function boot(){
view::composer('profile', 'app\http\viewcomposers\profilecomposer'); # 使用类来指定视图组件
view::composer('dashboard', function($view){ # 使用闭包来指定视图组件
...
});
}
...
}
使用类来指定视图组件,在视图被渲染之前将调用所指定类的名为compose的方法。如上例中,profilecomposer'类的定义为:
users = $users;
}
public function compose(view $view){ # compose方法被传入了一个view的实例,在此可以传参给view
$view->with('count', $this->users->count());
}
}
# 在视图组件内使用通配符
view::composer('*', function($view){ # 相当于定义给所有视图
# 同时对多个视图附加视图组件
view::composer(['profile', 'dashboard'], 'app\http\viewcomposers\myviewcomposer');
# 定义多个视图组件
view::composers([
'app\http\viewcomposers\admincomposer' => ['admin.index', 'admin.profile'],
'app\http\viewcomposers\usercomposer' => 'user',
'app\http\viewcomposers\productcomposer' => 'product'
]);
service providers
每个自定义的provider都必须继承自illuminate\support\serviceprovider,并在config/app.php中的providers数组中注册。自定义的provider必须定义register()方法,用于定义注册时的行为。此外还有两个可选方法和一个可选属性:boot()方法在所有的provider都被加载后才会调用,而provides()方法用来和$defer可选属性配合,提供缓载功能。
通过服务提供者的方式来提供服务的思路:实现一个完成实际工作的类,定义一个provider,并在provider的register()方法中往系统容器注册实际工作类以及获取实际工作类实例的方法。然后再在应用配置中注册这个provider。这样,应用初始化时会调用所有provider的register()方法来间接注册获取实际工作类实例的方式。
# 定义一个基本provider
app->singleton('riak\contracts\connection', function($app){
return new connection($app['config']['riak']);
});
}
}
service container
基本用法
在provider内部,可以通过$this->app来访问服务容器。
注册依赖主要有两种方式:回调接口方式和绑定实例接口。
# 闭包回调的方式
$this->app->bind('foobar', function($app){
return new foobar($app['somethingelse']);
});
# 以单例方式注册,之后的调用都返回相同的实例
$this->app->singleton('foobar', function($app){
return new foobar($app['somethingelse']);
});
# 绑定为一个已经存在的实例
$foobar = new foobar(new somethingelse);
$this->app->instance('foobar', $foobar);
从容器解析出实例也有两种方式:
$foobar = $this->app->make('foobar'); # 使用make()方法解析
$foobar = $this->app['foobar']; # 因为容器实现了arrayaccess接口,所以可以用数组访问形式
在定义好注册、解析信息后,就可以直接在类的构造函数中通过type-hint的方式指定所需要的依赖,容器将自动注入所需要的所有依赖。
users = $users;
}
public function show($id){
}
}
绑定接口
interface eventpusher {
public function push($event, array $data);
}
class pushereventpusher implements eventpusher {
...
}
因为pushereventpusher类实现了eventpusher接口,所以可以直接注册这个接口并绑定为某个实现了该接口的类:
$this->app->bind('app\contracts\eventpusher', 'app\services\pushereventpusher');
当有类需要eventpusher接口时,会告诉容器应该注入pushereventpusher。
上下文绑定
$this->app->when('app\handlers\commands\createorderhandler')
->needs('app\contracts\eventpusher')
->give('app\services\pubnubeventpusher');
标签
$this->app->bind('speedreport', function(){
});
$this->app->bind('memoryreport', function(){
});
$this->app->tag(['speedreport', 'memoryreport'], 'reports'); # 将上两步注册的类打成一个标签‘reports’
一旦服务打上标签,可以通过 tagged 方法轻易地解析它们:
$this->app->bind('reportaggregator', function($app){
return new reportaggregator($app->tagged('reports'));
});
容器事件
容器在解析每一个对象时就会触发一个事件。可以用resolving方法来监听此事件(被解析的对象将被传入到闭包方法中):
$this->app->resolving(function($object, $app){ # 当容器解析任意类型的依赖时被调用
...
});
$this->app->resolving(function(foobar $foobar, $app){ # 当容器解析’foobar’类型的依赖时被调用
...
});
contracts
contracts是所有laravel主要组件实现所用的接口,可以看到contracts目录下的目录结构和illuminate中的一样。contracts中为接口定义,illuminate为具体实现。illuminate中每个具体实现的类都扩展了其在contracts中对应的接口。这样将接口和实现相分离,可以使依赖注入变得低耦合。
/laravel
/framework
/src
/illuminate
/auth
/broadcasting
/bus
...
/contracts
/auth
/broadcasting
/bus
...
facades
基本用法
facades提供一个静态接口给在应用程序的服务容器中可以取用的类。(设计模式中“装饰模式”的一个应用,主要是使用class_alias来创建类别名,另外使用__callstatic()来提供一个静态代理,其最终是使用模拟对象的方式模拟php对象并调用对象的方法)
laravel的facades和你建立的任何自定义facades,将会继承基类facade,并只需要去实现一个方法:getfacadeaccessor()。
如cache这个facade的调用:$value = cache::get('key');
看一下类的实现:
class cache extends facade {
protected static function getfacadeaccessor() { return 'cache'; } # 该方法的作用就是返回服务容器绑定的名称
}
当用户在cache的facade上执行任何的静态方法,laravel会从服务容器解析被绑定的cache ,并对该对象执行被请求的方法 (在这个例子中,get)
所有的facades存在于全局命名空间,当在有嵌套的命名空间中使用时,需要导入facade类进入命名空间:
use cache; # 导入cache facade
class photoscontroller extends controller {
public function index(){
$photos = cache::get('photos');
}
}
