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