PHP MVC Architecture

This article explains PHP MVC architecture in detail, including models, views, controllers, routing, folder structure, database layer, request handling, validation, and how MVC helps organize PHP applications.

Jun 10, 2026
PHP MVC Architecture

PHP MVC Architecture

MVC is one of the most important architectural patterns in web development. It helps developers organize code into clear layers instead of mixing database queries, HTML, validation, and business logic in the same file.

In PHP, MVC is commonly used in modern frameworks such as Laravel, Symfony, CodeIgniter, and many custom applications. Understanding MVC helps developers write cleaner, more maintainable, and more scalable PHP projects.

This article explains PHP MVC architecture in detail, including the meaning of MVC, models, views, controllers, routing, folder structure, database layer, request handling, validation, and how MVC works in real PHP applications.

What Is MVC?

MVC stands for Model, View, and Controller. It is a software architecture pattern that separates an application into three main parts.

  • Model: handles data, database operations, and business rules.

  • View: handles the user interface and displays HTML output.

  • Controller: receives requests, coordinates logic, and returns responses.

The main goal of MVC is separation of concerns. Each part has a specific responsibility. This makes the project easier to understand, test, maintain, and extend.

Without MVC, a PHP file may contain HTML, SQL queries, validation, redirects, session checks, and business logic all mixed together. This may work for small scripts, but it becomes difficult to manage as the project grows.

Why MVC Is Important in PHP

PHP is flexible and allows developers to write code in many ways. Beginners often start by writing PHP directly inside HTML files. This is fine for learning basic syntax, but larger applications need better organization.

MVC is important because it helps solve common problems in growing PHP projects:

  • It separates database logic from HTML output.

  • It keeps request handling inside controllers.

  • It makes views cleaner and easier to edit.

  • It reduces repeated code.

  • It improves maintainability.

  • It makes the project easier to scale.

  • It prepares developers for frameworks like Laravel and Symfony.

When a project follows MVC, developers can work on different parts more easily. For example, one developer can update the view design while another developer works on database logic.

Model in PHP MVC

The model is responsible for data and business rules. In a PHP application, models commonly communicate with the database using PDO, query builders, or ORM tools.

For example, in a blog application, a Post model may be responsible for getting posts, finding one post, creating a new post, updating a post, and deleting a post.

