CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Laravel 实战入门/课时/目录结构
RawBlame
课时目录12 节

Laravel 实战入门

  1. 01Laravel 概览
  2. 02目录结构
  3. 03路由
  4. 04控制器
  5. 05Blade 模板
  6. 06Eloquent ORM
  7. 07认证与授权
  8. 08验证与异常
  9. 09队列与事件
  10. 10运维与工具
  11. 11测试
  12. 12综合练习

目录结构

app/routes/config 分层。

30 分钟

目录结构、配置与请求生命周期

这一课你将能做到

  1. 说出 Laravel 11 每个顶层目录的职责,新增代码时知道该放在哪里
  2. 读懂 .env 与 config/*.php 的关系,并能自己加一个自定义配置项
  3. 画出从 public/index.php 到响应发出的完整生命周期,并在 bootstrap/app.php 中注册中间件、异常和调度任务

为什么要学

初学者常见的困惑不是「某段代码看不懂」,而是「文件太多,不知道东西放哪」。Spring Boot 项目里你写的类会按 controller / service / mapper / entity / config / common 分包,Laravel 也有等价的目录约定。理解目录约定后,读任何一个 Laravel 项目都像读自家代码——控制器在 app/Http/Controllers,业务模型在 app/Models,配置在 config,路由在 routes。

另一个必须现在解决的问题是请求生命周期。它是回答「中间件什么时候执行」「服务容器什么时候构建」「异常被谁接住」这些问题的底座,也是面试高频题。本课我们用一份可运行的最小项目把这条链路走一遍。

先建立图像

Laravel 11 项目的目录地图:

shop-api/
├── app/
│   ├── Http/
│   │   ├── Controllers/        # 控制器(等价 Spring 的 @RestController)
│   │   ├── Middleware/         # 自定义中间件(等价 Filter/Interceptor)
│   │   └── Requests/           # 表单请求(含验证规则,第 8 课)
│   ├── Models/                 # Eloquent 模型(等价 @Entity + DAO)
│   ├── Providers/              # 服务提供者(启动/绑定逻辑,等价 @Configuration)
│   ├── Services/               # 业务服务类(约定目录,需自建)
│   ├── Events/ Listeners/      # 事件与监听器(第 9 课)
│   ├── Jobs/                   # 队列任务(第 9 课)
│   └── Console/Commands/       # 自定义 artisan 命令
├── bootstrap/
│   ├── app.php                 # ★ 11 的应用引导:中间件/异常/调度配置
│   ├── providers.php           # 包级服务提供者清单
│   └── cache/                  # 缓存后的配置/路由/服务(需可写)
├── config/                     # 所有配置文件(database/cache/queue...)
├── database/
│   ├── migrations/             # 数据库版本化脚本
│   ├── factories/ seeders/     # 测试数据工厂与填充
│   └── database.sqlite         # 学习期默认数据库
├── public/
│   └── index.php               # ★ 所有 HTTP 请求的唯一入口
├── routes/
│   ├── web.php                 # 网页路由(带 Session、CSRF)
│   ├── api.php                 # API 路由(默认 /api 前缀,无状态)
│   ├── console.php             # 闭包形式的命令行任务
│   └── channels.php            # 广播频道(第 9 课 Reverb)
├── resources/views/            # Blade 模板(第 5 课)
├── storage/                    # 日志、编译后的 Blade、上传文件(需可写)
└── tests/                      # Pest/PHPUnit 测试(第 11 课)

与 Spring Boot 的目录心智对照:app ≈ src/main/java,config ≈ application.yml + @Configuration 类,resources/views ≈ templates/, ≈ 日志目录, ≈ 。

storage/logs
tests
src/test/java

跟着做

步骤 1:观察入口文件

打开 public/index.php(Laravel 11 版本非常短):

<?php

use Illuminate\Foundation\Application;
use Illuminate\Http\Request;

define('LARAVEL_START', microtime(true));

// 维护模式(php artisan down 生成的文件)
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
    require $maintenance;
}

// Composer 自动加载
require __DIR__.'/../vendor/autoload.php';

// 引导应用并处理请求
(require_once __DIR__.'/../bootstrap/app.php')
    ->handleRequest(Request::capture());

三行核心动作:加载自动加载器 → 拿到应用实例 → 用它处理当前请求。这与 Spring Boot 中 SpringApplication.run() 启动容器、再由 DispatcherServlet 处理请求是同构的,只不过 PHP 每个请求都走一遍引导。

步骤 2:读懂 Laravel 11 的引导文件

Laravel 10 以前的 app/Http/Kernel.php 在 11 中消失了,配置全部集中在 bootstrap/app.php:

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        // 全局中间件、别名、分组都在这里配
        // $middleware->append(\App\Http\Middleware\TrackRequest::class);
        // $middleware->alias(['role' => \App\Http\Middleware\EnsureRole::class]);
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        // 自定义异常渲染,等价 Spring 的 @ControllerAdvice
    })
    ->create();

health: '/up' 会自动注册一个健康检查路由,供负载均衡探活,相当于 Spring Boot Actuator 的 /actuator/health 简版。

步骤 3:加一个自定义配置项

新建 config/shop.php:

<?php

return [
    // 单笔订单最大金额(分),防止误操作
    'max_order_amount' => (int) env('SHOP_MAX_ORDER_AMOUNT', 5_000_000),

    // 免运费门槛(分)
    'free_shipping_threshold' => 9_900,

    // 支持的支付渠道
    'payment_channels' => ['alipay', 'wechat', 'mock'],

    // 订单号前缀
    'order_no_prefix' => env('SHOP_ORDER_NO_PREFIX', 'NO'),
];

在 .env 中追加:

SHOP_MAX_ORDER_AMOUNT=8000000
SHOP_ORDER_NO_PREFIX=BETA

在路由里读取(注意:代码里应读 config(),不要直接读 env(),因为 config:cache 后 env() 在模块外会返回 null):

// routes/web.php
use Illuminate\Support\Facades\Route;

Route::get('/config-demo', function () {
    return response()->json([
        'max_amount'    => config('shop.max_order_amount'),
        'free_shipping' => config('shop.free_shipping_threshold'),
        'channels'      => config('shop.payment_channels'),
        'prefix'        => config('shop.order_no_prefix'),
    ]);
});

访问 http://127.0.0.1:8000/config-demo 验证。

步骤 4:注册一个全局中间件观察生命周期

生成中间件:

php artisan make:middleware TrackRequest

编写 app/Http/Middleware/TrackRequest.php:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;

class TrackRequest
{
    public function handle(Request $request, Closure $next): Response
    {
        // 前置逻辑:请求进入时执行
        $start = microtime(true);
        Log::info('request in', [
            'method' => $request->method(),
            'path'   => $request->path(),
        ]);

        $response = $next($request); // 把请求交给下一层

        // 后置逻辑:响应返回前执行
        $response->headers->set('X-Request-Duration',
            number_format((microtime(true) - $start) * 1000, 2).'ms');

        return $response;
    }
}

在 bootstrap/app.php 的 withMiddleware 中注册为全局中间件:

->withMiddleware(function (Middleware $middleware): void {
    $middleware->append(\App\Http\Middleware\TrackRequest::class);
})

请求任意页面,然后:

# 查看日志,确认前置逻辑执行
# Windows PowerShell:
Get-Content storage\logs\laravel.log -Tail 5

# 确认响应头
curl -I http://127.0.0.1:8000/
# X-Request-Duration: 12.34ms

步骤 5:看清中间件的「洋葱模型」

请求 ──► TrackRequest 前置
          └─► EncryptCookies 前置
                └─► StartSession 前置
                      └─► 路由/控制器处理
                      ◄─ 响应
                ◄─ StartSession 后置
          ◄─ EncryptCookies 后置
请求 ◄─ TrackRequest 后置

这与 Spring 的 FilterChain 完全一致:chain.doFilter() 之前是前置,之后是后置。理解这张图,你就理解了为什么认证中间件能在控制器之前拦截未登录请求。

讲清楚要点

  1. app/ 放你写的代码,vendor/ 放别人写的代码:业务代码永远不要落到 vendor,config、routes、database 则是框架协作面。
  2. bootstrap/app.php 是 11 版的神经中枢:路由文件挂载、中间件、异常渲染、命令调度(withSchedule)都在这里声明。
  3. 配置分两层:config/*.php 是结构化配置,.env 提供环境差异值。代码只读 config(),env() 只出现在 config/ 文件中。
  4. 服务提供者是启动扩展点:AppServiceProvider 的 register() 只做容器绑定,boot() 在所有提供者注册完后执行,适合做视图 Composer、事件绑定等。它等价于 Spring 的 @Configuration + @PostConstruct。
  5. storage/ 必须可写:storage/framework 放 Session、缓存、编译视图,storage/logs 放日志,storage/app 放用户上传。部署时权限错误几乎都出在这里。
  6. web 路由与 api 路由的区别是中间件不同:web 组带 Session、CSRF 验证;api 组默认无状态、挂 /api 前缀。给前端 App 或第三方调用的接口写 api.php。

你可能会懵的地方

  • Laravel 11 文件怎么这么少?Kernel、Exception Handler 都去哪了? 被框架内部化了。需要定制时通过 bootstrap/app.php 的回调注入,属于「默认零配置、需要时再显式声明」的设计,目的是让新项目噪音更少。

  • Service 层要不要建?为什么官方没有 app/Services? 框架不强制。控制器很薄、模型方法够用时可以不建;一旦业务逻辑跨多个模型或被多个入口(HTTP + 命令 + 队列)复用,就应当建 app/Services,这点和 Spring 项目抽 @Service 的判断标准一样。

  • config:cache 之后为什么 .env 改了没效果? 缓存会把 config 与当时的 env 值合并固化为一个 PHP 文件。改完配置需要 php artisan config:clear(或重新 config:cache)。这也解释了为什么不能在 config/ 外面调用 env()。

  • Facade 和助手函数(如 config()、cache())该用哪个? 两者解析的是同一个容器服务。Laravel 官方两种都认可;团队内统一风格即可。控制器构造函数注入最利于测试,Facade 最简洁。

  • 维护模式怎么用? php artisan down(可带 --secret=xxx 设访问暗号、--retry=60 设 Retry-After),部署时先 down 再更新代码最后 php artisan up。负载均衡探活 /up 在维护模式会返回 503。

错法对照

错法问题正解
在 app/Http/Controllers 之外散落业务代码入口多了无法复用抽到 app/Services 或模型作用域
控制器里直接 env('DB_HOST')配置缓存后返回 null一律 config('database...')
把上传文件存到 public/uploads 手写路径权限与可见性失控用 Storage::disk('public') + storage:link
在 ServiceProvider register() 里查数据库此时依赖可能尚未就绪查库类逻辑放 boot()
修改 bootstrap/cache/*.php 想改配置这是生成物,会被覆盖改源文件后重新生成缓存
给 API 路由挂 web 中间件拿 Session无状态接口被污染API 用 token/sanctum,保持无状态

卡点急救

  1. 新增的配置项读出来是 null:先确认文件名为 config/xxx.php 且返回数组;若跑过缓存,执行 php artisan config:clear;再用 php artisan tinker 输入 config('shop') 直接检查。
  2. 日志写不进去 / 视图编译报错:给 storage 与 bootstrap/cache 写权限。Linux 下常见命令 chown -R www-data:www-data storage bootstrap/cache && chmod -R 775 storage bootstrap/cache。
  3. 改了 bootstrap/app.php 后整个站白屏:多半是闭包内引用了不存在的类或漏写 use。打开 storage/logs/laravel.log 看第一行错误;临时无法定位可先 git diff 逐段回退。

面试常问

  • 请描述 Laravel 的请求生命周期。 请求进入 public/index.php,引入 Composer 自动加载;bootstrap/app.php 创建 Application,注册基础服务提供者并 Boot;Request 穿过全局与路由组中间件(洋葱模型);路由器匹配后由容器调用路由对应的闭包或控制器方法(自动解析构造与方法依赖);返回 Response,逆序穿过中间件后置逻辑,发送给浏览器;应用触发终止回调(如 Session 写入)。按「入口 → 引导 → 中间件 → 路由分发 → 响应 → 终止」六段讲即可。

  • Service Container、Service Provider、Facade 的关系? Container 是绑定与解析对象的容器(IoC);Provider 在启动阶段向容器做绑定与初始化;Facade 提供一个静态代理,从容器里解析服务。对应 Spring:IoC Container、@Configuration/@Bean、以及一个自动注入后暴露的静态句柄。

  • Laravel 为什么推荐配置缓存?有什么坑? config:cache 把数十个配置文件合并为一个数组文件,省去每个请求重复加载与闭包;坑是 env() 只应在 config 内调用,每次部署后必须重新缓存,否则新环境变量不生效。

自测三问

  1. 用户登录状态相关的逻辑(Session、CSRF)在哪一组路由上?对外提供给移动端的 API 应该写哪个文件?为什么?
  2. 一个请求到达控制器之前,至少经过哪三类组件?请按顺序说出来。
  3. 要新增「全站统一的灰度开关」,配置文件、.env、读取代码分别写在哪里?部署时要执行什么缓存命令?

小结一句话

public/index.php 点火、bootstrap/app.php 装配、中间件洋葱包裹路由分发——看懂目录约定和这条生命周期,你就拿到了在 Laravel 项目里自由导航的地图。

延伸

  • Laravel 11 目录结构说明
  • 请求生命周期
  • 服务容器
  • 服务提供者
  • 下一课:路由系统:从 URL 到控制器——我们将详细学习路由参数、命名路由、路由分组与模型绑定。
加载本节练习…
← 上一课Laravel 概览下一课 →路由