Controllers

A controller is where a request lands once routing has decided which class and method should handle it. Its job is coordination: read whatever the request needs (URL segments, form input), talk to a model if data is required, and hand a finished response back — usually a rendered view. This page covers how controllers are structured, named, and written in CodeIgniter 4.

1. Every Controller Extends BaseController

CodeIgniter's own base Controller class lives in the framework itself, but every controller you write extends App\Controllers\BaseController instead — a thin class, generated for you at app/Controllers/BaseController.php, that itself extends CodeIgniter's Controller. This extra layer exists specifically so you have a safe, project-owned place to add shared behaviour (helpers loaded on every request, common properties, etc.) without ever touching framework code directly.

<?php

namespace App\Controllers;

use CodeIgniter\Controller;

class BaseController extends Controller
{
    // Shared setup and helper properties for all your controllers
}

2. Naming and Namespacing

Controller classes live in app/Controllers/ and belong to the App\Controllers namespace (or a sub-namespace matching a subfolder, for organizing larger applications). The class name should match the filename, and by convention it's written in PascalCase:

<?php

namespace App\Controllers;

class Articles extends BaseController
{
    // Actions go here
}

This file would live at app/Controllers/Articles.php. If it were organized under an admin area instead, it might live at app/Controllers/Admin/Articles.php with the namespace App\Controllers\Admin, matching the folder path exactly — CodeIgniter's autoloader relies on this namespace-to-path convention to find the class at all.

3. Actions Are Just Public Methods

There's no special interface or annotation required to make a method callable as an action — any public method on a controller is a potential target for a route. That's it. Private and protected methods, by contrast, are invisible to routing and safe to use as internal helpers within the controller.

public function index()
{
    // Handles GET /articles
}

4. Receiving URL Segments as Parameters

Recall from Routing that a route like $routes->get('articles/(:num)', 'Articles::show/$1'); captures a URL segment and passes it through as $1. On the controller side, that captured value simply arrives as a normal method parameter, in order:

public function show($id)
{
    // $id holds whatever (:num) captured from the URL, e.g. "5"
}

The same applies whether the segment arrived via an explicit route or via automatic routing — either way, extra URL segments after the method name become positional arguments to that method.

5. Loading a Model

When an action needs data, it typically reaches for a model. The most direct way is instantiating it straight from its fully qualified class name:

$model = new \App\Models\ArticleModel();
$article = $model->find($id);
Foreshadowing: Models, the Query Builder methods like find(), and the full range of database operations they support get their own detailed lesson at Models & Query Builder. For now, just know that a model is a plain class you instantiate and call methods on like anything else.

6. Returning a Rendered View

Once a controller has whatever data it needs, it typically hands that data to a view and returns the rendered result. CodeIgniter's global view() function takes a view path and an associative array of data made available to that view as variables:

return view('articles/show', ['article' => $article]);

The details of how views locate templates, share layouts, and escape output safely are covered fully on the next page, Views.

7. A Full Worked Example

Putting all of the above together, here's a complete Articles controller with an index() action listing all articles and a show($id) action displaying one:

<?php

namespace App\Controllers;

class Articles extends BaseController
{
    public function index()
    {
        $model = new \App\Models\ArticleModel();
        $data['articles'] = $model->findAll();

        return view('articles/index', $data);
    }

    public function show($id)
    {
        $model = new \App\Models\ArticleModel();
        $article = $model->find($id);

        if ($article === null) {
            return $this->response->setStatusCode(404);
        }

        return view('articles/show', ['article' => $article]);
    }
}

Paired with the routes from the previous lesson — $routes->get('articles', 'Articles::index'); and $routes->get('articles/(:num)', 'Articles::show/$1'); — this single class fully handles both the article listing page and individual article pages.

8. Advanced: initController()

CodeIgniter 4 controllers support an initController() method, called automatically right after the controller is instantiated and before your action runs. It's most often used to gain access to the request/response objects and logger a little earlier than the constructor would normally allow, or to run setup logic shared across many actions. This is an advanced, occasional-use feature — most controllers never need to override it, since BaseController and plain constructor logic cover the vast majority of cases.

Next steps: Continue to Views to see how the view() function locates templates, passes data safely, and supports layouts and partials.