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

Mastering Constructors

Trongate v2 handles constructors differently than most frameworks. This page explains the rules, the reasons, and the patterns you need to know to build reliable, high-performance modules.

The Golden Rule

If your class extends Trongate AND has a constructor, you MUST call parent::__construct($module_name) as the first line. For example:

PHP
<?php
class Items extends Trongate {
    
    public function __construct(?string $module_name = null) {
        parent::__construct($module_name);  // First line!
        // Your custom code here
    }

}

Forgetting this will cause errors when using framework features like $this->db.

In PHP, the __construct() method is not allowed to declare a return type - not even void.

Understanding $module_name

Before we dive into constructors, let's clarify what $module_name actually is and where it comes from. This is fundamental to understanding why constructors work the way they do.

What is $module_name?

The $module_name is a lowercase string that identifies your module. It matches:

  • The folder name in the modules directory
  • The first part of your URL (i.e., the first URL segment)

A Concrete Example

Let's say you create a module for managing products. Here's how it all connects:

Your project structure:
modules/
  └─ products/              ← This folder name
       └─ Products.php      ← Controller class inside

User visits this URL:
https://yoursite.com/products/index
                     ^^^^^^^^
                     This is the $module_name

When someone visits https://yoursite.com/products/index:

  1. Trongate looks at the URL and extracts 'products'
  2. It sets $module_name = 'products'
  3. It looks for the folder modules/products/
  4. It loads the file modules/products/Products.php
  5. It instantiates the class: new Products('products')

So $module_name is simply the string 'products' - the identifier for your module.

Simple Rule: The module_name is always the lowercase version of your folder name and the first URL segment.

Examples:

  • URL: yoursite.com/users$module_name: 'users'
  • URL: yoursite.com/admin_panel$module_name: 'admin_panel'
  • URL: yoursite.com/api$module_name: 'api'

Where Does It Come From?

The framework extracts $module_name from the URL automatically. So, let's assume you're building a Trongate module and you write code like this:

PHP
parent::__construct($module_name);

At first glance, it may look as though you've plucked some random variable out of thin air. That may be a bit worrying. However, there's no need to worry!

The $module_name variable is created automatically by the framework and passed to your constructor. You don't create it - you simply receive it and pass it along to the parent class.

How It Gets To You

Here's what happens behind the scenes. Inside the framework's Core.php file, there's code that looks like this:

PHP
// Extract module name from URL
$module_name = 'products';  // Simplified for clarity

// Instantiate your controller
$controller_instance = new Products($module_name);

Notice how the framework passes $module_name when creating your controller? That's where it comes from!

Your job is simply to accept it in your constructor and pass it to the parent:

PHP
public function __construct(?string $module_name = null) {
    parent::__construct($module_name);
}

Think of it like a relay race - the framework hands you the baton ($module_name), and you pass it to the next runner (the parent class).

When the framework automatically loads modules like DB, you might notice it passes $this->module_name as an argument:

PHP
// Inside Trongate.php when you access $this->db:
$module_instance = new Db($this->module_name);

Why pass the module name? Why not just new Db() with no arguments?

The reason is simple: these framework modules need to know which module is using them.

For example, the Db class stores the current module name in its $current_module property. This helps the Db class to figure out the name of the target database table.

Without passing the module name, the framework wouldn't have this contextual information!

Parameter vs Property: Two Different Things

Here's something that trips up a lot of developers: there are actually two separate variables called module_name in play.

The Parameter (Local Variable)

This is what you receive in your constructor:

PHP
public function __construct(?string $module_name = null) {
    //                                 ^^^^^^^^^^^^
    //                                 This is a parameter
    //                                 A local variable
}

It only exists inside the constructor function. Once the constructor finishes, it disappears.

The Property (Class Variable)

This is what gets stored on your object:

PHP
$this->module_name  // This is a property
                    // Available throughout your class

This exists for the entire lifetime of your object and can be accessed from any method.

Why Not Just Use $this->module_name?

You might be thinking: "Why don't we just pass $this->module_name to the parent constructor?"

PHP
// Why not this?
public function __construct() {
    parent::__construct($this->module_name);  // Surely this?
}

Great question! The problem is timing. At the point where you're calling parent::__construct(), the property $this->module_name doesn't exist yet. The parent constructor is the thing that creates it!

