记一下笔记

入门

安装

composer create-project laravel/laravel my_super_powerfull_laravel_app
cd my_super_powerfull_laravel_app
php artisan serve

配置

环境配置

环境配置使用.env文件,其中所有的值都被解析为字符串,然后存放在全局变量$_ENV里面,不要提交到版本管理器。
带空格的值需要用双引号。
可以用env函数来检索

'debug'=>env('APP_DEBUG',false),//第二个参数表示默认值

获取当前环境

就是.env里面APP_ENV定义的环境。可以用门面方法。

use Illuminate\Support\Facades\App;

$environment = App::environment();

enviroment方法接收1个参数,判断当前环境是否为接收到的值。如果是则返回true。

if (App::environment('local')) {
    // 当前环境是 local
}

if (App::environment(['local', 'staging'])) {
    // 当前环境是 local 或者 staging...
}
  • 环境名可以在服务中重新定义

获取配置

任何时候都可以用config函数来获取,可以用小数点,还可以指定默认值。

$value = config('app.timezone');

// 如果配置值不存在,返回一个默认值
$value = config('app.timezone', 'Asia/Seoul');

修改配置,要用数组

config(['app.timezone' => 'America/Chicago']);

缓存配置

生产环境应该用php artisan config:cache 来将所有配置合并到一个缓存文件,以便程序快速访问。
本地开发环境不要用,因为使用后,更改配置不会生效。如果要用的话,那就应该只在配置文件里面调用env函数,不要在其他地方调用env函数。因为缓存了配置后,就不会加载.env文件的内容了。

调试模式Debug

config/app.php里面的debug,配置调试模式是否开启。默认情况下,会以.env文件中的APP_DEBUG设置为准。开发环境打开,生产环境关闭,否则会泄露服务器以及应用配置信息。

维护模式

请求生命周期

入口

入口public/index.php,之后从bootstrap/app.php中检索应用程序实例

HTTP和Console内核

先关注HTTP内核

  • HTTP内核从Illuminate\Foundation\HTTP\kernel扩展而来。这个内核定义了一个,执行请求前需要运行的引导程序数组。这些引导程序用来配置异常处理、配置日志、检测应用程序环境。通常不需要在意这些配置
  • 中间件列表,所有请求先经过中间件,再到应用程序。此处确定程序是否处于维护模式,校验csfr等。

服务Service Provider

内核会加载config/app.php里面providers数组里面定义的服务。
Laravel会遍历这个数组,实例化每一个服务,实例化后调用register方法。注册了所有服务后,会调用其boot方法。
服务负责引导框架的组件,如数据库、队列、验证和路由组件。Laravel的主要功能都来自服务。

路由Route

App\providers\RouteServiceProvider提供路由服务,它会加载routes目录中包含的路由文件。
框架启动完成,所有服务均已注册完毕后,路由服务将接管请求,并将其分发到对应的路径、控制器、或者路由相关中间件。

要过滤或检查请求内容,用中间件就很方便。比如,Laravel有个中间件,来判断用户是否有权限。如果没有,页面会跳转到登陆页;如果有权限,请求会进入应用程序去进一步处理。
有些中间件默认分配到所有路由里面,比如HTTP内核中$middleware变量中定义的那些;有些只会对部分路由生效。
经过了前置中间件的请求,会进入到路由或者控制器,控制器返回的响应还会经过后置中间件,才会发送到用户端。

完结

路由或者控制器返回的响应,还会经过后置中间件,以便检查或者修改响应数据。最后,HTTP内核的handle方法会返回响应,index.php调用send方把响应发送到用户浏览器。

至此,为一个生命周期。

服务很重要

应用实例创建,服务注册完毕,应用启动完成,处理用户请求。就这么简单。
应用默认的服务都在app\Provicers文件夹里面。
刚开始AppServiceProvider基本是空的,你可以自己添加要启动的服务。

