laravel详细学习 [TOC]
apache配置 <VirtualHost *:80 > ServerName localhost ServerAlias localhost DocumentRoot "${INSTALL_DIR} /www" <Directory "${INSTALL_DIR} /www/" > Options +Indexes +Includes +FollowSymLinks +MultiViews AllowOverride All Require local </Directory> </VirtualHost>
介绍 laravel是基于mvc模式的php框架,m——模型层,v——视图层,c——控制器层;以下为laravel框架的目录文件,框出来的文件目录将在后续中用到
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 |– app 包含Controller、Model、路由等在内的应用目录,大部分业务将在该目录下进行 | |– Console 命令行程序目录 | | |– Commands 包含了用于命令行执行的类,可在该目录下自定义类 | | |– Kernel.php 命令调用内核文件,包含commands变量(命令清单,自定义的命令需加入到这里)和schedule方法(用于任务调度,即定时任务) | |– Events 事件目录 | |– Exceptions 包含了自定义错误和异常处理类 | |– Http HTTP传输层相关的类目录 | | |– Controllers 控制器目录 | | |– Middleware 中间件目录 | | |– Requests 请求类目录 | | |– Kernel.php 包含http中间件和路由中间件的内核文件 | | |– routes.php 强大的路由 | |– Jobs 该目录下包含队列的任务类 | |– Listeners 监听器目录 | |– Providers 服务提供者目录 | |– User.php 自带的模型实例,我们新建的Model默认也存储在该目录 |– bootstrap 框架启动载入目录 | |– app.php 创建框架应用实例 | |– autoload.php 自动加载 | |– cache 存放框架启动缓存,web服务器需要有该目录的写入权限 |– config 各种配置文件的目录 | |– app.php 系统级配置文件 | |– auth.php 用户身份认证配置文件,指定好table和model就可以很方便地用身份认证功能了 | |– broadcasting.php 事件广播配置文件 | |– cache.php 缓存配置文件 | |– compile.php 编译额外文件和类需要的配置文件,一般用户很少用到 | |– database.php 数据库配置文件 | |– filesystems.php 文件系统配置文件,这里可以配置云存储参数 | |– mail.php 电子邮件配置文件 | |– queue.php 消息队列配置文件 | |– services.php 可存放第三方服务的配置信息 | |– session.php 配置session的存储方式、生命周期等信息 | |– view.php 模板文件配置文件,包含模板目录和编译目录等 |– database 数据库相关目录 | |– factories 5.1 以上版本的新特性,工厂类目录,也是用于数据填充 | | |– ModelFactory.php 在该文件可定义不同Model所需填充的数据类型 | |– migrations 存储数据库迁移文件 | |– seeds 存放数据填充类的目录 | |– DatabaseSeeder.php 执行php artisan db:seed命令将会调用该类的run方法。该方法可调用执行该目录下其他Seeder类,也可调用factories方法生成ModelFactory里定义的数据模型 |– public 网站入口,应当将ip或域名指向该目录而不是根目录。可供外部访问的css、js和图片等资源皆放置于此 | |– index.php 入口文件 | |– .htaccess Apache服务器用该文件重写URL | |– web.config IIS服务器用该文件重写URL |– resources 资源文件目录 | |– assets 可存放包含LESS、SASS、CoffeeScript在内的原始资源文件 | |– lang 本地化文件目录 | |– views 视图文件就放在这啦 |– storage 存储目录。web服务器需要有该目录及所有子目录的写入权限 | |– app 可用于存储应用程序所需的一些文件 | |– framework 该目录下包括缓存、sessions和编译后的视图文件 | |– logs 日志目录 |– tests 测试目录 |– vendor 该目录下包含Laravel源代码和第三方依赖包 |– .env 环境配置文件。config目录下的配置文件会使用该文件里面的参数,不同生产环境使用不同的.env文件即可。 |– artisan 强大的命令行接口,你可以在app/Console/Commands下编写自定义命令 |– composer.json 存放依赖关系的文件 |– composer.lock 锁文件,存放安装时依赖包的真实版本 |– gulpfile.js gulp(一种前端构建工具)配置文件 |– package.json gulp配置文件 |– phpspec.yml phpspec(一种PHP测试框架)配置文件 |– phpunit.xml phpunit(一种PHP测试框架)配置文件 |– server.php PHP内置的Web服务器将把这个文件作为入口。以public /index.php为入口的可以忽略掉该文件
app是应用的核心代码文件目录,以后的代码基本都在这里完成;app/Http/Controller目录是应用的控制器文件;routes.php是框架的路由文件,负责路由分配和映射;Http下的类文件,比如上面目录中的User.php、Menu.php文件是应用的模型文件;config目录是所有应用的配置文件目录;public是框架的入口文件及静态资源文件目录;resources/views则是应用的视图文件目录。
关于路由 在 Laravel 应用中,定义路由有两个入口,一个是 routes/web.php
,用于处理终端用户通过 Web 浏览器直接访问的请求,另一个是 routes/api.php
,
get请求和post请求
<?php Route::get(/test1,function ( ) { return 'hello' ; }); Route::post(/test1,function ( ) { return 'hello' ; }); Route::get('/' , 'WelcomeController@index' ); 访问/时调用App\Http\Controllers\WelcomeController 控制器的 index 方法
关于多路由访问有match和any方式
<?php Route::match(['get' ,'post' ], 'multi' , function ( ) { return 'multi post or get' ; }); Route::any('multi' , function ( ) { return 'multi get or post' ; });
路由参数 如果你定义的路由需要传递参数,只需要在路由路径中进行标识并将其传递到闭包函数即可:
Route::get('user/{id}' , function ($id ) { return "用户ID: " . $id ; });
这样,当你访问 http://blog.test/user/1000
的时候,就可以在浏览器看到 用户ID: 1000
字符串。
此外,你还可以定义可选的路由参数,只需要在参数后面加个 ?
标识符即可,同时你还可以为可选参数指定默认值:
Route::get('user/{id?}' , function ($id = 1 ) { return "用户ID: " . $id ; });
这样,如果不传递任何参数访问 http://blog.test/user
,则会使用默认值 1
作为用户 ID。更深入的用法还可以用正则匹配
路由分组 所谓路由分组,其实就是通过 Route::group
将几个路由聚合到一起,然后给它们应用对应的共享特征:
Route::group([], function ( ) { Route::get('hello' , function ( ) { return 'Hello' ; }); Route::get('world' , function ( ) { return 'World' ; }); });
路由别名 给路由通过[‘as’ => ‘alias’]数组使用别名后,可通过route(‘别名’)生成url,请看代码理解:
<?php Route::get('student/info' ,['as' => 'studentInfo' ,function ( ) { return route('studentInfo' ); }]); 访问url:http: 页面返回:http: 注:别名的好处在于,以后在控制器中使用route('别名' )的方式生成url后,即便修改了路由的名字,也不用再修改控制器程序,因为通过别名程序能自动生成修改后的url
具体还有路由路径前缀
, 子域名路由
,子命名空间
,路由命名前缀
。可以看看laravel官方手册
中间件 简介 中间件为过滤进入应用的 HTTP 请求提供了一套便利的机制。例如,Laravel 内置了一个中间件来验证用户是否经过认证(如登录),如果用户没有经过认证,中间件会将用户重定向到登录页面,而如果用户已经经过认证,中间件就会允许请求继续往前进入下一步操作。
当然,除了认证之外,中间件还可以被用来处理很多其它任务。比如:CORS 中间件可以用于为离开站点的响应添加合适的头(跨域);日志中间件可以记录所有进入站点的请求,从而方便我们构建系统日志系统。
Laravel 框架自带了一些中间件,包括认证、CSRF 保护中间件等等。所有的中间件都位于 app/Http/Middleware
目录下。
定义中间件 这个命令会在 app/Http/Middleware
目录下创建一个新的中间件类 CheckAge
,在这个中间件中,我们只允许提供的 age
大于 200 的请求才能访问应用该中间件的路由,否则,我们会将用户重定向到 /
URI:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php namespace App \Http \Middleware ;use Closure ;class CheckAge { public function handle ($request , Closure $next ) { if ($request ->age <= 200 ) { return redirect('/' ); } return $next ($request ); } }
正如你所看到的,如果请求参数中的 age
小于等于 200,中间件会返回一个 HTTP 重定向给客户端;否则,请求会被传递下去。将请求往下传递可以通过调用回调函数 $next
并传入当前 $request
。
理解中间件的最好方式就是将中间件看做 HTTP 请求到达目标动作之前必须经过的“层”,每一层都会检查请求并且可以完全拒绝它。所有的中间都是在服务容器 中解析,所以你可以在中间件的构造函数中类型提示任何依赖。
具体还有请求之前/之后的中间件
,注册中间件
,中间件参数
等。
CSRF防护 简单地说,csrf就是攻击者伪装用户来攻击授信网站。
在laravel中防护csrf用的时csrf令牌,其实就是用token或者cookie来验证请求输入的 token 值和 Session 中存储的 token 是否一致,如果没有传递该字段或者传递过来的字段值和 Session 中存储的数值不一致,则会抛出异常。
控制器入门 可以通过artisan命令快速创建一个控制器
php artisan make :controller TakeController
该命令会在 app/Http/Controllers
目录下创建一个新的名为 TaskController.php
的文件,默认生成的控制器代码如下:
<?php namespace App \Http \Controllers ;use Illuminate \Http \Request ;class ShowController extends Controller { public function show (User $user ) { return view('user.profile' , ['user' => $user ]); } }
单一动作控制器 用__invoke
方法:
例如可在上述类中定义
public function __invoke ($id ) { return view('user.profile' , ['user' => User::findOrFail($id )]); }
当你为这个单动作控制器注册路由的时候,不需要指定方法:
Route::get('user/{user}' , \App\Http\Controllers\ShowController::class);
控制器中间件 Route::get('profile', [UserController::class, 'show'])->middleware('auth');
不过,在控制器的构造函数中设置中间件更方便,你可以使用基类提供的 middleware
方法轻松分配中间件给该控制器的动作,你甚至可以限制中间件只应用到该控制器类的指定方法:
class UserController extends Controller { public function __construct ( ) { $this ->middleware('auth' ); $this ->middleware('log' )->only('index' ); $this ->middleware('subscribed' )->except('store' ); } }
资源控制器 命令创建
php artisan make:controller PostController --resource
该 Artisan 命令将会生成一个控制器文件 app/Http/Controllers/PostController.php
,这个控制器包含了每一个资源操作对应的方法:
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 76 77 78 79 80 81 82 83 84 <?php namespace App \Http \Controllers ; use Illuminate \Http \Request ; class PostController extends Controller { public function index ( ) { } public function create ( ) { } public function store (Request $request ) { } public function show ($id ) { } public function edit ($id ) { } public function update (Request $request , $id ) { } public function destroy ($id ) { } }
然后创建路由
Route::resource('post' ,app/Http/Controllers/PostController.php::class);
获取用户输入 Route::get('task/create' , 'TaskController@create' ); Route::post('task' , 'TaskController@store' );
我们可以在TaskController.php
中创建create方法满足我们的功能
public function store (Request $request ) { $task = new Task(); $task ->title = $request ->input('title' ); $task ->description = $request ->input('description' ); $task ->save(); return redirect('task' ); }
后面还可以用门面来获取input
.
请求 访问请求实例 在控制器中获取当前 HTTP 请求实例,需要在构造函数或方法中对 Illuminate\Http\Request
类进行依赖注入,这样当前请求实例会被服务容器 自动注入:
依赖注入 & 路由参数 如果还期望在控制器方法中获取路由参数,只需要将路由参数置于其它依赖之后即可,例如,如果你的路由定义如下:
use App\Http\Controllers\UserController; Route::put('user/{id}' , [UserController::class, 'update' ]);
仍然可以对 Illuminate\Http\Request
进行依赖注入并通过如下方式定义控制器方法来访问路由参数 id
:
public function update (Request $request , $id ) { }
还可以在路由闭包中注入 Illuminate\Http\Request
,在执行闭包函数的时候服务容器会自动注入输入请求:
use Illuminate \Http \Request ; Route::get('/' , function (Request $request ) { });
获取请求路径 $path = $request ->path(); if ($request ->is('user/*' )){ }
获取请求 URL 获取请求方法 获取请求输入 文件上传 上述在文档中都比较简单易懂直接看即可
响应 这块主要就是Response 对象 ,其他没有什么特别的
返回一个完整的 Response
实例允许你自定义响应的 HTTP 状态码和头信息。Response
实例继承自 Symfony\Component\HttpFoundation\Response
基类,该类提供了一系列方法用于创建 HTTP 响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Route::get('cookie/response' , function ( ) { return response('Hello World' , 200 ) ->header('Content-Type' , 'text/plain' ); });return response($content ) ->withHeaders([ 'Content-Type' => $type , 'X-Header-One' => 'Header Value' , 'X-Header-Two' => 'Header Value' , ]);return response($content ) ->header('Content-Type' , $type ) ->cookie('name' , 'value' , $minutes ); 利用辅助函数redirect重定向 Route::get('dashboard' , function ( ) { return redirect('home/dashboard' ); }); 还有一些响应,视图响应,文件响应等
关于视图 这个比较没什么特别的东西,跳过。
关于核心类Kernel类 vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php 是laravel处理网络请求的最核心类,在app容器准备好了之后,就会调用本类,之后所有的处理都在此类中。
具体参考:https://www.136.la/php/show-9877.html
https://www.136.la/php/show-6124.html
https://www.136.la/php/show-16856.html
https://blog.csdn.net/w13707470416/article/details/84979186?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242
https://blog.csdn.net/weixin_33916256/article/details/89063048?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
https://blog.csdn.net/imbibi/article/details/78542258?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-4&spm=1001.2101.3001.4242
启动过程 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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 index.php bootstrap/autoload.php --> 自动加载 bootstrap/app.php --> 初始化服务容器(注册基础的服务提供者(事件、日志、路由)、注册核心类别名) bootstrap/app.php --> 注册共享的Kernel和异常处理器 Foundation\\Http\\Kernel.php --> 处理请求和响应 index.php --> 将响应信息发送到浏览器 index.php --> 处理继承自TerminableMiddleware接口的中间件(Session)并结束应用生命周期 其中处理请求和响应包括: 解析Illuminate\\Contracts\\Http\\Kernel,实例化App\\Http\\Kernel a. 实例化Kernel : 构造函数:设置$app /$router ,初始化$router 中middleware数值 b. handle处理请求: 加载路由中间件、加载环境变量、加载配置文件、加载异常处理机制、注册门面、注册服务提供者、启动服务提供者、管道模式注入中间件 c.将响应信息发送到浏览器 注册request实例到容器 ($app [\'request\']->Illuminate\\Http\\Request) -- $request是经过Symfony封装的请求对象 清空之前容器中的request实例 调用bootstrap方法,启动一系列启动类的bootstrap方法: Illuminate\\Foundation\\Bootstrap\\DetectEnvironment 环境配置($app[‘env’]) Illuminate\\Foundation\\Bootstrap\\LoadConfiguration 基本配置($app[‘config’]) Illuminate\\Foundation\\Bootstrap\\ConfigureLogging 日志文件($app[‘log’]) Illuminate\\Foundation\\Bootstrap\\HandleExceptions 错误&异常处理 Illuminate\\Foundation\\Bootstrap\\RegisterFacades 清除已解析的Facade并重新启动,注册config文件中alias定义的所有Facade类到容器 Illuminate\\Foundation\\Bootstrap\\RegisterProviders 注册config中providers定义的所有Providers类到容器 Illuminate\\Foundation\\Bootstrap\\BootProviders 调用所有已注册Providers的boot方法 通过Pipeline发送请求,经过中间件,再由路由转发,最终返回响应 1.自动加载 包括全局函数的加载、顶级命名空间映射、PSR0、PSR4标准的实现 2.初始化服务容器 注册容器本身 将基本的绑定注册到容器中,包括容器自身、容器实例名称app 实例化 app, Illuminate\\Container\\Container 关键代码: protected function registerBaseBindings() { static::setInstance($this); $this->instance(\'app\', $this); $this->instance(Container::class, $this); } 注册基础服务提供者 向容器分别注册了Key为以下值得实例 events log router、url、redirect、Illuminate\\Contracts\\Routing\\ResponseFactory 关键代码: protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); $this->register(new LogServiceProvider($this)); $this->register(new RoutingServiceProvider($this)); } 注册容器别名(注册共享的Kernel) 在调用此方法之前,我们想取得一个容器实例的做法是 App::make(\'app\'); 现在我们可以使用三种方法来取得一个容器实例app App::make(\'Illuminate\\Foundation\\Application\') App::make(\'Illuminate\\Contracts\\Container\\Container\') App::make(\'Illuminate\\Contracts\\Foundation\\Application\') 关键代码: public function registerCoreContainerAliases(){ ... } 3. 注册共享的Kernel和异常处理器 关键代码: $app->singleton( Illuminate\\Contracts\\Http\\Kernel::class, App\\Http\\Kernel::class ); $app->singleton( Illuminate\\Contracts\\Console\\Kernel::class, App\\Console\\Kernel::class ); $app->singleton( Illuminate\\Contracts\\Debug\\ExceptionHandler::class, App\\Exceptions\\Handler::class ); 4. 处理请求和响应 实例化App\\Http\\Kernel 构造函数:设置$app/$router,初始化$router中middleware数值 关键代码: public function __construct(Application $app, Router $router) { $this->app = $app; $this->router = $router; $router->middlewarePriority = $this->middlewarePriority; foreach ($this->middlewareGroups as $key => $middleware) { $router->middlewareGroup($key, $middleware); } foreach ($this->routeMiddleware as $key => $middleware) { $router->aliasMiddleware($key, $middleware); } } 5. handle处理请求 a. 注册request实例到容器 ($app[\'request\']->Illuminate\\Http\\Request) -- $request是经过Symfony封装的请求对象 b. 清空之前容器中的request实例 c. 调用bootstrap方法,启动一系列启动类的bootstrap方法 d. 通过Pipeline发送请求,经过中间件,再由路由转发,最终返回响应 关键代码: protected function sendRequestThroughRouter($request) { $this->app->instance(\'request\', $request); Facade::clearResolvedInstance(\'request\'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } 6. bootstrap方法 a.检测环境变量文件是否正常 b.取得配置文件,即把/config/下的所有配置文件读取到容器(app()->make(\'config\')可以查看所有配置信息) c.注册异常: set_error_handler,set_exception_handler, register_shutdown_function d.把/config/app.php里面的aliases项利用PHP库函数class_alias创建别名,从此,我们可以使用App::make(\'app\')方式取得实例 e.把/config/app.php里面的providers项,注册到容器 f.运行容器中注册的所有的ServiceProvider中得boot方法 关键代码: protected $bootstrappers = [ \\Illuminate\\Foundation\\Bootstrap\\LoadEnvironmentVariables::class, \\Illuminate\\Foundation\\Bootstrap\\LoadConfiguration::class, \\Illuminate\\Foundation\\Bootstrap\\HandleExceptions::class, \\Illuminate\\Foundation\\Bootstrap\\RegisterFacades::class, \\Illuminate\\Foundation\\Bootstrap\\RegisterProviders::class, \\Illuminate\\Foundation\\Bootstrap\\BootProviders::class, ]; 7. 将响应信息发送到浏览器 关键代码: $response->send(); 9. 处理继承自TerminableMiddleware 关键代码: $kernel->terminate($request, $response); 10. Laravel路由 $this->dispatchToRouter() --> $this->router->dispatch($request) --> $this->dispatchToRoute($request); -- /Illuminate/Routing/Router.php --> $response = $this->runRouteWithinStack($route, $request); //干货来了 protected function runRouteWithinStack(Route $route, Request $request) { // 取得routes.php里面的Middleware节点 $middleware = $this->gatherRouteMiddlewares($route); //这个有点眼熟 return (new Pipeline($this->container)) ->send($request) ->through($middleware) //执行上述的中间件 ->then(function($request) use ($route) { //不容易啊,终于到Controller类了 return $this->prepareResponse( $request, $route->run($request); //run控制器 ); }); }
事件