-
Notifications
You must be signed in to change notification settings - Fork 0
Entity Definition
In BOUNDLY, your domain entities are your database schema. No migrate files needed. Just attributes.
Infrastructure\FrameworkCore\Attributes\Schema\Required for every persistent class. Marks a class as a database entity.
| Parameter | Type | Default | Description |
|---|---|---|---|
table |
string |
Required | The database table name |
resource |
string |
null |
The API resource name (plural). Defaults to table name |
morphName |
string |
null |
Morph map alias for polymorphic relations |
connection |
string |
'mysql' |
Database connection to use |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Entity;
#[Entity(table: 'users', resource: 'users')]
class User extends AggregateRoot { ... }Defines a database column with its properties.
| Parameter | Type | Default | Description |
|---|---|---|---|
type |
string |
'string' |
Column type (see below) |
length |
int |
255 |
Max length for string types |
nullable |
bool |
false |
Allow NULL values |
default |
mixed |
null |
Default value |
unique |
bool |
false |
Add unique index |
roles |
array |
[] |
Role-based visibility control |
Supported Column Types:
| Type | SQL | Notes |
|---|---|---|
string |
VARCHAR |
Variable length |
text |
TEXT |
Unlimited text |
integer |
INT |
Whole numbers |
bigint |
BIGINT |
Large integers (auto-mapped to Laravel's bigInteger) |
boolean |
TINYINT(1) |
0 or 1 |
decimal(p,s) |
DECIMAL |
Precise numbers (e.g., 'decimal(10,2)') |
date |
DATE |
YYYY-MM-DD |
datetime |
DATETIME |
Date and time |
timestamp |
TIMESTAMP |
Unix timestamp |
json |
JSON |
JSON data |
uuid |
CHAR(36) |
UUID string |
Note: When using
#[Column]attributes, if you also use#[Auditable], ensure you don't duplicatecreated_byorupdated_bycolumns. The framework will auto-add these audit columns only if they are not already defined as#[Column]attributes.
Example:
#[Column(type: 'string', length: 150, nullable: true, default: 'N/A')]
private string $nickname;
#[Column(type: 'decimal(10,2)', default: 0.00)]
private string $price;
#[Column(type: 'json')]
private array $metadata;Marks a property as the primary key. By default, it creates an auto-incrementing BIGINT.
| Parameter | Type | Default | Description |
|---|---|---|---|
autoIncrement |
bool |
true |
Auto-increment the ID |
Example:
#[Id]
private int $id;Extended primary key configuration. Use this when you need custom column types or non-auto-incrementing keys (e.g., UUIDs).
| Parameter | Type | Default | Description |
|---|---|---|---|
autoIncrement |
bool |
true |
Auto-increment the ID |
type |
string |
'bigint' |
Column type (bigint, int, uuid) |
Use Case: When using UUIDs or string-based primary keys.
Example:
// UUID Primary Key
#[PrimaryKey(autoIncrement: false, type: 'uuid')]
private string $id;
// Custom BIGINT Primary Key
#[PrimaryKey(autoIncrement: true, type: 'bigint')]
private int $id;Defines a foreign key constraint with cascade behavior.
| Parameter | Type | Default | Description |
|---|---|---|---|
entity |
string |
Required | Related entity class name |
column |
string |
null |
FK column name (auto-generated if null) |
onDelete |
string |
'RESTRICT' |
Action on delete (CASCADE, SET NULL, RESTRICT, NO ACTION) |
onUpdate |
string |
'CASCADE' |
Action on update (CASCADE, SET NULL, RESTRICT, NO ACTION) |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\{Entity, Column, ForeignKey};
use Infrastructure\FrameworkCore\Attributes\Relations\BelongsTo;
#[Entity(table: 'posts', resource: 'posts')]
class Post extends AggregateRoot
{
#[Id]
private int $id;
#[Column(type: 'string', length: 255)]
private string $title;
#[ForeignKey(entity: User::class, onDelete: 'CASCADE')]
#[BelongsTo(relatedEntity: User::class)]
private array $author;
}Cascade Options:
| Option | Behavior |
|---|---|
CASCADE |
Delete/update related rows |
SET NULL |
Set FK to NULL |
RESTRICT |
Prevent delete/update if related rows exist |
NO ACTION |
Similar to RESTRICT, checked after constraints |
Creates database indexes for query performance optimization.
| Parameter | Type | Default | Description |
|---|---|---|---|
columns |
array |
null |
Column(s) to index (null = property name) |
name |
string |
null |
Custom index name |
unique |
bool |
false |
Create unique index |
Use Case: Speed up lookups on frequently queried columns.
Examples:
// Single column index (auto-detect column name)
#[Index]
#[Column(type: 'string', length: 150)]
private string $email;
// Single column index (explicit)
#[Index(columns: ['email'])]
#[Column(type: 'string', length: 150)]
private string $email;
// Composite index (multiple columns)
#[Index(columns: ['last_name', 'first_name', 'created_at'])]
private array $name;
// Unique index
#[Index(unique: true)]
#[Column(type: 'string', length: 100)]
private string $slug;
// Named index
#[Index(name: 'idx_user_status', columns: ['user_id', 'status'])]
private array $userStatus;Defines composite unique constraints across multiple columns at the entity level.
| Parameter | Type | Default | Description |
|---|---|---|---|
columns |
array |
Required | Array of column names |
name |
string |
null |
Custom constraint name |
Use Case: Enforce business rules like "no duplicate active records per tenant".
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\{Entity, Column, Id, UniqueConstraint};
#[Entity(table: 'product_variants', resource: 'product-variants')]
#[UniqueConstraint(columns: ['product_id', 'sku'], name: 'unique_product_sku')]
class ProductVariant extends AggregateRoot
{
#[Id]
private int $id;
#[Column(type: 'integer')]
private int $productId;
#[Column(type: 'string', length: 100)]
private string $sku;
#[Column(type: 'string', length: 50)]
private string $size;
}Generated SQL:
CREATE UNIQUE INDEX unique_product_sku ON product_variants (product_id, sku);use Infrastructure\FrameworkCore\Attributes\Schema\{Entity, Column, Id, PrimaryKey, ForeignKey, Index, UniqueConstraint};
use Infrastructure\FrameworkCore\Attributes\Relations\{BelongsTo, HasMany, ManyToMany};
use Domain\Shared\Entities\AggregateRoot;
#[Entity(table: 'posts', resource: 'posts')]
#[UniqueConstraint(columns: ['author_id', 'slug'], name: 'unique_author_slug')]
class Post extends AggregateRoot
{
#[PrimaryKey(autoIncrement: true, type: 'bigint')]
private int $id;
#[ForeignKey(entity: User::class, onDelete: 'CASCADE')]
#[BelongsTo(relatedEntity: User::class)]
private array $author;
#[Index]
#[Column(type: 'string', length: 255)]
private string $title;
#[Index(unique: true)]
#[Column(type: 'string', length: 100)]
private string $slug;
#[Column(type: 'text')]
private string $content;
#[Column(type: 'boolean', default: false)]
private bool $isPublished;
#[Column(type: 'datetime')]
private string $publishedAt;
#[Column(type: 'json')]
private array $metadata;
#[HasMany(relatedEntity: Comment::class)]
private array $comments;
#[ManyToMany(relatedEntity: Category::class)]
private array $categories;
}Based on the example above, BOUNDLY would generate:
CREATE TABLE posts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
author_id BIGINT UNSIGNED NOT NULL,
title VARCHAR(255),
slug VARCHAR(100) UNIQUE,
content TEXT,
is_published TINYINT(1) DEFAULT 0,
published_at DATETIME,
metadata JSON,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_posts_title (title)
);
CREATE UNIQUE INDEX unique_author_slug ON posts (author_id, slug);
CREATE TABLE category_post (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
post_id BIGINT UNSIGNED NOT NULL,
category_id BIGINT UNSIGNED NOT NULL,
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
);Next Step: Relations-Definition π