Skip to content

Meeting 12 - MVC Concept in Laravel

Goal: Understand Laravel's Model–View–Controller (MVC) architecture: how a request flows through the framework, where each type of code lives, and a minimal example that ties the pieces together.

1. What is MVC?

MVC (Model–View–Controller) is an architectural pattern that separates responsibilities:

  • Model: Data representation + business logic (Eloquent ORM: relationships, queries, accessors/mutators, etc.).
  • View: Presentation layer (Blade templates) that renders HTML returned to the user (or components/fragments reused across pages).
  • Controller: Coordinates the request: receives input, calls the Model, chooses the View (or returns JSON / redirect / file response).

This separation improves readability, testability, scalability, and teamwork velocity.

2. Request Flow in Laravel

  1. Browser (or API client) sends an HTTP request.
  2. The single entry point public/index.php boots the framework (service providers, environment, autoload, etc.).
  3. The router matches the URL + HTTP method against definitions in routes/web.php (HTML) or routes/api.php (API/JSON).
  4. The matched route invokes a Controller method (or a closure / invokable class / middleware pipeline).
  5. The Controller interacts with Models (Eloquent) to read or persist data.
  6. The Controller returns a response: a Blade view, JSON, redirect, file, stream, etc.
  7. The HTTP response is sent back to the client.

3. Diagram

MVC Diagram

4. Relevant Folders

text
app/
  Models/            # Eloquent models (User, Article, etc.)
  Http/
    Controllers/     # Controllers (ArticleController, Auth\LoginController, ...)
resources/
  views/             # Blade templates (*.blade.php)
routes/
  web.php            # Web routes (HTML responses)
  api.php            # API routes (stateless / JSON)
database/
  migrations/        # Table structure definitions
  seeders/           # Initial or sample data population
public/
  index.php          # Front controller (entry point)
app/
  Models/            # Eloquent models (User, Article, etc.)
  Http/
    Controllers/     # Controllers (ArticleController, Auth\LoginController, ...)
resources/
  views/             # Blade templates (*.blade.php)
routes/
  web.php            # Web routes (HTML responses)
  api.php            # API routes (stateless / JSON)
database/
  migrations/        # Table structure definitions
  seeders/           # Initial or sample data population
public/
  index.php          # Front controller (entry point)

5. Minimal Example: Article Listing & Create

We will build only the essential pieces to illustrate how MVC connects: show a paginated list and create new articles.

5.1 Migration

Generate the migration:

bash
php artisan make:migration create_articles_table
php artisan make:migration create_articles_table

Example content (file in database/migrations/xxxx_xx_xx_xxxxxx_create_articles_table.php):

php
public function up(): void
{
    Schema::create('articles', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('body');
        $table->timestamps();
    });
}
public function up(): void
{
    Schema::create('articles', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('body');
        $table->timestamps();
    });
}

Run it:

bash
php artisan migrate
php artisan migrate

5.2 Model

bash
php artisan make:model Article
php artisan make:model Article

app/Models/Article.php:

php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'body'];
}
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'body'];
}

5.3 Controller

bash
php artisan make:controller ArticleController
php artisan make:controller ArticleController

app/Http/Controllers/ArticleController.php:

php
namespace App\Http\Controllers;

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

class ArticleController extends Controller
{
    public function index()
    {
        $articles = Article::latest()->paginate(5); // fetch data from DB

        return view('articles.index', compact('articles')); // pass to Blade view
    }

    public function create()
    {
        return view('articles.create');
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'title' => 'required|min:3',
            'body'  => 'required|min:10'
        ]);

        Article::create($data); // persist through Model

        return redirect()->route('articles.index')->with('success', 'Article created.');
    }
}
namespace App\Http\Controllers;

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

class ArticleController extends Controller
{
    public function index()
    {
        $articles = Article::latest()->paginate(5); // fetch data from DB

        return view('articles.index', compact('articles')); // pass to Blade view
    }

    public function create()
    {
        return view('articles.create');
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'title' => 'required|min:3',
            'body'  => 'required|min:10'
        ]);

        Article::create($data); // persist through Model

        return redirect()->route('articles.index')->with('success', 'Article created.');
    }
}

5.4 Routes

Edit routes/web.php:

php
use App\Http\Controllers\ArticleController;

Route::get('/articles', [ArticleController::class, 'index'])->name('articles.index');
Route::get('/articles/create', [ArticleController::class, 'create'])->name('articles.create');
Route::post('/articles', [ArticleController::class, 'store'])->name('articles.store');
use App\Http\Controllers\ArticleController;

