TL;DR

Get started with Laravel, PHP's most popular framework. Learn routing, controllers, Blade templates, and Eloquent ORM basics.

Key concepts

  • Laravel tutorial
  • Laravel basics
  • Laravel routing
  • Laravel Eloquent ORM

Laravel Basics

Laravel is the most widely-used PHP framework, known for its elegant syntax and rich ecosystem. It provides tools for routing, database management, authentication, templating, and much more -- all while following modern software design patterns. In this lesson you will learn the core concepts of Laravel: routing, controllers, Blade templates, and Eloquent ORM. These fundamentals will give you the foundation to build full-featured web applications.

What You'll Learn

  • What Laravel provides and why it is popular
  • Routing: mapping URLs to code
  • Controllers: organizing request handling logic
  • Blade templates: building dynamic HTML views
  • Eloquent ORM: working with databases using models
  • Middleware: filtering HTTP requests
  • Artisan: the command-line tool for Laravel

Routing

Routes define how your application responds to HTTP requests. Laravel routes are defined in routes/web.php for web pages and routes/api.php for APIs:

<?php

// Laravel routes are defined using the Route facade
// These examples show common routing patterns

// Basic routes
// Route::get('/welcome', function () {
//     return 'Welcome to Laravel!';
// });

// Route with parameter
// Route::get('/user/{id}', function (int $id) {
//     return "User profile for ID: {$id}";
// });

// Simulating Laravel-style routing to demonstrate concepts
class Route {
    private static array $routes = [];

    public static function get(string $uri, callable|array $action): void {
        self::$routes['GET'][$uri] = $action;
    }

    public static function post(string $uri, callable|array $action): void {
        self::$routes['POST'][$uri] = $action;
    }

    public static function resolve(string $method, string $uri): string {
        $action = self::$routes[$method][$uri] ?? null;
        if ($action === null) {
            return "404 Not Found: {$method} {$uri}";
        }
        if (is_callable($action)) {
            return $action();
        }
        return "Route matched: {$method} {$uri}";
    }
}

// Define routes (similar to Laravel syntax)
Route::get('/', fn() => 'Welcome to the homepage!');
Route::get('/about', fn() => 'About our company');
Route::get('/contact', fn() => 'Contact page');
Route::post('/contact', fn() => 'Form submitted successfully');

// Simulate requests
$requests = [
    ['GET', '/'],
    ['GET', '/about'],
    ['GET', '/contact'],
    ['POST', '/contact'],
    ['GET', '/missing'],
];

foreach ($requests as [$method, $uri]) {
    $response = Route::resolve($method, $uri);
    echo "{$method} {$uri} => {$response}\n";
}

In a real Laravel application, you use the Route facade from the framework. Routes can have parameters, middleware, names, and can be grouped by prefix or namespace. The routing system is one of the first things you interact with when building a Laravel app.

Controllers

Controllers group related request handling logic into classes. Instead of defining all logic in route closures, you organize it into controller methods:

<?php

// Simulating Laravel's controller pattern

abstract class Controller {
    // Base controller class (Laravel provides this)
}

class PostController extends Controller {
    private array $posts = [
        1 => ['title' => 'Getting Started with Laravel', 'body' => 'Laravel is a PHP framework...', 'author' => 'Alice'],
        2 => ['title' => 'Eloquent ORM Guide', 'body' => 'Eloquent makes database work easy...', 'author' => 'Bob'],
        3 => ['title' => 'Blade Templates', 'body' => 'Blade is Laravel\'s templating engine...', 'author' => 'Charlie'],
    ];

    public function index(): string {
        $output = "All Posts:\n";
        foreach ($this->posts as $id => $post) {
            $output .= "  [{$id}] {$post['title']} by {$post['author']}\n";
        }
        return $output;
    }

    public function show(int $id): string {
        if (!isset($this->posts[$id])) {
            return "404: Post not found";
        }
        $post = $this->posts[$id];
        return "Post #{$id}\n  Title: {$post['title']}\n  By: {$post['author']}\n  Content: {$post['body']}\n";
    }

    public function store(array $data): string {
        // Validation would go here in Laravel
        $id = count($this->posts) + 1;
        $this->posts[$id] = $data;
        return "Created post #{$id}: {$data['title']}";
    }

    public function destroy(int $id): string {
        if (!isset($this->posts[$id])) {
            return "404: Post not found";
        }
        $title = $this->posts[$id]['title'];
        unset($this->posts[$id]);
        return "Deleted: {$title}";
    }
}

// In Laravel, routes point to controller methods:
// Route::get('/posts', [PostController::class, 'index']);
// Route::get('/posts/{id}', [PostController::class, 'show']);
// Route::post('/posts', [PostController::class, 'store']);
// Route::delete('/posts/{id}', [PostController::class, 'destroy']);

$controller = new PostController();

echo $controller->index();
echo "\n";
echo $controller->show(2);
echo "\n";
echo $controller->store([
    'title' => 'New Post',
    'body' => 'This is a new post...',
    'author' => 'Diana',
]) . "\n";
echo "\n";
echo $controller->destroy(1) . "\n";