It's like asking someone to hand you a cup of tea that they're about to make - they can't give you something that doesn't exist yet.

The Correct Sequence

Here's what actually happens, step by step:

PHP
public function __construct(?string $module_name = null) {
    // 1. Framework passes 'products' to your parameter
    //    Local variable $module_name = 'products'
    
    // 2. You pass it to parent
    parent::__construct($module_name);
    
    // 3. Parent creates the property
    //    Inside Trongate.php:
    //    $this->module_name = $module_name;
    
    // 4. NOW you can use the property
    echo $this->module_name;  // Prints 'products'
}

So you receive it as a parameter, pass it to parent, and then parent converts it into a property.

Breaking Down the Constructor Signature

Let's look at the recommended constructor signature piece by piece:

PHP
public function __construct(?string $module_name = null)

Why ?string (Nullable String)?

The question mark means "this can be a string OR null". This gives you flexibility:

PHP
// Framework usage (normal):
new Products('products');  // Passes a string

// Direct instantiation (testing):
new Products();            // No parameter - uses null
new Products(null);        // Explicitly passes null

Without the ?, you'd be forced to always pass a string, which would make testing awkward.

Why = null (Default Value)?

The = null makes the parameter optional. This means all of these work:

PHP
new Products('products');  // Framework does this
new Products(null);        // You could do this
new Products();            // Or this

Without = null, that last one would throw an error about a missing argument.

Can't We Just Omit the Parameter Entirely?

You might wonder: "If the framework knows what module this is, why not just have an empty constructor?"

PHP
// Can't we do this?
public function __construct() {
    parent::__construct($module_name);  // Where does this come from?
}

This doesn't work for two reasons:

  1. The variable $module_name wouldn't exist - you'd get an "undefined variable" error
  2. Your constructor wouldn't accept the parameter the framework is trying to pass

You must accept the parameter in order to receive it and pass it along.

Why the "Awkward" Syntax?

When you start writing constructors in Trongate v2, you might find yourself asking: "Why can't I just write a simple constructor? Is this extra code actually necessary?"

First, a Reminder: You might not need a constructor at all! If you don't have custom initialization logic to run, simply omit the constructor entirely. PHP will automatically handle the parent call for you, keeping your code clean and minimal.

Common Developer Questions

1. Why the "awkward" constructor signature?

Trongate v2 is built for Context Awareness. By using the standard ?string $module_name = null signature, you are accepting a "context baton" from the framework. This baton allows utility modules (like the Database or Validation classes) to automatically know which module is calling them without you having to configure them manually.

2. What are the benefits of this approach?

  • Zero-Config Database: $this->db automatically knows your table names.
  • Smart Validation: $this->validation instantly finds your specific language files.
  • Traceability: It makes your code easier to debug because the "flow of information" is explicit and linear.
  • Performance: It enables "Lazy Loading," ensuring you only load exactly what you need.

3. Am I allowed to write constructors like this?

PHP
public function __construct() {
    parent::__construct();
}

Technically, yes - but it is fragile. If you use this empty version, the framework has to "guess" your module name using PHP class reflection. This works for basic, standalone controllers, but it will break if you ever load that module as a service or try to use internal security features like .

4. Is this a "MUST" or a "SHOULD"?

Think of it as a Professional Standard. While the framework tries to be helpful if you use the simple version, using the recommended signature is a "MUST" if you want your module to be robust, secure, and fully compatible with advanced features like internal callbacks and cross-module service loading.

5. Is there any scenario where the simple version is acceptable?

The simple parent::__construct() is only acceptable for very basic controllers that will never interact with other modules. However, since the "correct" way only takes an extra few seconds to type, we recommend the full pattern to future-proof your application against "Ghost" 403 errors and context-loss bugs.

The Golden Rule: 5 seconds of "awkward" code now saves hours of manual configuration and troubleshooting later. Stick to the standard, and the framework handles the heavy lifting for you.


When You Don't Need a Constructor

Here's the good news: most of the time, you don't need to write a constructor at all!

PHP
<?php
class Products extends Trongate {
    
    // No constructor needed!
    
    public function index() {
        $data = $this->db->query('SELECT * FROM products');
        $this->view('list', $data);
    }
}

When you don't define a constructor, PHP automatically calls the parent constructor for you. It's like saying "just do whatever the parent class does" - which is usually exactly what you want.

