Create Controller

15 minutes BEGINNER

Learn how to create the Controller layer - the business logic orchestrator. Master mapping request data to models and coordinating between API and Model layers.

What You'll Learn

Understanding Controller

What is the Controller layer and its responsibilities

Mapping Request to Model

Use mapPostToObject() to convert requests

Creating Controller

Create your first Controller class

Video Coming Soon...

What is Controller Layer?

The Controller Layer is the business logic orchestrator in GEMVC. It's the second layer in the 4-layer architecture and sits between the API Service and Model layers.

  • Business Logic Orchestration - Coordinates the flow of data and operations
  • Request Mapping - Maps HTTP request data to Model objects using mapPostToObject()
  • Coordination - Can coordinate between multiple models if needed
  • Delegation - Passes mapped data to Model layer for business validations

Key Principle: The Controller layer orchestrates business logic flow but doesn't contain data validations or database operations. It maps requests to models and delegates to the Model layer.

1

Understanding the Structure

Basic Controller Structure

Every Controller must extend Controller. Here's the basic structure:

app/controller/ProductController.php
<?php
namespace App\Controller;

use App\Model\ProductModel;
use Gemvc\Core\Controller;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;

class ProductController extends Controller
{
    public function __construct(Request $request)
    {
        parent::__construct($request);
    }

    public function create(): JsonResponse
    {
        // Map POST data to Model
        $model = $this->request->mapPostToObject(
            new ProductModel(),
            [
                'name' => 'name',
                'price' => 'price',
                'description' => 'description'
            ]
        );
        
        if(!$model instanceof ProductModel) {
            return $this->request->returnResponse();
        }
        
        // Delegate to Model layer
        return $model->createModel();
    }
}

Key Points:

  • ✓ Extends Controller base class
  • ✓ Constructor receives Request object
  • ✓ Methods return JsonResponse
  • ✓ Uses mapPostToObject() to convert request data to Model
  • ✓ Delegates to Model layer methods (e.g., createModel())
2

Mapping Request to Model

Using mapPostToObject()

The mapPostToObject() method converts POST request data into a Model object. It takes two parameters:

  1. Model instance - A new instance of your Model class (e.g., new ProductModel())
  2. Mapping array - An array that maps POST field names to Model property names or method calls
app/controller/ProductController.php - Basic Mapping Example
<?php
public function create(): JsonResponse
{
    // Map POST data to Model
    $model = $this->request->mapPostToObject(
        new ProductModel(),
        [
            'name' => 'name',           // POST['name'] → $model->name
            'price' => 'price',         // POST['price'] → $model->price
            'description' => 'description' // POST['description'] → $model->description
        ]
    );
    
    // Check if mapping was successful
    if(!$model instanceof ProductModel) {
        // Mapping failed - return error response
        return $this->request->returnResponse();
    }
    
    // Mapping successful - delegate to Model
    return $model->createModel();
}

Mapping Array Format:

  • 'field' => 'property' - Maps POST field to Model property
  • 'field' => 'methodName()' - Calls a method on the Model (e.g., 'password' => 'setPassword()')

Method Calls in Mapping:

You can specify method calls in the mapping array. The method will be called with the POST value as the argument:

Method Call Example
<?php
$model = $this->request->mapPostToObject(
    new ProductModel(),
    [
        'name' => 'name',
        'price' => 'price',
        'discount' => 'applyDiscount()'  // Calls $model->applyDiscount($post['discount'])
    ]
);

Note: Method calls are useful for data transformations (e.g., password hashing, discount calculations).

3

Complete Example

Full CRUD Controller

Here's a complete example of a Controller with all CRUD operations:

app/controller/ProductController.php - Complete Example
<?php
namespace App\Controller;

use App\Model\ProductModel;
use Gemvc\Core\Controller;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;

class ProductController extends Controller
{
    public function __construct(Request $request)
    {
        parent::__construct($request);
    }

