Introduction to Controllers in MVC Architecture

Welcome to the fourth lesson of our course on building an image generation service with PHP! So far, we've built several key components of our application: the PromptManager for formatting user inputs, the ImageManager for storing and processing images, and the ImageGeneratorService that connects to the Gemini API to generate images.

Now, we're ready to add another important layer to our application architecture: the controller. In web applications, controllers play a crucial role in the Model-View-Controller (MVC) pattern. They act as intermediaries between the service layer (which contains our business logic) and the presentation layer (which handles user interactions).

The controller's primary responsibilities include:

  • Receiving and validating input from the user interface
  • Calling the appropriate service methods with validated inputs
  • Handling errors that might occur during processing
  • Formatting responses in a consistent way before sending them back to the user

In our image generation application, the controller will receive text prompts from users, validate them, pass them to our ImageGeneratorService, and then format the responses (either successful image data or error messages) before sending them back to the client.

By adding this controller layer, we're further improving the separation of concerns in our application. The service layer can focus purely on business logic (generating images via the Gemini API), while the controller handles the specifics of HTTP requests and responses. This makes our code more maintainable, testable, and easier to extend in the future.

Setting Up the Image Generator Controller

Let's start by creating our controller class. We'll create a new file called ImageGeneratorController.php in the app/controllers directory. This controller will depend on our ImageGeneratorService to perform the actual image generation.

Here's how we'll set up the basic structure of our controller:

<?php

require_once 'services/ImageGeneratorService.php';

class ImageGeneratorController {
    private $imageGeneratorService;

    public function __construct() {
        $this->imageGeneratorService = new ImageGeneratorService();
    }
}

In this code, we're requiring the ImageGeneratorService class that we created in the previous lesson.

The controller's __construct initializes an instance of the ImageGeneratorService. This establishes the dependency between our controller and service layers. By creating this dependency in the constructor, we're following the principle of dependency injection, which makes our code more modular and easier to test.

Our controller will be responsible for two main operations:

  1. Generating a new image based on user input
  2. Retrieving all previously generated images

For each of these operations, we'll create a dedicated method in our controller class. These methods will handle input validation, error handling, and response formatting, ensuring that our API provides a consistent interface to clients.

Implementing the Image Generation Endpoint
Implementing the Image Retrieval Endpoint
Storing Images in a Database

To ensure that our images are not lost when the application restarts, we need to store them in a database. This involves creating a database table and an Eloquent model, and updating our ImageManager to interact with the database.

Creating the Images Table

First, we'll create a migration for the images table. This table will have fields for id, prompt, and image_base64:

<?php

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

class CreateImagesTable extends Migration
{
    public function up()
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->text('prompt');
            $table->longText('image_base64');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('images');
    }
}
Creating the Image Model

Next, we'll create an Eloquent model for the Image entity. This model will represent the images table in our application:

<?php

namespace App\Models;

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

class Image extends Model
{
    use HasFactory;

    protected $fillable = ['prompt', 'image_base64'];
}
Updating the ImageManager
Manual Validation of the Controller
Verifying Error Handling Behavior
Summary and Practice Preview

In this lesson, we've built the ImageGeneratorController, which serves as an intermediary between our service layer and the future PHP routes. This controller handles input validation, error management, and response formatting, providing a clean, consistent interface for clients to interact with our Gemini-powered image generation service.

Let's review what we've learned:

  1. We understood the role of controllers in the MVC architecture and how they fit into our application.
  2. We created a controller class that depends on our ImageGeneratorService.
  3. We implemented methods for generating images and retrieving previously generated ones.
  4. We added input validation and error handling to ensure robust operation.
  5. We integrated database storage for images, ensuring they persist across application restarts.
  6. We tested our controller to verify that it correctly triggers the Gemini image generation flow and handles responses.

The ImageGeneratorController is a crucial component of our application architecture. It abstracts away the details of how images are generated via the Gemini API and stored, presenting a clean, consistent API to clients. This separation of concerns makes our code more maintainable, testable, and easier to extend.

In the next lesson, we'll integrate our controller with PHP routes to create a complete web API for image generation. We'll define endpoints that clients can call to generate images and retrieve previously generated ones.

In the upcoming practice exercises, you'll have the opportunity to work with the ImageGeneratorController, testing its functionality with different inputs and exploring how it handles various scenarios. You'll also get to experiment with error handling and see how the controller formats responses for different types of requests.

With the ImageGeneratorController in place, we're one step closer to having a complete Gemini-powered image generation web application. In the next lesson, we'll see how to expose our controller's functionality through HTTP endpoints using PHP.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal