Menü schliessen
Created: December 28th 2025
Categories: Laravel,  Php
Author: Aleksandar Pantic

Understanding Laravel MVC Structure: A Beginner's Guide with Real Examples

Laravel is one of the most popular PHP frameworks, known for its elegant syntax and powerful features. At its core, Laravel follows the MVC (Model-View-Controller) architectural pattern, which helps developers organize code in a clean and maintainable way. This blog post will introduce you to Laravel's MVC structure, explain how it works, and provide real-world examples to help beginners understand this essential concept.

What is MVC?

MVC stands for Model-View-Controller, a design pattern that separates an application into three interconnected components. This separation helps organize code, making it easier to manage, test, and scale your applications.

  • Model: Represents the data and business logic of your application. It interacts with the database and handles data-related operations.
  • View: Handles the presentation layer - what users see and interact with. It displays data received from the controller.
  • Controller: Acts as a bridge between Model and View. It processes user requests, interacts with the model, and returns the appropriate view.

Why Use MVC in Laravel?

The MVC pattern offers several advantages for web development:

  • Separation of Concerns: Each component has a specific responsibility, making code easier to understand and maintain.
  • Reusability: Models and views can be reused across different parts of your application.
  • Team Collaboration: Developers can work on different components simultaneously without conflicts.
  • Testability: Isolated components are easier to test individually.

Understanding Laravel's MVC Structure

Let's explore each component of Laravel's MVC pattern with practical examples.

The Model

In Laravel, models are stored in the app/Models directory. A model represents a database table and provides methods to interact with that data. Laravel uses Eloquent ORM (Object-Relational Mapping) to make database operations simple and intuitive.

Example: Creating a Product Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = ['name', 'description', 'price', 'stock'];

    // Relationship: A product belongs to a category
    public function category()
    {
        return $this->belongsTo(Category::class);
    }

    // Custom method to check if product is in stock
    public function isInStock()
    {
        return $this->stock > 0;
    }

    // Scope to get only available products
    public function scopeAvailable($query)
    {
        return $query->where('stock', '>', 0);
    }
}

This model handles all product-related data operations. You can easily retrieve, create, update, or delete products without writing raw SQL queries.

The Controller

Controllers are stored in the app/Http/Controllers directory. They handle incoming HTTP requests, process them, and return responses. Controllers use models to retrieve data and pass it to views.

Example: ProductController

<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    // Display all products
    public function index()
    {
        $products = Product::available()->with('category')->get();
        return view('products.index', compact('products'));
    }

    // Show single product
    public function show($id)
    {
        $product = Product::findOrFail($id);
        return view('products.show', compact('product'));
    }

    // Show form to create new product
    public function create()
    {
        return view('products.create');
    }

    // Store new product
    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|max:255',
            'description' => 'required',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0'
        ]);

        Product::create($validated);

        return redirect()->route('products.index')
            ->with('success', 'Product created successfully!');
    }

    // Show form to edit product
    public function edit($id)
    {
        $product = Product::findOrFail($id);
        return view('products.edit', compact('product'));
    }

    // Update product
    public function update(Request $request, $id)
    {
        $product = Product::findOrFail($id);

        $validated = $request->validate([
            'name' => 'required|max:255',
            'description' => 'required',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0'
        ]);

        $product->update($validated);

        return redirect()->route('products.index')
            ->with('success', 'Product updated successfully!');
    }

    // Delete product
    public function destroy($id)
    {
        $product = Product::findOrFail($id);
        $product->delete();

        return redirect()->route('products.index')
            ->with('success', 'Product deleted successfully!');
    }
}

This controller demonstrates CRUD (Create, Read, Update, Delete) operations. Each method has a specific purpose and returns the appropriate view with necessary data.

The View

Views are stored in the resources/views directory. Laravel uses the Blade templating engine, which provides a clean syntax for displaying data and creating reusable layouts.

Example: Product List View (products/index.blade.php)

