Lập trình · 22/09/2026

Laravel 13 Course – Lesson 03: Laravel 13 Project Structure and Request Lifecycle with TaskFlow

Lesson 3 — TaskFlow: learn where code belongs and follow one request before building CRUD. After installing Laravel 13, you have a working skeleton. This lesson adds a deliberately small /taskflow page so you can observe the path without domain tables or authentication.

Cấu trúc dự án và vòng đời request Laravel 13 qua TaskFlow

Lesson 3 — TaskFlow: learn where code belongs and follow one request before building CRUD. After installing Laravel 13, you have a working skeleton. This lesson adds a deliberately small /taskflow page so you can observe the path without domain tables or authentication.

1. Read the project by responsibility

Inspect your actual files before following an older online diagram. This skeleton configures routing in bootstrap/app.php; do not create app/Http/Kernel.php simply because an older tutorial mentions it. The framework still has an internal HTTP kernel, but application configuration is organized differently.

LocationTaskFlow question
routes/web.phpWhich URL opens the overview?
resources/viewsWhich HTML displays the data?
app/Http/ControllersWhere will request coordination move later?
app/ModelsWhich model represents persisted data?
database/migrationsWhat history defines schema changes?
configWhere are operational options read?
bootstrap/app.phpHow are routes, middleware and exceptions configured?
bootstrap/providers.phpWhere are application providers registered?
tests/FeatureHow is HTTP behavior verified?
storageWhere do logs, compiled views and runtime data go?

This skeleton does not have routes/api.php yet. That is not an installation failure, and this lesson does not need API setup. Do not edit vendor to add application features or place .env and databases inside public. A production web server must serve the public directory, not the repository root. See the official directory structure.

2. Follow a request

GET /taskflow
  → public/index.php
  → Composer autoload + bootstrap/app.php
  → framework bootstrap, providers, middleware
  → router selects the matching route
  → route returns a view with data
  → response travels outward through middleware
  → browser receives HTML

This is a conceptual map, not every internal method call. Middleware can return early, for example with a login redirect, so the route callback may never run. Exceptions can also change the path. Providers prepare application services; routes select behavior for particular URLs. Adding a screen does not require editing public/index.php. Compare the official request lifecycle.

3. Add a named route

Keep the welcome route in routes/web.php and append this route. Do not duplicate the use statement if Route is already imported:

use Illuminate\Support\Facades\Route;

Route::get('/taskflow', function () {
    return view('taskflow.overview', [
        'projectName' => 'TaskFlow',
        'milestone' => 'Request lifecycle',
    ]);
})->name('taskflow.overview');

The URL is /taskflow; taskflow.overview is the name used to generate that URL in application code. These are different concepts. This closure passes two fixed strings to a view. It does not query a database, authorize users or list tasks. A later routing lesson will introduce a controller when request coordination needs it.

4. Create the view and track its data

Create resources/views/taskflow/overview.blade.php. The view name taskflow.overview maps to this file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ $projectName }}</title>
</head>
<body>
    <main>
        <h1>{{ $projectName }}</h1>
        <p>Milestone: {{ $milestone }}</p>
        <p>No tasks have been loaded yet.</p>
    </main>
</body>
</html>

The projectName and milestone keys become Blade variables. Double braces escape output before inserting it into HTML. Do not switch to raw HTML output just to suppress a rendering problem. This minimal page needs no Vite asset so you can focus on the data path.

php artisan route:list --path=taskflow -v
php artisan serve --host=127.0.0.1 --port=8000

Open /taskflow and look for TaskFlow and Milestone: Request lifecycle. The route list should show GET|HEAD and the taskflow.overview name. In browser DevTools, inspect the document request: its status, Content-Type and response body show what the server actually returned.

5. Test behavior, not merely file existence

Create tests/Feature/TaskFlowOverviewTest.php:

<?php

namespace Tests\Feature;

use Tests\TestCase;

class TaskFlowOverviewTest extends TestCase
{
    public function test_overview_passes_data_to_the_expected_view(): void
    {
        $this->get(route('taskflow.overview', absolute: false))
            ->assertOk()
            ->assertViewIs('taskflow.overview')
            ->assertViewHas('projectName', 'TaskFlow')
            ->assertSeeText('Request lifecycle');
    }

    public function test_an_unknown_taskflow_route_returns_not_found(): void
    {
        $this->get('/taskflow/not-a-route')->assertNotFound();
    }

    public function test_overview_rejects_an_unsupported_method(): void
    {
        $this->post('/taskflow')->assertStatus(405);
    }
}
php artisan test --filter=TaskFlowOverviewTest
php artisan test

The first test checks the response, view name, data and text. The other tests distinguish an unknown URL (404) from an unsupported HTTP method (405). Laravel normally bypasses CSRF verification during tests, so this POST test does not establish that a browser form has correct CSRF protection. Forms and CSRF have a dedicated later lesson.

Feature tests invoke the application within the testing process; they do not require a running Artisan server. They also do not verify production Nginx, DNS or TLS. A real HTTP request is still needed to check the network path. A passing application test cannot prove every deployment layer works.

6. Diagnose the point where the request breaks

  • Route missing from route:list: check the project, file and withRouting configuration. If local routes were cached, clear them with route:clear and inspect again.
  • View not found: check the directory, dot notation and .blade.php extension, including case sensitivity on Linux.
  • Undefined variable: compare the route's data keys with Blade variable names rather than disabling error reporting.
  • 404: compare the path, method and route table before suspecting the database.
  • 500: read the exception and log instead of adding a catch-all that returns 200.
  • Changes do not appear: confirm the browser URL points to the project you just edited.

7. Exercise

Change the milestone value and update the test expectation. Intentionally misspell the view name, observe the failing test, then restore it. Explain which file selects the URL, which receives data, which renders HTML and why public/index.php remains unchanged.

Navigation: Lesson 2 — Installation · Course roadmap. The next lesson standardizes environment configuration and logging for TaskFlow.

Laravel 13 course navigation

Previous lesson (02) · Next lesson (04) · All 37 lessons

Discussion

Comments 0

Sign in to comment

You need an account to join the discussion and reply to other readers.

Sign inRegister

No comments yet. Be the first to share your thoughts.