前言Laravel 是 PHP 生态里文档最完整、周边工具最齐备的全栈 Web 框架。很多人的入门路径是「装好项目 → 抄一段路由 → 写一个 Eloquent 查询 → 页面出来了」然后就认为入门完成。这个理解不算错但会漏掉真正决定后期效率的部分服务容器Service Container、服务提供者Service Provider和门面Facade。Laravel 里几乎所有看起来像「魔法」的写法——auth()、cache()、Route::get()——底层都走这三样东西。先把这条主线建立起来再学具体 API 会轻松很多。第二个常见误解是Laravel 各版本「差不多」随便找一份教程照着敲就行。实际上 Laravel 每个大版本都对 PHP 最低版本有硬性要求并且骨架结构在大版本之间会变。拿 Laravel 11 的项目去套 Laravel 8 的教程或者反过来最常见的症状是「文档里说的app/Http/Kernel.php我这儿根本没有」。这不是你装错了是版本变了。本文面向已经会写基础 PHP、准备上手 Laravel 的读者把版本前提、请求链路、路由与中间件、迁移与 Eloquent、配置与环境这几块串成一条线讲清楚示例以 Laravel 9 及以上为基准涉及版本差异的地方会单独标注。一、版本前提与项目骨架先确认版本约束这一步不能跳。表里的最低 PHP 版本是硬性的低于它 Composer 直接拒绝安装。Laravel 大版本最低 PHP骨架上的关键变化Laravel 8PHP 7.3模型工厂改为类支持匿名类迁移Laravel 9PHP 8.0引入Attribute访问器/修改器新语法Laravel 10PHP 8.1骨架方法补齐原生返回类型新增Process门面Laravel 11PHP 8.2精简骨架移除app/Http/Kernel.php中间件改到bootstrap/app.php配置创建项目与启动内置服务器# 需要 PHP 8.2 时选 Laravel 11需要 PHP 8.1 时选 Laravel 10composer create-project laravel/laravel:^11.0 my-appcd my-appphp artisan serve --host127.0.0.1 --port8000php artisan serve起的是 PHP 内置服务器built-in server只适合本机开发不要拿到生产环境用。它的作用是让你不必额外装 Nginx 或 Apache 就能看到欢迎页。骨架目录在 Laravel 11 有一处非常显眼的变化config/目录默认只剩少数几个文件app/Http/Kernel.php和app/Console/Kernel.php都不再存在原本写在里面的中间件、异常处理、调度配置统一挪到了bootstrap/app.php。Laravel 10 及以前则相反config/是全量的两个 Kernel 都在。你在搜索报错信息时如果搜到的答案让你去改Kernel.php先确认自己的版本。二、一次请求的完整链路理解入口文件比背 API 重要。所有请求最终都汇集到public/index.php这一个入口?php // public/index.phpLaravel 10 及以前的结构Laravel 11 内容类似define(LARAVEL_START, microtime(true));require __DIR__./../vendor/autoload.php;// 从 bootstrap/app.php 拿到应用实例服务容器$app require_once __DIR__./../bootstrap/app.php;// 从容器里解析出 HTTP 内核$kernel $app-make(Illuminate\Contracts\Http\Kernel::class);// 把请求交给内核处理拿到响应对象$response $kernel-handle($request Illuminate\Http\Request::capture());// 发送响应输出 HTTP 头与正文$response-send();// 收尾执行可终止中间件、注册的终止回调$kernel-terminate($request, $response);这条链路值得记住的点有三个。第一bootstrap/app.php返回的是一个应用实例它同时就是服务容器后面代码里无处不在的app()拿到的正是它。第二HTTP 内核在handle()内部会先跑一遍「启动引导」bootstrap——加载.env、加载配置、注册门面、注册并启动所有服务提供者然后才把请求推进中间件管道最后交给路由器Router分派。第三terminate()发生在响应已经发给浏览器之后所以终止回调里做的事不会影响用户看到的响应内容。三、路由、控制器与中间件路由文件在routes/下Web 路由默认走web中间件组带 Session、CSRF 校验API 路由默认走api中间件组无状态、带节流。?php // routes/web.php适用于 Laravel 9/10/11需要 PHP 8.0use App\Http\Controllers\PostController;use Illuminate\Support\Facades\Route;Route::get(/, function () {return view(welcome);});// 路由模型绑定{post} 会被自动解析成 Post 实例找不到则返回 404// whereNumber 限制该段只匹配数字避免 /posts/abc 走进控制器再抛类型错误Route::get(/posts/{post}, [PostController::class, show])-whereNumber(post)-name(posts.show);// 路由组统一加中间件、统一前缀、统一路由名前缀Route::middleware([auth])-prefix(admin)-name(admin.)-group(function () {Route::get(/dashboard, [PostController::class, dashboard])-name(dashboard);});// 注册资源路由index/create/store/show/edit/update/destroy 七个动作Route::resource(comments, CommentController::class)-only([index, store]);whereNumber()、whereAlpha()、whereAlphaNumeric()、whereUuid()是 Laravel 8 起提供的约束方法比手写-where(post, [0-9])更不容易写错正则。控制器一侧构造函数和动作方法的参数都会由容器自动注入?php // app/Http/Controllers/PostController.php需要 PHP 8.0 的构造器属性提升namespace App\Http\Controllers;use App\Models\Post;use Illuminate\Http\Request;use Illuminate\View\View;class PostController extends Controller{public function index(): View{$posts Post::query()-where(published, true)-latest(published_at)-paginate(15);return view(posts.index, [posts $posts]);}public function show(Request $request, Post $post): View{return view(posts.show, [post $post]);}}paginate(15)返回的是LengthAwarePaginator在 Blade 里用$posts-links()渲染分页链接即可。四、迁移、模型与配置迁移Migration是版本化的表结构定义。Laravel 9 起骨架默认使用匿名类迁移?php // database/migrations/xxxx_create_posts_table.php需要 PHP 8.0 的匿名类use Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;return new class extends Migration{public function up(): void{Schema::create(posts, function (Blueprint $table) {$table-id();$table-foreignId(user_id)-constrained()-cascadeOnDelete();$table-string(title, 120);$table-text(body);$table-boolean(published)-default(false);$table-timestamp(published_at)-nullable();$table-timestamps();});}public function down(): void{Schema::dropIfExists(posts);}};对应模型?php // app/Models/Post.php适用于 Laravel 9namespace App\Models;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo;class Post extends Model{use HasFactory;// 白名单式批量赋值只有列在这里的字段能被 create()/update() 批量写入protected $fillable [user_id, title, body, published, published_at];protected $casts [published boolean,published_at datetime,];public function user(): BelongsTo{return $this-belongsTo(User::class);}}Laravel 11 起$casts属性也可以改成方法写法protected function casts(): array { return [...]; }。两种写法在 Laravel 11 下都能用属性写法更常见。配置与环境变量方面.env只负责给config/*.php提供值业务代码不应该直接读env()?php // config/database.php 片段配置文件读 env业务代码读 configreturn [default env(DB_CONNECTION, mysql),connections [mysql [host env(DB_HOST, 127.0.0.1),database env(DB_DATABASE, forge),username env(DB_USERNAME, forge),password env(DB_PASSWORD, ),],],];原因是php artisan config:cache会把配置合并成单个文件缓存起来此时.env根本不会被加载env()会返回null。所以规范做法是.env里定义APP_XXXconfig/app.php里用env()读取业务代码用config(app.xxx)读取。常见坑点❌ 业务代码里写env(MAIL_HOST)本地能跑上线执行php artisan config:cache后返回null邮件全部发不出去。✅ 只在config/*.php里用env()业务代码一律用config(mail.host)。❌ 模型不写$fillable且采用默认的$guarded [*]之外的宽松配置直接把$request-all()传进create()。✅ 明确写protected $fillable [...]或改用$request-validated()只取校验过的字段。未受保护的批量赋值会让攻击者通过多传一个is_admin字段提升权限。❌ 用-where(published, 1)或-where(published, 1)混着比较布尔列在不同数据库驱动下语义不一致。✅ 在模型里声明protected $casts [published boolean];比较时统一用true/false让框架负责类型转换。❌ 路由写成Route::get(/posts/{id}, ...)然后在控制器里Post::find($id)忘记处理找不到的情况用户拿到 500。✅ 用Route::get(/posts/{post}, ...)配合Post $post参数做路由模型绑定框架自动返回 404确实要手动查时用findOrFail()。❌ 在 Blade 里用{!! $content !!}输出用户提交的内容script会被原样执行。✅ 默认用{{ $content }}自动htmlspecialchars转义确实需要输出富文本时先经过白名单式 HTML 净化再输出。❌ Laravel 11 项目里照着旧教程找app/Http/Kernel.php加中间件文件不存在以为自己装错了版本。✅ Laravel 11 改用bootstrap/app.php的-withMiddleware(...)注册中间件需要旧版风格时用php artisan install:api之类命令补齐对应组件。❌ 直接依赖php artisan serve做联调然后抱怨并发一高就卡死。✅artisan serve只是内置服务器的包装单进程、非并发安全仅用于本机开发压测和生产部署请用 Nginx PHP-FPM。总结知识点关键结论版本前提L8 需 PHP 7.3、L9 需 PHP 8.0、L10 需 PHP 8.1、L11 需 PHP 8.2请求入口所有请求经public/index.php→ 容器 → HTTP 内核 → 中间件管道 → 路由生命周期bootstrap()装载 .env/配置/门面/服务提供者terminate()在响应发出后执行路由{post}参数配合Post $post参数做模型绑定Laravel 8 有whereNumber()等约束迁移匿名类迁移从 Laravel 9 起是骨架默认写法配置env()只出现在config/里业务代码一律config()骨架差异Laravel 11 移除两个 Kernel配置收敛到bootstrap/app.php入门的正确顺序是先跑通一条「路由 → 控制器 → 模型 → 视图」的最小闭环再回头补服务容器与服务提供者。真正需要花时间理解的是容器怎么解析依赖、register()和boot()的区别以及门面背后到底解析到了哪个类——这三点掌握了Laravel 的「魔法」就变成了可推演的普通代码。
阅读完成 · 觉得有帮助?