Trongate PHP Framework Docs
Introduction
Quick Start
Basic Concepts
Understanding Routing
Intercepting Requests
Module Fundamentals
Database Operations
Templates
Helpers
Form Handling
Form Validation
Working With Files
Image Manipulation
Working With Dates & Times
Language Control
Security
Tips And Best Practices

Building Modules: Controllers vs Utilities

One of Trongate v2's most powerful features is its ability to intelligently handle both framework-dependent controllers and standalone utility classes. This page explains when to use each pattern and how the framework automatically adapts.

The Two Types of Modules

In Trongate v2, modules fall into two categories:

Controllers (Extend Trongate)

Controllers are modules that need framework features like database access, view rendering, or loading other modules.

PHP
<?php
class Products extends Trongate {
    
    public function index() {
        $data = $this->model->get_all();      // Database access
        $this->view('list', $data);           // View rendering
        $this->module('auth');                // Module loading
    }
}

Utilities (Standalone Classes)

Utilities are self-contained classes that perform specific tasks without needing any framework features.

PHP
<?php
class Image {  // No extends Trongate
    
    public function resize($width, $height) {
        // Pure image processing using GD library
        // No database, no views, no framework dependencies
    }
}

When to Extend Trongate

Your module should extend Trongate if it needs any of these features:

  • Database operations - $this->model
  • View rendering - $this->view()
  • Loading other modules - $this->module('name') or $this->modulename
  • Module name tracking - $this->module_name

Common Controller Examples

  • Products - manages product data, displays views
  • Users - handles user accounts, authentication
  • Orders - processes orders, generates invoices
  • Admin - dashboard, reports, management interface
  • Api - endpoints that query databases and return JSON
PHP
<?php
class Users extends Trongate {
    
    public function create() {
        // Uses validation (framework feature)
        $this->validation->set_rules('email', 'Email', 'required|valid_email');
        
        if ($this->validation->run()) {
            // Uses model (framework feature)
            $data = $this->_get_data_from_post();
            $this->model->insert($data);
            
            redirect('users/manage');
        } else {
            // Uses view rendering (framework feature)
            $this->view('create_form');
        }
    }
}

When NOT to Extend Trongate

Create a standalone utility class if your module:

  • Performs pure computation - math, string manipulation, data formatting
  • Wraps external libraries - image processing (GD), PDFs, email
  • Has no database needs - doesn't read or write data
  • Has no view rendering - doesn't display HTML
  • Could be used outside Trongate - framework-agnostic functionality

Common Utility Examples

  • Image - resize, crop, manipulate images with GD
  • Pdf - generate or manipulate PDF documents
  • Calculator - perform complex calculations
  • Converter - convert between units, formats, currencies
  • Validator - custom validation logic (credit cards, phone numbers)
  • Encryptor - encryption/decryption utilities
PHP
<?php
class Calculator {  // Standalone utility - no extends
    
    public function compound_interest($principal, $rate, $time) {
        return $principal * pow((1 + $rate / 100), $time);
    }
    
    public function mortgage_payment($principal, $rate, $years) {
        $monthly_rate = $rate / 100 / 12;
        $num_payments = $years * 12;
        return $principal * ($monthly_rate * pow(1 + $monthly_rate, $num_payments)) 
               / (pow(1 + $monthly_rate, $num_payments) - 1);
    }
}

How Trongate Handles Both Types

Here's the brilliant part: you can use both types of modules exactly the same way in your controllers.

Using a Controller Module

PHP
<?php
class Dashboard extends Trongate {
    
    public function index() {
        // Load the 'users' controller module (extends Trongate)
        $this->module('users');
        $user_count = $this->users->get_count();
    }
}

Using a Utility Module

PHP
<?php
class Products extends Trongate {
    
    public function upload_image() {
        // Load the 'image' utility (does NOT extend Trongate)
        $config = ['max_width' => 800];
        $file_info = $this->image->upload($config);
    }
}

Notice: The syntax is identical! You don't need to know or care whether a module extends Trongate or not.

The Framework's Intelligence

When you access $this->image, here's what happens behind the scenes:

PHP
// Inside Trongate base class:
protected function module(string $target_module): void {
    // Load the module file
    require_once $controller_path;
    
    // Check: Does this class extend Trongate?
    if (is_subclass_of($controller_class, 'Trongate')) {
        // Yes - pass module name to constructor
        $module_instance = new $controller_class($target_module);
    } else {
        // No - it's a standalone utility
        $module_instance = new $controller_class();
    }
    
    // Cache it for future use
    $this->loaded_modules[$target_module] = $module_instance;
}