Laravel generates controller files using the Artisan command: php artisan make:controller PostController --resource. Resource controllers automatically include methods for index, create, store, show, edit, update, and destroy.

Blade Templates

Blade is Laravel's templating engine. It compiles to plain PHP but provides a clean, expressive syntax with template inheritance and components:

<?php

// Blade template syntax examples (these would be .blade.php files)
// This demonstrates the concepts; actual Blade needs the Laravel framework

class BladeDemo {
    public static function showVariables(): void {
        echo "=== Blade Variable Syntax ===\n\n";

        // {{ $variable }} - escaped output (XSS safe)
        // {!! $variable !!} - raw output (use carefully)
        // @if / @elseif / @else / @endif
        // @foreach / @endforeach
        // @extends / @section / @yield

        $template = <<<'BLADE'
        <!-- resources/views/posts/show.blade.php -->

        @extends('layouts.app')

        @section('title', 'View Post')

        @section('content')
            <h1>{{ $post->title }}</h1>
            <p>By {{ $post->author }} on {{ $post->created_at }}</p>

            <div class="content">
                {!! $post->html_content !!}
            </div>

            @if($post->tags->isNotEmpty())
                <div class="tags">
                    @foreach($post->tags as $tag)
                        <span class="tag">{{ $tag->name }}</span>
                    @endforeach
                </div>
            @endif

            @auth
                <a href="{{ route('posts.edit', $post) }}">Edit Post</a>
            @endauth
        @endsection
        BLADE;

        echo $template . "\n";
    }

    public static function showLayout(): void {
        echo "\n=== Blade Layout Syntax ===\n\n";

        $layout = <<<'BLADE'
        <!-- resources/views/layouts/app.blade.php -->

        <!DOCTYPE html>
        <html>
        <head>
            <title>@yield('title', 'My App')</title>
        </head>
        <body>
            <nav>
                <a href="/">Home</a>
                <a href="/posts">Blog</a>
                @auth
                    <a href="/dashboard">Dashboard</a>
                @else
                    <a href="/login">Login</a>
                @endauth
            </nav>

            <main>
                @if(session('success'))
                    <div class="alert-success">
                        {{ session('success') }}
                    </div>
                @endif

                @yield('content')
            </main>

            <footer>
                &copy; {{ date('Y') }} My Application
            </footer>
        </body>
        </html>
        BLADE;

        echo $layout . "\n";
    }
}

BladeDemo::showVariables();
BladeDemo::showLayout();

echo "\n=== Key Blade Directives ===\n";
$directives = [
    '{{ $var }}' => 'Echo escaped (XSS safe)',
    '{!! $var !!}' => 'Echo raw HTML',
    '@if / @else / @endif' => 'Conditionals',
    '@foreach / @endforeach' => 'Loops',
    '@extends(\'layout\')' => 'Inherit a layout',
    '@section / @yield' => 'Define and render sections',
    '@include(\'partial\')' => 'Include a sub-view',
    '@auth / @guest' => 'Authentication checks',
    '@csrf' => 'CSRF token field',
];

foreach ($directives as $syntax => $description) {
    echo "  {$syntax} - {$description}\n";
}

Blade templates use {{ }} for escaped output (safe from XSS attacks) and {!! !!} for raw HTML. The layout system with @extends, @section, and @yield lets you define a base template and override sections in child views.

Eloquent ORM

Eloquent is Laravel's database layer. Each database table has a corresponding Model class that you use to interact with the data:

<?php

// Simulating Eloquent-style model operations

class Model {
    protected static string $table = '';
    protected array $attributes = [];
    private static array $storage = [];

    public function __construct(array $attributes = []) {
        $this->attributes = $attributes;
    }

    public function __get(string $name): mixed {
        return $this->attributes[$name] ?? null;
    }

    public function __set(string $name, mixed $value): void {
        $this->attributes[$name] = $value;
    }

    public static function create(array $data): static {
        $instance = new static($data);
        $instance->attributes['id'] = count(self::$storage[static::$table] ?? []) + 1;
        self::$storage[static::$table][] = $instance->attributes;
        return $instance;
    }

    public static function all(): array {
        return self::$storage[static::$table] ?? [];
    }

    public static function find(int $id): ?static {
        foreach (self::$storage[static::$table] ?? [] as $row) {
            if ($row['id'] === $id) {
                return new static($row);
            }
        }
        return null;
    }

    public static function where(string $column, mixed $value): array {
        $results = [];
        foreach (self::$storage[static::$table] ?? [] as $row) {
            if (($row[$column] ?? null) === $value) {
                $results[] = new static($row);
            }
        }
        return $results;
    }
}

class Post extends Model {
    protected static string $table = 'posts';
}

class User extends Model {
    protected static string $table = 'users';
}

// Eloquent-style usage
$alice = User::create(['name' => 'Alice', 'email' => 'alice@example.com', 'role' => 'admin']);
$bob = User::create(['name' => 'Bob', 'email' => 'bob@example.com', 'role' => 'author']);