    // Create - Map POST data to Model
    public function create(): JsonResponse
    {
        $model = $this->request->mapPostToObject(
            new ProductModel(),
            [
                'name' => 'name',
                'price' => 'price',
                '?description' => 'description'
            ]
        );
        
        if(!$model instanceof ProductModel) {
            return $this->request->returnResponse();
        }
        
        return $model->createModel();
    }

    // Read - Get product by ID
    public function read(): JsonResponse
    {
        $id = $this->request->intValueGet('id');
        if (!$id) {
            return $this->request->returnResponse();
        }
        
        $model = new ProductModel();
        return $model->readModel($id);
    }

    // Update - Map POST data to existing Model
    public function update(): JsonResponse
    {
        $id = $this->request->intValueGet('id');
        if (!$id) {
            return $this->request->returnResponse();
        }
        
        $model = new ProductModel();
        $existing = $model->selectById($id);
        if (!$existing) {
            return $this->request->returnResponse();
        }
        
        $model = $this->request->mapPostToObject(
            $existing,
            [
                'name' => 'name',
                '?price' => 'price',
                '?description' => 'description'
            ]
        );
        
        if(!$model instanceof ProductModel) {
            return $this->request->returnResponse();
        }
        
        return $model->updateModel();
    }

    // Delete - Delete product by ID
    public function delete(): JsonResponse
    {
        $id = $this->request->intValueGet('id');
        if (!$id) {
            return $this->request->returnResponse();
        }
        
        $model = new ProductModel();
        return $model->deleteModel($id);
    }
}

⚠️ ProductModel Not Created Yet

The use App\Model\ProductModel; import and all ProductModel references will cause errors until we create the Model class. This is normal! We'll create ProductModel in the next step: Create Model.

Key Methods Used:

  • mapPostToObject() - Maps POST data to Model
  • intValueGet('id') - Gets integer value from GET parameters
  • stringValueGet('name') - Gets string value from GET parameters
  • returnResponse() - Returns error response if validation fails

Important Notes

  • No Data Validations: Controller layer should not contain business validations (e.g., duplicate email check). That's the Model layer's responsibility.
  • No Database Operations: Controller never directly accesses the database. Always delegate to Model layer.
  • Always Check Mapping Result: Always verify that mapPostToObject() returns the correct Model instance before proceeding.
  • Naming Convention: Use PascalCase + "Controller" suffix (e.g., ProductController.php, UserController.php).
4

Create Your Controller

Manual Creation

To create a Controller, simply create a new PHP class file in the app/controller/ directory. For example, to create a Product Controller:

File Location:

Create app/controller/ProductController.php

The class name should match the filename (e.g., ProductController.phpclass ProductController)

Example: Create a Product Controller

app/controller/ProductController.php
<?php
namespace App\Controller;

use App\Model\ProductModel;
use Gemvc\Core\Controller;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;

class ProductController extends Controller
{
    public function __construct(Request $request)
    {
        parent::__construct($request);
    }

    public function create(): JsonResponse
    {
        $model = $this->request->mapPostToObject(
            new ProductModel(),
            [
                'name' => 'name',
                'price' => 'price',
                'description' => 'description'
            ]
        );
        
        if(!$model instanceof ProductModel) {
            return $this->request->returnResponse();
        }
        
        return $model->createModel();
    }
}

⚠️ ProductModel Not Created Yet

The use App\Model\ProductModel; import will cause an error because the Model class doesn't exist yet. This is expected! We'll create the ProductModel class in the next step: Create Model.

✨ Controller Flow:

The Controller receives validated data from the API Service layer, maps it to a Model object, and delegates to the Model layer for business logic and database operations.

Request Flow:

1. API Service validates schema → Calls Controller
2. Controller maps POST data to Model
3. Controller delegates to Model method
4. Model handles business logic → Returns response

Alternative: Use CLI Command

You can also use the GEMVC CLI command to generate the Controller file automatically:

Terminal
gemvc create:controller Product

Tip: Use gemvc create:controller Product -mt to also create Model and Table layers!

🎉 Controller Created!

Great job! You've learned how to create Controller layer. Now let's move to the Model layer to handle business validations and data transformations.