<?php
class Post
{
    private PDO $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function all(): array
    {
        $statement = $this->pdo->query("
            SELECT id, title, content, created_at
            FROM posts
            ORDER BY id DESC
        ");

        return $statement->fetchAll(PDO::FETCH_ASSOC);
    }

    public function find(int $id): ?array
    {
        $statement = $this->pdo->prepare("
            SELECT id, title, content, created_at
            FROM posts
            WHERE id = :id
            LIMIT 1
        ");

        $statement->execute([
            "id" => $id
        ]);

        $post = $statement->fetch(PDO::FETCH_ASSOC);

        return $post ?: null;
    }
}
?>

This model focuses only on data operations. It does not print HTML and does not directly control the page layout.

In larger applications, models may also include relationships, validation rules, business methods, and domain logic. In frameworks such as Laravel, models are often connected to database tables through an ORM called Eloquent.

View in PHP MVC

The view is responsible for displaying data to the user. It usually contains HTML with small PHP expressions for printing variables, loops, and conditions.

For example, a view file that displays a list of posts may look like this:

<h1>Blog Posts</h1>

<?php foreach ($posts as $post): ?>
    <article>
        <h2><?php echo htmlspecialchars($post["title"], ENT_QUOTES, "UTF-8"); ?></h2>
        <p><?php echo htmlspecialchars($post["content"], ENT_QUOTES, "UTF-8"); ?></p>
    </article>
<?php endforeach; ?>

The view should focus on presentation. It should not contain database queries or complex business logic.

Views are useful because designers and frontend developers can work with the HTML structure without needing to understand every detail of the database layer.

In Laravel, views are usually written using Blade templates. In pure PHP MVC, normal PHP files can be used as templates.

Controller in PHP MVC

The controller receives the request, calls the required model methods, prepares data, and loads the correct view.

For example, a PostController may handle showing a list of posts and showing a single post.

<?php
class PostController
{
    private Post $postModel;

    public function __construct(Post $postModel)
    {
        $this->postModel = $postModel;
    }

    public function index(): void
    {
        $posts = $this->postModel->all();

        require __DIR__ . "/../views/posts/index.php";
    }

    public function show(int $id): void
    {
        $post = $this->postModel->find($id);

        if (!$post) {
            http_response_code(404);
            echo "Post not found.";
            return;
        }

        require __DIR__ . "/../views/posts/show.php";
    }
}
?>

The controller does not write SQL directly in this example. It asks the model for data, then sends that data to the view.

Controllers are useful for organizing application behavior. They can handle form submissions, redirects, validation, authentication checks, and API responses.

Routing in PHP MVC

Routing connects URLs to controller methods. Instead of each PHP file being directly opened by the browser, the application can route requests through a central entry point.

For example:

  • /posts can call PostController@index.

  • /posts/show?id=5 can call PostController@show.

  • /posts/create can call PostController@create.

  • /posts/store can call PostController@store.

A very simple router can be written using the request URI.

<?php
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);

if ($uri === "/posts") {
    $controller->index();
} elseif ($uri === "/posts/show") {
    $id = (int) ($_GET["id"] ?? 0);
    $controller->show($id);
} else {
    http_response_code(404);
    echo "Page not found.";
}
?>

This is a basic routing example. Real routers are more advanced and can handle route parameters, HTTP methods, middleware, named routes, and grouped routes.

Frameworks such as Laravel provide powerful routing systems, but understanding a simple router helps you understand what happens behind the scenes.

Front Controller Pattern

Many MVC applications use the front controller pattern. This means that all requests enter the application through one main file, usually public/index.php.

The front controller is responsible for loading configuration, starting the application, registering routes, and dispatching the request to the correct controller.

A basic front controller may look like this:

<?php
require __DIR__ . "/../config/database.php";
require __DIR__ . "/../app/Models/Post.php";
require __DIR__ . "/../app/Controllers/PostController.php";

$postModel = new Post($pdo);
$controller = new PostController($postModel);

$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);

if ($uri === "/posts") {
    $controller->index();
} else {
    http_response_code(404);
    echo "Page not found.";
}
?>

The front controller gives the application one controlled entry point instead of exposing many internal PHP files to the browser.

This structure is common in modern frameworks. Laravel, Symfony, and many other frameworks use a public entry point that starts the application.

PHP MVC Folder Structure

A simple PHP MVC project can be organized into folders for models, views, controllers, configuration, and public files.

project/
├── app/
│   ├── Controllers/
│   │   └── PostController.php
│   ├── Models/
│   │   └── Post.php
│   └── Views/
│       └── posts/
│           ├── index.php
│           └── show.php
├── config/
│   └── database.php
├── public/
│   └── index.php
└── routes/
    └── web.php

The app folder contains the main application code. The Controllers folder contains request-handling classes. The Models folder contains data-related classes. The Views folder contains templates.

The config folder stores configuration files such as database connection settings. The public folder is the web-accessible folder and contains the main entry point.

This structure keeps the project organized and prevents internal application files from being directly accessed by users.

Database Layer in MVC

The database layer is usually handled by models or repository classes. Its job is to communicate with the database and return data to the controller.

A database configuration file may look like this:

<?php
$pdo = new PDO(
    "mysql:host=localhost;dbname=php_mvc;charset=utf8mb4",
    "root",
    ""
);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
?>

The model can use this PDO connection to run prepared statements.

<?php
class User
{
    public function __construct(private PDO $pdo)
    {
    }

    public function findByEmail(string $email): ?array
    {
        $statement = $this->pdo->prepare("
            SELECT id, name, email
            FROM users
            WHERE email = :email
            LIMIT 1
        ");

        $statement->execute([
            "email" => $email
        ]);

        $user = $statement->fetch();

        return $user ?: null;
    }
}
?>

Keeping database logic inside models or repositories makes controllers cleaner and easier to read.

Request Handling in MVC

Request handling means receiving input from the browser or API client. This input may come from forms, query strings, JSON requests, uploaded files, cookies, or sessions.

In MVC, the controller usually receives the request data and decides what to do with it.

<?php
class ContactController
{
    public function store(): void
    {
        $name = trim($_POST["name"] ?? "");
        $email = trim($_POST["email"] ?? "");
        $message = trim($_POST["message"] ?? "");

        if ($name === "" || !filter_var($email, FILTER_VALIDATE_EMAIL) || $message === "") {
            echo "Invalid contact form.";
            return;
        }

        echo "Message received.";
    }
}
?>

In a larger project, request validation may be moved into a separate validator class or request class. This keeps controllers smaller and more focused.

Frameworks such as Laravel provide request classes and validation rules to organize this process.

Validation in MVC

Validation is the process of checking whether input data is acceptable. In MVC applications, validation may happen inside the controller, model, service, or a separate validation class.

For simple projects, validation inside the controller may be enough. For larger projects, separating validation makes the code cleaner.

<?php
class PostValidator
{
    public function validate(array $data): array
    {
        $errors = [];

        $title = trim($data["title"] ?? "");
        $content = trim($data["content"] ?? "");

        if ($title === "") {
            $errors["title"] = "Title is required.";
        }

        if (strlen($title) > 200) {
            $errors["title"] = "Title must not exceed 200 characters.";
        }

        if ($content === "") {
            $errors["content"] = "Content is required.";
        }

        return $errors;
    }
}
?>

The controller can then use the validator:

<?php
$validator = new PostValidator();

$errors = $validator->validate($_POST);

if (count($errors) > 0) {
    require __DIR__ . "/../views/posts/create.php";
    return;
}
?>

This keeps validation logic reusable and easier to test.

Rendering Views

Rendering a view means loading a template and passing data to it. In pure PHP, a simple view renderer can be created using extract() and require.

<?php
function view(string $path, array $data = []): void
{
    extract($data);

    require __DIR__ . "/../app/Views/" . $path . ".php";
}
?>

The controller can call the view helper like this:

<?php
public function index(): void
{
    $posts = $this->postModel->all();

    view("posts/index", [
        "posts" => $posts
    ]);
}
?>

Then the view file can access the $posts variable.

<?php foreach ($posts as $post): ?>
    <h2><?php echo htmlspecialchars($post["title"], ENT_QUOTES, "UTF-8"); ?></h2>
<?php endforeach; ?>

A view renderer is a small but useful improvement because it creates a consistent way to load templates.

Layouts in PHP MVC

Most websites have shared layout parts such as header, navigation, and footer. MVC applications often use layouts to avoid repeating the same HTML in every view.

A simple layout file may contain the main HTML structure:

<!DOCTYPE html>
<html>
<head>
    <title><?php echo htmlspecialchars($title ?? "PHP MVC", ENT_QUOTES, "UTF-8"); ?></title>
</head>
<body>
    <header>
        <nav>
            <a href="/posts">Posts</a>
        </nav>
    </header>

    <main>
        <?php echo $content; ?>
    </main>

    <footer>
        <p>PHP MVC Application</p>
    </footer>
</body>
</html>

More advanced template systems use output buffering to inject page content into the layout.

<?php
ob_start();

require __DIR__ . "/posts/index.php";

$content = ob_get_clean();

require __DIR__ . "/layout.php";
?>

Layouts help keep the interface consistent across pages and reduce duplicated HTML.

MVC and CRUD

CRUD applications are a good way to understand MVC. A posts module can have a model, controller, and views for listing, creating, editing, and deleting posts.

A basic PostController may contain methods like:

  • index() to list posts.

  • show() to show one post.

  • create() to display a form.

  • store() to save a new post.

  • edit() to display an edit form.

  • update() to update a post.