Don't add a constructor unless you have a specific reason. Let PHP handle it automatically.

When You DO Need a Constructor

Only add a constructor if you need to run some code before your methods execute. Common reasons include:

  • Setting up properties that all methods will use
  • Performing initialization checks
  • Configuring settings

Example: Setting a Property

PHP
<?php
class Products extends Trongate {
    
    protected $items_per_page = 20;
    
    public function __construct(?string $module_name = null) {
        parent::__construct($module_name);  // ALWAYS first!
        
        // Set items per page based on configuration
        $this->items_per_page = 50;
    }
    
    public function index() {
        // items_per_page is already set
        echo "Displaying " . $this->items_per_page . " items per page";
    }
}

What Happens If You Forget parent::__construct()?

If you add a constructor but forget to call the parent, things will break when you try to use framework features:

PHP
<?php
class Products extends Trongate {
    
    public function __construct(?string $module_name = null) {
        // Oops! Forgot parent::__construct($module_name);
        $this->some_property = 'value';
    }
    
    public function index() {
        $data = $this->db->query('SELECT * FROM products');  // ERROR!
    }
}

You'll see an error like:

Fatal error: Uncaught Exception: Undefined property: Products::db

This happens because the parent constructor is responsible for setting up the $this->module_name property, which the framework needs for automatic loading of modules.

Remember: If you write a constructor, parent::__construct($module_name) must be the first line.

How Automatic Loading Works

Understanding what happens behind the scenes helps clarify why constructors matter.

When you write this:

PHP
$results = $this->db->query('SELECT * FROM products');

Here's what the framework does:

  1. Looks for a property called $db on your class
  2. Doesn't find one, so calls the __get('db') magic method
  3. The magic method checks if the Db module exists
  4. Loads the file modules/db/Db.php
  5. Instantiates it: new Db($this->module_name)
  6. Returns the Db instance to you

Notice step 5? The framework needs $this->module_name to be set. That's what the parent constructor does!

The same process works for any module you access via $this->modulename. The framework automatically loads it and passes your module name during instantiation.

Testing Your Constructor

If you're not sure whether your constructor is set up correctly, try this test method:

PHP
public function test() {
    // Check module_name is set
    echo "Module name: " . $this->module_name . "";
    
    // Try accessing the db module (loads automatically)
    echo "Db class: " . get_class($this->db) . "";
    
    // Try a database query
    $result = $this->db->query('SELECT 1 as test');
    echo "Database connected successfully!";
}

Visit yoursite.com/products/test and if you see output instead of errors, your constructor is working properly.

Performance

You might wonder: "Does all this constructor stuff slow things down?"

Not at all! The parent constructor does exactly one thing:

PHP
$this->module_name = $module_name ?? strtolower(get_class($this));

This is a simple variable assignment - one of the fastest operations in PHP. The overhead is negligible.

Speed Matters: Trongate v2 is designed to be the fastest PHP framework. The constructor pattern is optimized for both speed and clarity.

Common Questions

"What if I rename my class?"

The $module_name comes from the URL and folder structure, not your class name. So even if you rename your class to ProductsController, it would still receive 'products' as the module name.

That said, stick to Trongate conventions: your class name should match your folder name (capitalized).

"Can I use a different parameter name?"

Yes! PHP doesn't care what you call it:

PHP
public function __construct(?string $name = null) {
    parent::__construct($name);  // Works fine
}

But using $module_name makes your code clearer for other developers.

"What about child modules?"

The same rules apply. If you have a module at modules/products/categories/Categories.php, the framework handles it automatically. You still just write the same constructor pattern.

Quick Reference

No Constructor Needed (Most Common)

PHP
<?php
class Products extends Trongate {
    
    public function index() {
        // Just write your methods
    }
}

With Custom Initialization

PHP
<?php
class Products extends Trongate {
    
    public function __construct(?string $module_name = null) {
        parent::__construct($module_name);  // First line!
        // Your custom code here
    }
}

Summary

Let's recap the key points:

  • $module_name is a lowercase string from the URL (e.g., 'products')
  • The framework passes it to your constructor automatically
  • Most controllers don't need a custom constructor
  • If you add a constructor, call parent::__construct($module_name) first
  • Use the signature: ?string $module_name = null
  • Don't confuse the parameter with the property ($this->module_name)

Follow these guidelines and your constructors will work perfectly every time.

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