The framework automatically detects whether a module extends Trongate and instantiates it correctly.

Zero Configuration: You don't register modules as "controllers" or "utilities". The framework figures it out automatically by checking the class inheritance.

Real-World Example: Image Utility

Let's look at a complete example of a standalone utility class:

PHP
<?php
/**
 * Image manipulation utility
 * Standalone class - no framework dependencies
 */
class Image {
    
    private $image;
    private $image_type;
    
    public function __construct(?string $filename = null) {
        // Block direct URL access
        block_url('image');
        
        // Check for GD library
        if (!extension_loaded('gd')) {
            die('GD library required');
        }
        
        // Optionally load an image
        if ($filename) {
            $this->load($filename);
        }
    }
    
    public function load($filename) {
        $image_info = getimagesize($filename);
        $this->image_type = $image_info[2];
        
        switch($this->image_type) {
            case IMAGETYPE_JPEG:
                $this->image = imagecreatefromjpeg($filename);
                break;
            case IMAGETYPE_PNG:
                $this->image = imagecreatefrompng($filename);
                break;
            // ... other formats
        }
    }
    
    public function resize($width, $height) {
        $new_image = imagecreatetruecolor($width, $height);
        imagecopyresampled(
            $new_image, $this->image,
            0, 0, 0, 0,
            $width, $height,
            imagesx($this->image), imagesy($this->image)
        );
        $this->image = $new_image;
    }
    
    public function save($filename, $quality = 90) {
        switch($this->image_type) {
            case IMAGETYPE_JPEG:
                imagejpeg($this->image, $filename, $quality);
                break;
            case IMAGETYPE_PNG:
                imagepng($this->image, $filename);
                break;
        }
    }
}

Notice what this class doesn't have:

  • No extends Trongate
  • No $this->model usage
  • No $this->view() calls
  • No framework dependencies at all

It's pure functionality. And it works perfectly when loaded via $this->image in any controller.

Using the Image Utility in a Controller

PHP
<?php
class Products extends Trongate {
    
    public function upload_product_image() {
        // Configure upload
        $config = [
            'destination' => 'uploads/products',
            'max_width' => 800,
            'max_height' => 800
        ];
        
        // Upload and resize automatically
        $file_info = $this->image->upload($config);
        
        // Save to database
        $data = [
            'product_id' => segment(3),
            'image_path' => $file_info['file_path']
        ];
        
        $this->model->insert($data, 'product_images');
        
        set_flashdata('Image uploaded successfully!');
        redirect('products/manage');
    }
}

See how natural it is? The Image utility handles image processing, while the Products controller handles business logic, database, and views.

Constructor Differences

Controllers (Extending Trongate)

PHP
<?php
class Products extends Trongate {
    
    // If you need a constructor:
    public function __construct(?string $module_name = null) {
        parent::__construct($module_name); // REQUIRED!
        // Your initialization
    }
    
    // Most controllers don't need a constructor at all
}

Utilities (Standalone)

PHP
<?php
class Image {
    
    // Constructor accepts whatever parameters make sense
    public function __construct(?string $filename = null) {
        // No parent::__construct() needed
        // No module_name parameter needed
        // Just your logic
    }
}

Important: Standalone utilities should NOT accept $module_name as a parameter. The framework instantiates them without parameters.

Performance Benefits of Standalone Utilities

Creating standalone utilities has real performance advantages:

Lower Memory Usage

Standalone classes don't carry the framework's base class overhead:

  • No $instances cache array
  • No $loaded_modules cache array
  • No $module_name, $parent_module, $child_module properties

Faster Instantiation

The framework doesn't need to:

  • Call parent::__construct()
  • Set module name properties
  • Initialize framework features

Framework-Agnostic

Standalone utilities can be:

  • Reused in other projects
  • Tested independently
  • Shared as libraries
  • Used outside of Trongate entirely

Speed Priority: For Trongate v2's goal of being the fastest PHP framework, creating standalone utilities for pure functionality eliminates unnecessary overhead.

Migration Decision Tree

When creating a new module, ask yourself:

Does this module need to:
├─ Query the database?
│  └─ YES → Extend Trongate
│
├─ Render views?
│  └─ YES → Extend Trongate
│
├─ Load other modules?
│  └─ YES → Extend Trongate
│
└─ Just perform calculations/processing?
   └─ NO to all above → Standalone utility

Converting Between Types

You can easily convert between controller and utility if you change your mind:

From Controller to Utility

PHP
// Before: Controller
<?php
class Calculator extends Trongate {
    
    public function __construct(?string $module_name = null) {
        parent::__construct($module_name);
    }
    
    public function add($a, $b) {
        return $a + $b;
    }
}

// After: Utility (remove extends, remove constructor)
<?php
class Calculator {
    
    public function add($a, $b) {
        return $a + $b;
    }
}

From Utility to Controller

PHP
// Before: Utility
<?php
class Reports {
    
    public function generate($type) {
        // Just generates data
        return $data;
    }
}

// After: Controller (add extends, add constructor if needed)
<?php
class Reports extends Trongate {
    
    public function generate($type) {
        $data = $this->calculate_data($type);
        
        // Now can use framework features
        $this->view('report', $data);
    }
}

Common Patterns

Pattern 1: Controller Uses Multiple Utilities

PHP
<?php
class Invoices extends Trongate {
    
    public function generate($order_id) {
        // Load data from database
        $order = $this->model->get_where($order_id, 'orders');
        
        // Use calculator utility
        $total = $this->calculator->apply_tax($order['subtotal']);
        
        // Use PDF utility
        $pdf_path = $this->pdf->generate_invoice($order, $total);
        
        // Return result
        echo json_encode(['pdf_url' => $pdf_path]);
    }
}

Pattern 2: Utility Uses Another Utility

Utilities can use other utilities, but they load them differently (no $this-> magic):

PHP
<?php
class Pdf {
    
    private $image;
    
    public function __construct() {
        // Manually instantiate utilities
        require_once '../modules/image/Image.php';
        $this->image = new Image();
    }
    
    public function add_logo() {
        // Use the image utility
        $this->image->load('logo.png');
        $this->image->resize(200, 100);
    }
}

Or, convert Pdf to extend Trongate if it needs multiple utilities:

PHP
<?php
class Pdf extends Trongate {
    
    public function add_logo() {
        // Now can use automatic loading
        $this->image->load('logo.png');
        $this->image->resize(200, 100);
    }
}

Testing Your Module Type

To verify you've chosen the right pattern:

Test 1: Can it work standalone?

PHP
// Try instantiating directly
$calc = new Calculator();
$result = $calc->add(5, 3);

// If this works, it's a good utility candidate

Test 2: Does it need framework features?

PHP
// If you see any of these in your class:
$this->model->...
$this->view(...)
$this->module(...)

// Then it MUST extend Trongate

Best Practices Summary

Controllers (extend Trongate):

  • Handle business logic and user interactions
  • Need database, views, or other modules
  • Must call parent::__construct($module_name) if they have a constructor
  • Examples: Products, Users, Admin, Orders

Utilities (standalone):

  • Perform specific, self-contained tasks
  • No database, views, or framework dependencies
  • Constructor accepts task-specific parameters only
  • Examples: Image, Calculator, Pdf, Validator

Advanced: Hybrid Approach

Some modules might need BOTH a controller interface AND utility functions:

PHP
// products/Products.php - Controller for web interface
<?php
class Products extends Trongate {
    
    public function create() {
        $this->view('create_form');
    }
}

// products/products_helper.php - Utility functions
<?php
function calculate_price_with_tax($price, $tax_rate) {
    return $price * (1 + $tax_rate);
}

function format_price($price) {
    return '$' . number_format($price, 2);
}

Load the helper in your controller:

PHP
<?php
class Products extends Trongate {
    
    public function __construct(?string $module_name = null) {
        parent::__construct($module_name);
        require_once 'products_helper.php';
    }
    
    public function display($id) {
        $product = $this->model->get_where($id);
        
        // Use helper function
        $price = calculate_price_with_tax($product['price'], 0.08);
        
        $data['formatted_price'] = format_price($price);
        $this->view('details', $data);
    }
}

Conclusion

Trongate v2's dual module system gives you:

  • Flexibility - Choose the right pattern for each module
  • Performance - Zero framework overhead for utilities
  • Simplicity - Use both types identically in controllers
  • Intelligence - Framework auto-detects and adapts

Understanding when to extend Trongate vs creating standalone utilities is key to building fast, maintainable applications.

Remember: When in doubt, start with a standalone utility. You can always add extends Trongate later if you need framework features. It's easier to add framework capabilities than remove them.

We're continually improving the Trongate documentation. If anything is incorrect, unclear, incomplete, or could be better, we'd genuinely appreciate your input.

Share your thoughts in the Documentation Feedback.

Leave Feedback About This Page