Post::create(['title' => 'First Post', 'user_id' => 1, 'status' => 'published']);
Post::create(['title' => 'Draft Post', 'user_id' => 1, 'status' => 'draft']);
Post::create(['title' => 'Bob\'s Post', 'user_id' => 2, 'status' => 'published']);

// Query examples (real Eloquent syntax)
echo "All users:\n";
foreach (User::all() as $user) {
    echo "  [{$user['id']}] {$user['name']} ({$user['email']})\n";
}

echo "\nFind user #1:\n";
$user = User::find(1);
echo "  {$user->name} - {$user->email}\n";

echo "\nPublished posts:\n";
$published = Post::where('status', 'published');
foreach ($published as $post) {
    echo "  [{$post->id}] {$post->title}\n";
}

echo "\n--- Real Eloquent would look like ---\n";
echo "  \$users = User::all();\n";
echo "  \$post = Post::find(1);\n";
echo "  \$published = Post::where('status', 'published')->get();\n";
echo "  \$recent = Post::latest()->take(5)->get();\n";
echo "  \$user->posts()->create(['title' => 'New Post']);\n";

In real Laravel, Eloquent handles relationships (hasMany, belongsTo, manyToMany), eager loading, scopes, soft deletes, events, and much more. The API is fluent and expressive, making complex database operations readable.

Middleware

Middleware filters HTTP requests before they reach your controller. Common uses include authentication checks, logging, and CORS headers:

<?php

// Simulating Laravel middleware pattern

interface Middleware {
    public function handle(array $request, callable $next): array;
}

class AuthMiddleware implements Middleware {
    public function handle(array $request, callable $next): array {
        if (empty($request['user'])) {
            return ['status' => 401, 'body' => 'Unauthorized: Please log in'];
        }
        return $next($request);
    }
}

class LogMiddleware implements Middleware {
    public function handle(array $request, callable $next): array {
        $method = $request['method'] ?? 'GET';
        $uri = $request['uri'] ?? '/';
        echo "[LOG] {$method} {$uri}\n";
        $response = $next($request);
        echo "[LOG] Response: {$response['status']}\n";
        return $response;
    }
}

class RateLimitMiddleware implements Middleware {
    private static array $requests = [];

    public function handle(array $request, callable $next): array {
        $ip = $request['ip'] ?? '0.0.0.0';
        self::$requests[$ip] = (self::$requests[$ip] ?? 0) + 1;

        if (self::$requests[$ip] > 3) {
            return ['status' => 429, 'body' => 'Too many requests'];
        }

        return $next($request);
    }
}

// Pipeline: run request through middleware stack
function pipeline(array $request, array $middlewares, callable $handler): array {
    $next = $handler;
    foreach (array_reverse($middlewares) as $middleware) {
        $currentNext = $next;
        $next = fn($req) => $middleware->handle($req, $currentNext);
    }
    return $next($request);
}

$middlewares = [new LogMiddleware(), new AuthMiddleware()];
$handler = fn($req) => ['status' => 200, 'body' => "Hello, {$req['user']}!"];

// Authenticated request
echo "--- Authenticated Request ---\n";
$response = pipeline(['method' => 'GET', 'uri' => '/dashboard', 'user' => 'Alice'], $middlewares, $handler);
echo "Result: {$response['body']}\n\n";

// Unauthenticated request
echo "--- Unauthenticated Request ---\n";
$response = pipeline(['method' => 'GET', 'uri' => '/dashboard', 'user' => ''], $middlewares, $handler);
echo "Result: {$response['body']}\n";

In Laravel, middleware is registered in app/Http/Kernel.php and can be applied globally, to route groups, or to individual routes. The auth middleware is the most commonly used, protecting routes from unauthenticated users.

Key Takeaways

  • Laravel provides routing, controllers, templates, ORM, and middleware out of the box
  • Routes map URLs and HTTP methods to controller actions or closures
  • Controllers organize request handling logic into organized classes
  • Blade templates provide safe output escaping, layout inheritance, and directives
  • Eloquent ORM lets you interact with databases using expressive model classes
  • Middleware filters requests before they reach your application logic
  • Artisan CLI (php artisan) generates code, runs migrations, and manages your app
  • Install Laravel with composer create-project laravel/laravel my-app

Next Steps

You now understand Laravel's core building blocks: routing, controllers, Blade templates, Eloquent ORM, and middleware. These are the tools professional PHP developers use daily.

Modern web applications rarely serve only HTML pages. Mobile apps, single-page frontends, and third-party integrations all need a structured way to exchange data, and that means APIs. API development teaches you how to design REST endpoints, return JSON responses, validate input, handle errors with proper HTTP status codes, and structure your responses consistently. The patterns you learned here with Laravel controllers and Eloquent carry directly into API development.

Continue to API Development -->

Pro Tip: Use php artisan make:model Post -mcr to generate a model, migration, and resource controller all at once. Learn the Artisan commands early -- they save a tremendous amount of time. Run php artisan list to see all available commands, and php artisan help <command> for detailed usage of any specific command.