  • destroy() to delete a post.

The model handles the database operations, the controller coordinates the request, and the view displays forms and data.

This structure is very similar to how Laravel controllers and resources work.

MVC and REST API

MVC can also be used for REST APIs. In this case, the controller returns JSON instead of loading an HTML view.

<?php
class ApiPostController
{
    public function __construct(private Post $postModel)
    {
    }

    public function index(): void
    {
        header("Content-Type: application/json");

        echo json_encode([
            "success" => true,
            "data" => $this->postModel->all()
        ]);
    }
}
?>

For API controllers, the model still handles data, but the response is usually JSON. This makes MVC useful for both traditional server-rendered websites and API-based applications.

A REST API MVC structure may have separate controllers such as PostApiController, UserApiController, and AuthApiController.

Benefits of PHP MVC Architecture

MVC provides several practical benefits for PHP applications.

  • Cleaner code: each part has a clear responsibility.

  • Better organization: files are grouped by purpose.

  • Reusability: models and services can be reused in different controllers.

  • Maintainability: changes are easier because logic is separated.

  • Scalability: the structure can grow with the project.

  • Team collaboration: backend and frontend work can be separated more clearly.

  • Framework readiness: MVC prepares developers for Laravel, Symfony, and similar frameworks.

For small scripts, MVC may feel like extra work. But for applications that will grow, MVC helps prevent messy code and repeated logic.

Common Mistakes in PHP MVC

Beginners sometimes misunderstand MVC and place code in the wrong layer. This can reduce the benefits of the pattern.

Common mistakes include:

  • Writing SQL queries directly inside views.

  • Putting too much business logic inside controllers.

  • Using models only as empty data containers.

  • Mixing HTML output inside models.

  • Skipping validation or placing it inconsistently.

  • Exposing internal application files publicly.

  • Creating very large controllers with too many responsibilities.

A good MVC application keeps views focused on display, controllers focused on request flow, and models focused on data and business rules.

Security Notes for PHP MVC

MVC improves organization, but it does not automatically make an application secure. Security still needs to be applied in each layer.

Important security practices include:

  • Use prepared statements in models and database layers.

  • Validate request data before saving it.

  • Escape output in views using htmlspecialchars().

  • Protect forms with CSRF tokens.

  • Check authentication and authorization in controllers or middleware.

  • Do not expose internal folders such as app, config, or routes publicly.

  • Hide technical errors in production.

  • Store secrets outside public files.

A common security-friendly structure is to make only the public folder accessible from the web server. Other folders should stay outside direct public access.

MVC in Laravel

Laravel is one of the most popular PHP frameworks and is heavily based on MVC concepts. In Laravel, routes receive requests, controllers handle logic, models interact with the database, and Blade views display the user interface.

Laravel MVC examples include:

  • Routes: defined in routes/web.php or routes/api.php.

  • Controllers: stored in app/Http/Controllers.

  • Models: stored in app/Models.

  • Views: stored in resources/views.

  • Validation: handled by requests, controllers, or validators.

  • Middleware: used for authentication, CSRF, and permissions.

Learning MVC in pure PHP makes Laravel easier to understand because the framework uses the same general ideas in a more advanced and organized way.

Simple MVC Request Flow

A typical MVC request flow looks like this:

  1. The browser sends a request to the application.

  2. The request enters through public/index.php.

  3. The router checks the URL and HTTP method.

  4. The router calls the correct controller method.

  5. The controller validates input and calls the model.

  6. The model reads or writes data using the database.

  7. The model returns data to the controller.

  8. The controller loads a view or returns JSON.

  9. The response is sent back to the browser or API client.

This flow keeps the application organized and makes it easier to understand where each part of the code belongs.

Conclusion

PHP MVC architecture is an important concept for building organized and maintainable web applications. MVC separates the application into models, views, and controllers, making the code cleaner and easier to manage.

Models handle data and business logic, views handle presentation, and controllers handle request flow. Routing connects URLs to controller methods, while the front controller provides a single entry point for the application.

After understanding PHP MVC architecture, the next step is to practice by building a small MVC CRUD application, then move toward more advanced topics such as services, repositories, middleware, authentication, REST APIs, Composer autoloading, and Laravel development.