RuleBuilder - Schema Definition
Revision: 2025/10/13
The RuleBuilder class provides a fluent interface for defining your database schema, validation rules, and relationships in the Model's configure() method. It handles table structure, field types, constraints, form generation, and relationships all in one place.
- Database Schema: Defines table structure and field types
- Validation Rules: Sets required fields, constraints, and validation
- Form Generation: Automatically generates form fields from schema
- Relationships: Defines hasOne, belongsTo, hasMany relationships
- Display Control: Controls visibility in lists, forms, and views
Basic Usage
RuleBuilder is used inside your Model's configure() method:
class ProductsModel extends AbstractModel
{
protected function configure($rule): void {
// $rule is a RuleBuilder instance
$rule->table('#__products')
->id() // Auto-increment primary key
->string('name', 100)->required() // VARCHAR(100) NOT NULL
->decimal('price', 10, 2)->default(0) // DECIMAL(10,2) DEFAULT 0
->text('description')->nullable() // TEXT NULL
->boolean('in_stock')->default(true) // TINYINT(1) DEFAULT 1
->datetime('created_at')->nullable(); // DATETIME NULL
}
}
Configuration Methods
table(string $name): self
Sets the database table name. Use #__ prefix which will be replaced with the configured database prefix.
$rule->table('#__products');
// Creates table: prefix_products
$rule->table('#__orders');
// Creates table: prefix_orders
db(string $type): self
Sets which database connection to use (default: 'db', or 'db2' for second database).
$rule->table('#__logs')
->db('db2'); // Use secondary database
renameField(string $from, string $to): self
Declares a rename operation during schema updates (use with buildTable()).
$rule->table('#__users')
->string('name', 150)
->renameField('full_name', 'name');
Field Type Methods
Primary Key
id(string $name = 'id'): self
Creates an auto-increment primary key field.
// Standard primary key
$rule->id(); // Creates 'id' INT AUTO_INCREMENT PRIMARY KEY
// Custom name
$rule->id('product_id'); // Creates 'product_id' as primary key
String Fields
string(string $name, int $length = 255): self
Creates a VARCHAR field.
// Basic string
$rule->string('name', 100); // VARCHAR(100)
// With constraints
$rule->string('username', 50)
->required()
->unique();
// With default value
$rule->string('status', 20)
->default('active');
text(string $name): self
Creates a TEXT field for long content.
$rule->text('description');
$rule->text('content')->nullable();
email(string $name): self
Creates an email field with automatic email validation.
$rule->email('user_email')
->required();
tel(string $name): self
Creates a telephone field (VARCHAR 25).
$rule->tel('phone')
->nullable();
url(string $name): self
Creates a URL field with validation.
$rule->url('website')
->nullable();
Numeric Fields
int(string $name): self
Creates an INTEGER field.
// Basic integer
$rule->int('quantity');
// With constraints
$rule->int('age')
->min(0)
->max(150);
// Unsigned integer
$rule->int('views')
->unsigned()
->default(0);
// Foreign key with index
$rule->int('category_id')
->index();
decimal(string $name, int $length = 10, int $precision = 2): self
Creates a DECIMAL field for precise numeric values (e.g., prices, measurements).
// Price field: 10 total digits, 2 decimal places
$rule->decimal('price', 10, 2)
->default(0)
->min(0);
// Weight: 8 total digits, 3 decimal places
$rule->decimal('weight', 8, 3)
->nullable();
// Percentage: 5 total digits, 2 decimal places
$rule->decimal('discount_rate', 5, 2)
->min(0)
->max(100);
Boolean Fields
boolean(string $name): self / checkbox(string $name): self
Creates a boolean/checkbox field (TINYINT 1).
$rule->boolean('is_active')
->default(true);
$rule->checkbox('accept_terms')
->required();
Date & Time Fields
datetime(string $name): self
Creates a DATETIME field (format: Y-m-d H:i:s).
$rule->datetime('created_at')
->nullable();
$rule->datetime('updated_at')
->default('CURRENT_TIMESTAMP');
date(string $name): self
Creates a DATE field (format: Y-m-d).
$rule->date('birth_date')
->nullable();
$rule->date('published_at');
time(string $name): self
Creates a TIME field (format: H:i:s).
$rule->time('opening_time')
->nullable();
Selection Fields
list(string $name, array $options): self / select(string $name, array $options): self
Creates a dropdown/select field.
// Basic dropdown
$rule->list('status', [
'draft' => 'Draft',
'published' => 'Published',
'archived' => 'Archived'
])->default('draft');
// From Model
$rule->list('category_id', (new CategoriesModel())->getList())
->required();
enum(string $name, array $options): self
Creates an ENUM field in the database.
$rule->enum('priority', [
'low' => 'Low Priority',
'medium' => 'Medium Priority',
'high' => 'High Priority'
])->default('medium');
radio(string $name, array $options): self
Creates radio buttons.
$rule->radio('gender', [
'M' => 'Male',
'F' => 'Female',
'O' => 'Other'
]);
checkboxes(string $name, array $options): self
Creates multiple checkboxes (stores as array).
$rule->checkboxes('features', [
'wifi' => 'WiFi',
'parking' => 'Parking',
'pool' => 'Swimming Pool'
]);
File Upload Fields
file(string $name): self
Creates a file upload field.
// Single file
$rule->file('document')
->accept('.pdf,.doc,.docx')
->maxSize(5 * 1024 * 1024) // 5MB
->uploadDir('uploads/documents');
// Multiple files
$rule->file('attachments')
->multiple()
->maxFiles(5)
->accept('.pdf,.jpg,.png');
image(string $name): self
Creates an image upload field (automatically accepts image/* types).
// Single image
$rule->image('profile_picture')
->maxSize(2 * 1024 * 1024) // 2MB
->uploadDir('uploads/profiles');
// Multiple images
$rule->image('gallery')
->multiple()
->maxFiles(10)
->uploadDir('uploads/gallery');
Constraint Methods
required(): self
Makes the field required (NOT NULL in database + form validation).
$rule->string('name', 100)
->required();
nullable(bool $nullable = true): self
Allows NULL values (opposite of required).
$rule->string('middle_name', 50)
->nullable();
default($value): self
Sets default value for the field.
$rule->boolean('is_active')
->default(true);
$rule->string('status', 20)
->default('pending');
$rule->int('views')
->default(0);
unique(): self
Creates a UNIQUE constraint in the database.
$rule->string('username', 50)
->required()
->unique();
$rule->email('user_email')
->unique();
index(): self
Creates a database index for faster queries.
// Foreign keys should have indexes
$rule->int('category_id')
->index();
// Frequently searched fields
$rule->string('sku', 50)
->index();
unsigned(): self
Makes numeric fields unsigned (only positive values).
$rule->int('quantity')
->unsigned()
->default(0);
Numeric Constraints
// min($value): Minimum value
$rule->int('age')
->min(18);
// max($value): Maximum value
$rule->int('quantity')
->max(9999);
// step($value): Step increment (for HTML5 input)
$rule->decimal('price', 10, 2)
->step(0.01)
->min(0);
Display Control Methods
hideFromList(): self
Hides field from table/list views.
$rule->text('long_description')
->hideFromList(); // Don't show in lists
hideFromEdit(): self
Hides field from edit forms.
$rule->datetime('created_at')
->hideFromEdit(); // Auto-set, don't allow editing
hideFromView(): self
Hides field from detail/view pages.
$rule->string('password_hash', 255)
->hideFromView(); // Never display passwords
excludeFromDatabase(): self
Field won't be created in database (virtual/computed fields).
$rule->string('full_name', 255)
->excludeFromDatabase() // Computed from first_name + last_name
->getter(function($obj) {
return $obj->first_name . ' ' . $obj->last_name;
});
Form Customization
label(string $label): self
Sets the display label for the field.
$rule->string('user_email', 100)
->label('Email Address')
->required();
formType(string $type): self
Sets the HTML5 input type.
$rule->string('website', 255)
->formType('url');
$rule->string('phone', 25)
->formType('tel');
$rule->text('content')
->formType('textarea');
formLabel(string $label): self
Sets a different label specifically for forms.
$rule->string('pwd', 255)
->label('Password') // Display label
->formLabel('Enter Password'); // Form-specific label
formParams(array $params): self
Sets custom form parameters (HTML attributes).
$rule->string('username', 50)
->formParams([
'placeholder' => 'Enter username',
'autocomplete' => 'username',
'pattern' => '[a-zA-Z0-9]+',
'minlength' => 3,
'maxlength' => 50
]);
error(string $message): self
Sets custom validation error message.
$rule->string('username', 50)
->required()
->error('Username is required and must be 3-50 characters');
Relationship Methods
hasOne(string $alias, string $relatedModel, string $foreignKey, string $onDelete = 'CASCADE'): self
Defines a one-to-one relationship where the foreign key is in the RELATED table.
// Actor hasOne Biography (biography.actor_id references actor.id)
class ActorsModel extends AbstractModel {
protected function configure($rule): void {
$rule->table('#__actors')
->id()->hasOne('biography', BiographyModel::class, 'actor_id', 'CASCADE')
->string('name', 100)->required();
}
}
// Access the relationship
$actor = $actorsModel->getById(1);
$bio = $actor->biography; // Lazy loads BiographyModel
echo $bio->bio_text;
onDelete options:
CASCADE- Delete child record when parent is deletedSET NULL- Set foreign key to NULL in childRESTRICT- Prevent parent deletion if child exists
hasMany(string $alias, string $relatedModel, string $foreignKey, string $onDelete = 'CASCADE'): self
Defines a one-to-many relationship where the foreign key is in the RELATED table.
// Author hasMany Books (books.author_id references authors.id)
class AuthorsModel extends AbstractModel {
protected function configure($rule): void {
$rule->table('#__authors')
->id()->hasMany('books', BooksModel::class, 'author_id', 'CASCADE')
->string('name', 100)->required();
}
}
// Access the relationship
$author = $authorsModel->getById(1);
$books = $author->books; // Returns array of BooksModel instances
foreach ($books as $book) {
echo $book->title . "\n";
}
belongsTo(string $alias, string $relatedModel, string $relatedKey = 'id'): self
Defines a many-to-one relationship where the foreign key is in THIS table.
// Post belongsTo User (posts.user_id references users.id)
class PostsModel extends AbstractModel {
protected function configure($rule): void {
$rule->table('#__posts')
->id()
->string('title', 200)->required()
->int('user_id')->belongsTo('author', UsersModel::class, 'id')
->text('content');
}
}
// Access the relationship
$post = $postsModel->getById(1);
$author = $post->author; // Lazy loads UsersModel
echo $author->name;
Advanced Customization
Custom Getters and Setters
// getter(callable $fn): Custom getter function
$rule->string('price_formatted', 50)
->excludeFromDatabase()
->getter(function($obj) {
return '€' . number_format($obj->price, 2);
});
// setter(callable $fn): Custom setter function
$rule->string('email', 255)
->setter(function($value) {
return strtolower(trim($value));
});
// rawGetter(callable $fn): Raw value getter
$rule->datetime('created_at')
->rawGetter(function($value) {
return $value->format('Y-m-d H:i:s');
});
property(string $key, $value): self
Sets a custom property on the field configuration.
$rule->string('code', 50)
->property('auto-generate', true)
->property('prefix', 'PRD-');
customize(callable $callback): self
Applies a custom callback to modify field configuration.
$rule->string('status', 20)
->customize(function($fieldConfig) {
$fieldConfig['custom_validator'] = 'validateStatus';
$fieldConfig['allow_transitions'] = ['draft' => 'published'];
return $fieldConfig;
});
Complete Example
class ProductsModel extends AbstractModel
{
protected function configure($rule): void {
$rule->table('#__products')
// Primary Key
->id()
// Basic Fields
->string('sku', 50)
->required()
->unique()
->index()
->label('Product SKU')
->string('name', 200)
->required()
->label('Product Name')
->text('description')
->nullable()
->hideFromList()
// Numeric Fields
->decimal('price', 10, 2)
->required()
->min(0)
->step(0.01)
->default(0)
->int('stock_quantity')
->unsigned()
->default(0)
->min(0)
->decimal('weight', 8, 3)
->nullable()
->label('Weight (kg)')
// Selection Fields
->list('status', [
'draft' => 'Draft',
'active' => 'Active',
'discontinued' => 'Discontinued'
])->default('draft')
->list('category_id', (new CategoriesModel())->getList())
->required()
->index()
// Relationships
->int('brand_id')
->index()
->belongsTo('brand', BrandsModel::class, 'id')
->id()
->hasMany('images', ProductImagesModel::class, 'product_id', 'CASCADE')
->hasMany('reviews', ProductReviewsModel::class, 'product_id', 'CASCADE')
// Boolean Fields
->boolean('featured')
->default(false)
->boolean('is_active')
->default(true)
// File Upload
->image('main_image')
->nullable()
->maxSize(5 * 1024 * 1024)
->uploadDir('uploads/products')
->accept('image/*')
// Date Fields
->datetime('created_at')
->nullable()
->hideFromEdit()
->datetime('updated_at')
->nullable()
->hideFromEdit()
// Virtual Field (computed)
->string('price_formatted', 50)
->excludeFromDatabase()
->hideFromEdit()
->getter(function($obj) {
return '€' . number_format($obj->price, 2);
});
}
}
Field Types Reference
| Method | Database Type | Description |
|---|---|---|
id($name) | INT AUTO_INCREMENT | Auto-increment primary key |
string($name, $len) | VARCHAR($len) | Variable length string |
text($name) | TEXT | Long text content |
email($name) | VARCHAR(255) | Email with validation |
tel($name) | VARCHAR(25) | Telephone number |
url($name) | VARCHAR(255) | URL with validation |
int($name) | INT | Integer number |
decimal($n, $l, $p) | DECIMAL($l,$p) | Precise decimal number |
boolean($name) | TINYINT(1) | True/False value |
datetime($name) | DATETIME | Date and time |
date($name) | DATE | Date only |
time($name) | TIME | Time only |
list($name, $opts) | varies | Dropdown select |
enum($name, $opts) | ENUM | Database enum |
radio($name, $opts) | varies | Radio buttons |
checkboxes($n, $o) | TEXT/JSON | Multiple selection |
file($name) | VARCHAR(255) | File upload |
image($name) | VARCHAR(255) | Image upload |
Tests
php vendor/bin/phpunit tests/Unit/Builders/RuleBuilderMethodsTest.php
See Also
- Model Overview - General Model concepts
- Relationships - Detailed relationship documentation
- Schema Management - Advanced schema operations