@extends('layouts.app')

@section('content')
<div class="container">
    <h1>Products</h1>

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

    <a href="{{ route('products.create') }}" class="btn btn-primary mb-3">
        Add New Product
    </a>

    <table class="table">
        <thead>
            <tr>
                <th>Name</th>
                <th>Category</th>
                <th>Price</th>
                <th>Stock</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            @forelse($products as $product)
                <tr>
                    <td>{{ $product->name }}</td>
                    <td>{{ $product->category->name }}</td>
                    <td>${{ number_format($product->price, 2) }}</td>
                    <td>
                        @if($product->isInStock())
                            <span class="badge bg-success">{{ $product->stock }} in stock</span>
                        @else
                            <span class="badge bg-danger">Out of stock</span>
                        @endif
                    </td>
                    <td>
                        <a href="{{ route('products.show', $product->id) }}" class="btn btn-sm btn-info">View</a>
                        <a href="{{ route('products.edit', $product->id) }}" class="btn btn-sm btn-warning">Edit</a>
                        <form action="{{ route('products.destroy', $product->id) }}" method="POST" style="display:inline">
                            @csrf
                            @method('DELETE')
                            <button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure?')">Delete</button>
                        </form>
                    </td>
                </tr>
            @empty
                <tr>
                    <td colspan="5" class="text-center">No products found.</td>
                </tr>
            @endforelse
        </tbody>
    </table>
</div>
@endsection

This view displays a list of products in a table format. Blade directives like @foreach, @if, and {{ }} make it easy to work with data and control structures.

How MVC Works Together: The Request Flow

Understanding how a request flows through Laravel's MVC structure is crucial. Here's what happens when a user visits /products:

  1. Route: The request hits a route defined in routes/web.php:
    Route::get('/products', [ProductController::class, 'index'])->name('products.index');
  2. Controller: The route calls the index method in ProductController.
  3. Model: The controller uses the Product model to fetch data from the database.
  4. View: The controller passes the data to the view and returns it.
  5. Response: The rendered HTML is sent back to the user's browser.

Real-World Example: Blog Application

Let's build a simple blog to see MVC in action. We'll create a system to manage blog posts.

Step 1: Create the Post Model and Migration

php artisan make:model Post -m

This creates a model and migration file. Update the migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->string('author');
            $table->boolean('published')->default(false);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
};

Run the migration: php artisan migrate

Step 2: Define the Post Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'content', 'author', 'published'];

    public function scopePublished($query)
    {
        return $query->where('published', true);
    }
}

Step 3: Create the PostController

php artisan make:controller PostController --resource

The --resource flag creates all CRUD methods automatically.

Step 4: Define Routes

In routes/web.php:

use App\Http\Controllers\PostController;

Route::resource('posts', PostController::class);

This single line creates all necessary routes for CRUD operations.

Benefits of Laravel's MVC Approach

  • Clean Code Organization: Each component has its own directory and purpose.
  • Eloquent ORM: Makes database interactions intuitive and expressive.
  • Blade Templating: Provides powerful yet simple syntax for views.
  • Built-in Validation: Controllers can easily validate user input.
  • Routing System: Simple and expressive route definitions.

Common Beginner Mistakes to Avoid

  • Business Logic in Views: Keep complex logic in models or controllers, not views.
  • Fat Controllers: Don't put too much code in controllers. Extract complex operations to service classes or model methods.
  • Not Using Relationships: Leverage Eloquent relationships instead of manual joins.
  • Ignoring Validation: Always validate user input in controllers before processing.

Conclusion

Laravel's MVC structure provides a solid foundation for building maintainable web applications. By separating concerns into Models, Views, and Controllers, you create code that's easier to understand, test, and scale. Start with simple CRUD operations like the examples shown here, and gradually explore Laravel's more advanced features. The MVC pattern might seem complex at first, but with practice, it becomes a natural and efficient way to organize your code. Happy coding!