Route::get('/articles', [ArticleController::class, 'index'])->name('articles.index');
Route::get('/articles/create', [ArticleController::class, 'create'])->name('articles.create');
Route::post('/articles', [ArticleController::class, 'store'])->name('articles.store');

(Full RESTful alternative: Route::resource('articles', ArticleController::class);)

5.5 Views (Blade)

resources/views/articles/index.blade.php:

blade
@extends('layouts.app')

@section('content')
<h1>Articles</h1>
<a href="{{ route('articles.create') }}">+ New Article</a>

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

@foreach($articles as $article)
  <article style="margin-bottom:1rem;">
    <h2>{{ $article->title }}</h2>
    <p>{{ Str::limit($article->body, 120) }}</p>
  <small>Published: {{ $article->created_at->diffForHumans() }}</small>
  </article>
@endforeach

{{ $articles->links() }}
@endsection
@extends('layouts.app')

@section('content')
<h1>Articles</h1>
<a href="{{ route('articles.create') }}">+ New Article</a>

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

@foreach($articles as $article)
  <article style="margin-bottom:1rem;">
    <h2>{{ $article->title }}</h2>
    <p>{{ Str::limit($article->body, 120) }}</p>
  <small>Published: {{ $article->created_at->diffForHumans() }}</small>
  </article>
@endforeach

{{ $articles->links() }}
@endsection

resources/views/articles/create.blade.php:

blade
@extends('layouts.app')

@section('content')
<h1>Create Article</h1>
<form method="POST" action="{{ route('articles.store') }}">
  @csrf
  <div>
  <label>Title</label>
    <input type="text" name="title" value="{{ old('title') }}" />
    @error('title') <small style="color:red;">{{ $message }}</small> @enderror
  </div>
  <div>
  <label>Body</label>
    <textarea name="body" rows="6">{{ old('body') }}</textarea>
    @error('body') <small style="color:red;">{{ $message }}</small> @enderror
  </div>
  <button type="submit">Save</button>
</form>
@endsection
@extends('layouts.app')

@section('content')
<h1>Create Article</h1>
<form method="POST" action="{{ route('articles.store') }}">
  @csrf
  <div>
  <label>Title</label>
    <input type="text" name="title" value="{{ old('title') }}" />
    @error('title') <small style="color:red;">{{ $message }}</small> @enderror
  </div>
  <div>
  <label>Body</label>
    <textarea name="body" rows="6">{{ old('body') }}</textarea>
    @error('body') <small style="color:red;">{{ $message }}</small> @enderror
  </div>
  <button type="submit">Save</button>
</form>
@endsection

5.6 Simple Layout (Optional)

resources/views/layouts/app.blade.php:

blade
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Mini Blog</title>
  <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css" />
</head>
<body>
  <nav>
  <a href="/articles">Articles</a>
  <a href="/articles/create">Create</a>
  </nav>
  <main>
    @yield('content')
  </main>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Mini Blog</title>
  <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css" />
</head>
<body>
  <nav>
  <a href="/articles">Articles</a>
  <a href="/articles/create">Create</a>
  </nav>
  <main>
    @yield('content')
  </main>
</body>
</html>

6. Component Roles (Summary)

ComponentExampleResponsibility
Route/articlesMaps URL + method to controller action
ControllerArticleController@indexOrchestrates data fetch & selects view
ModelArticleEncapsulates table articles, queries & business rules
Migrationcreate_articles_tableDefines database structure
Viewarticles/index.blade.phpRenders HTML output

7. Quick Best Practices

  • Keep controllers thin: move heavy logic to Models, Query Objects, or dedicated Services.
  • Prefer resource controllers for conventional CRUD routing.
  • Extract validation into Form Request classes (php artisan make:request).
  • Leverage Eloquent relationships (hasMany, belongsTo, etc.) for readable queries.
  • Reuse UI via Blade components / includes / layouts.

8. CRUD Flow (Create & List)

GET /articles        -> route -> controller@index -> Article::latest() -> Blade view -> HTML response
GET /articles/create -> route -> controller@create -> Blade form view
POST /articles       -> route -> controller@store -> validate -> Article::create() -> redirect
GET /articles        -> route -> controller@index -> Article::latest() -> Blade view -> HTML response
GET /articles/create -> route -> controller@create -> Blade form view
POST /articles       -> route -> controller@store -> validate -> Article::create() -> redirect