基础功能

路由

基本路由

//返回字符串
Route::get('/greeting', function () {
    return 'Hello World';
});

//返回视图
Route::get('/greeting', function () {
    return view('greeting',['title'=>'欢迎页面']);
});

//分配控制器
use App\Http\Controllers\UserController;

Route::get('/user', [UserController::class, 'index']);

//限定方法
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

//匹配多个方法
Route::match(['get', 'post'], '/', function () {
    //
});

//不限制请求方法
Route::any('/', function () {
    //
});

//依赖注入
use Illuminate\Http\Request;

Route::get('/users', function (Request $request) {
    // ...
});

CSFR

指向 POST, PUT, PATCH, 或 DELETE 路由的 HTML 表单都应该包含一个 CSRF 令牌字段,否则,这个请求将被拒绝。

<form method="POST" action="/profile">
    @csrf
    ...
</form>

重定向

Route::redirect('/here', '/there');
Route::redirect('/here', '/there', 301);
Route::permanentRedirect('/here', '/there');

返回视图

Route::view('/welcome', 'welcome');

Route::view('/welcome', 'welcome', ['name' => 'Taylor']);
  • 参数不能使用view, data, status, 和 headers。

路由参数

Route::get('/user/{id}', function ($id) {
    return 'User '.$id;
});

//多个参数
Route::get('/posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

//依赖注入,在依赖关系后,列出参数
use Illuminate\Http\Request;

Route::get('/user/{id}', function (Request $request, $id) {
    return 'User '.$id;
});

//可选参数,在参数后面加上?,但是要保证有默认值
Route::get('/user/{name?}', function ($name = null) {
    return $name;
});

Route::get('/user/{name?}', function ($name = 'John') {
    return $name;
});

//正则约束
Route::get('/user/{name}', function ($name) {
    //
})->where('name', '[A-Za-z]+');

Route::get('/user/{id}', function ($id) {
    //
})->where('id', '[0-9]+');

Route::get('/user/{id}/{name}', function ($id, $name) {
    //
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

//正则约束助手函数
Route::get('/user/{id}/{name}', function ($id, $name) {
    //
})->whereNumber('id')->whereAlpha('name');

Route::get('/user/{name}', function ($name) {
    //
})->whereAlphaNumeric('name');

Route::get('/user/{id}', function ($id) {
    //
})->whereUuid('id');
  • 如果传入参数与约束不匹配,则返回404

路由参数全局约束

App\Providers\RouteServiceProvider@boot中定义。

public function boot()
{
    Route::pattern('id', '[0-9]+');
}

之后,在相同名称的参数上都会应用这些约束条件。

Route::get('/user/{id}', function ($id) {
    // 只有在 id 为数字时才执行...
});

路由命名

必须全局唯一。

Route::get(
    '/user/profile',
    [UserProfileController::class, 'show']
)->name('profile');

生成url

命名后,就可以在route 和 redirect中使用路由的名字了。额外参数会以查询字符串的形式添加到url。

$url = route('profile', ['id' => 1, 'photos' => 'yes']);

检查当前路由

比如在中间件检查:

public function handle($request, Closure $next)
{
    if ($request->route()->named('profile')) {
        //
    }

    return $next($request);
}

路由组

批量分配中间件

定义组之前,指定中间件。

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // Uses first & second middleware...
    });

    Route::get('/user/profile', function () {
        // Uses first & second middleware...
    });
});

子域路由

最好在定义根路由之前定义子域路由,以免先匹配到根路由。

Route::domain('{account}.example.com')->group(function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

路由前缀

Route::prefix('admin')->group(function () {
    Route::get('/users', function () {
        // Matches The "/admin/users" URL
    });
});

路由命名前缀

Route::name('admin.')->group(function () {
    Route::get('/users', function () {
        // Route assigned name "admin.users"...
    })->name('users');
});

路由和模型绑定

数据库模型