Skip to content

Behavioral Traits

Jean-Marc Strauven edited this page Aug 6, 2025 · 2 revisions

🎯 Behavioral Traits

Behavioral traits add common functionality to your DTOs without writing repetitive code. They provide standardized patterns for timestamps, UUIDs, soft deletes, and more.

How Traits Work

# In your YAML definition
header:
  class: UserDto
  namespace: App\DTOs
  traits: [HasTimestamps, HasUuid, SoftDeletes]  # Add behavioral traits

# Laravel Arc automatically adds:
# - Timestamp fields (created_at, updated_at)
# - UUID field with auto-generation
# - Soft delete functionality

Traits automatically add fields, methods, and functionality to your generated DTOs.

Available Traits

HasTimestamps

Adds automatic timestamp tracking:

header:
  traits: [HasTimestamps]

# Automatically adds these fields:
fields:
  created_at:
    type: datetime
    auto_fill: true
    description: "Creation timestamp"
    
  updated_at:
    type: datetime
    auto_fill: true
    description: "Last update timestamp"

Generated Methods:

// Touch updated_at timestamp
$dto->touch();

// Check if recently created (within last hour)
$dto->wasRecentlyCreated(); // boolean

// Get age of record
$dto->getAge(); // Carbon diff string

HasUuid

Adds UUID field with automatic generation:

header:
  traits: [HasUuid]

# Automatically adds:
fields:
  uuid:
    type: uuid
    auto_fill: true
    unique: true
    description: "Unique identifier"

Generated Methods:

// Get UUID string
$dto->getUuid(); // "550e8400-e29b-41d4-a716-446655440000"

// Generate new UUID
$dto->regenerateUuid();

// Find by UUID (when used with collections)
UserDto::findByUuid($uuid);

SoftDeletes

Adds soft delete functionality:

header:
  traits: [SoftDeletes]

# Automatically adds:
fields:
  deleted_at:
    type: datetime
    nullable: true
    description: "Soft delete timestamp"

Generated Methods:

// Soft delete
$dto->delete();

// Restore
$dto->restore();

// Check if deleted
$dto->isDeleted(); // boolean

// Force delete (permanent)
$dto->forceDelete();

HasTags

Adds tagging functionality:

header:
  traits: [HasTags]

# Automatically adds:
fields:
  tags:
    type: array
    items:
      type: string
    transformers: [trim_array, lowercase_array, unique_array]
    description: "Associated tags"

Generated Methods:

// Add tags
$dto->addTag('important');
$dto->addTags(['urgent', 'review']);

// Remove tags
$dto->removeTag('outdated');
$dto->removeTags(['old', 'deprecated']);

// Check tags
$dto->hasTag('important'); // boolean
$dto->hasAnyTag(['urgent', 'priority']); // boolean
$dto->hasAllTags(['important', 'urgent']); // boolean

// Get tags
$dto->getTags(); // array
$dto->getTagsAsString(); // "important, urgent, review"

HasStatus

Adds status management with predefined states:

header:
  traits: [HasStatus]

# Configuration (optional)
trait_config:
  HasStatus:
    default: "draft"
    states: [draft, published, archived, deleted]
    transitions:
      draft: [published, archived]
      published: [archived, deleted]
      archived: [published, deleted]

# Automatically adds:
fields:
  status:
    type: string
    default: "draft"
    enum: [draft, published, archived, deleted]

Generated Methods:

// Change status
$dto->setStatus('published');

// Check status
$dto->isDraft(); // boolean
$dto->isPublished(); // boolean
$dto->isArchived(); // boolean

// Status transitions
$dto->publish(); // draft β†’ published
$dto->archive(); // any β†’ archived
$dto->restore(); // archived β†’ previous state

// Get available transitions
$dto->getAvailableTransitions(); // array
$dto->canTransitionTo('published'); // boolean

HasMetadata

Adds flexible metadata storage:

header:
  traits: [HasMetadata]

# Automatically adds:
fields:
  metadata:
    type: json
    default: {}
    description: "Additional metadata"

Generated Methods:

// Set metadata
$dto->setMeta('color', 'blue');
$dto->setMeta('settings', ['theme' => 'dark']);

// Get metadata
$dto->getMeta('color'); // "blue"
$dto->getMeta('missing', 'default'); // "default"
$dto->getAllMeta(); // array

// Check metadata
$dto->hasMeta('color'); // boolean

// Remove metadata
$dto->removeMeta('color');
$dto->clearMeta();

HasSlug

Adds automatic slug generation:

header:
  traits: [HasSlug]

# Configuration
trait_config:
  HasSlug:
    source: "title"  # Field to generate slug from
    unique: true

# Automatically adds:
fields:
  slug:
    type: string
    unique: true
    transformers: [slugify]

Generated Methods:

// Generate slug from title
$dto->generateSlug(); // "my-awesome-title"

// Regenerate slug
$dto->regenerateSlug();

// Get URL-friendly slug
$dto->getSlug(); // "my-awesome-title"

Combining Multiple Traits

You can use multiple traits together:

header:
  class: BlogPostDto
  namespace: App\DTOs\Blog
  traits: [HasTimestamps, HasUuid, HasSlug, HasTags, HasStatus, SoftDeletes]

# Configuration for multiple traits
trait_config:
  HasSlug:
    source: "title"
    unique: true
  HasStatus:
    default: "draft"
    states: [draft, review, published, archived]
    transitions:
      draft: [review]
      review: [draft, published, archived]
      published: [archived]
      archived: [published]

fields:
  title:
    type: string
    required: true
    max_length: 255
    
  content:
    type: text
    required: true
    
  author_id:
    type: integer
    required: true

Generated DTO includes all trait functionality:

$post = BlogPostDto::from([
    'title' => 'My Awesome Blog Post',
    'content' => 'This is the content...',
    'author_id' => 1
]);

// Trait methods available
$post->generateSlug();        // HasSlug
$post->addTag('tutorial');    // HasTags
$post->setStatus('review');   // HasStatus
$post->touch();               // HasTimestamps
echo $post->getUuid();        // HasUuid

Custom Traits

Create your own behavioral traits:

1. Create Trait Class

<?php

namespace App\Traits\DTO;

trait HasPriority
{
    public function setPriority(string $priority): static
    {
        if (!in_array($priority, ['low', 'medium', 'high', 'urgent'])) {
            throw new InvalidArgumentException("Invalid priority: {$priority}");
        }
        
        return $this->with(['priority' => $priority]);
    }
    
    public function isHighPriority(): bool
    {
        return in_array($this->priority, ['high', 'urgent']);
    }
    
    public function isUrgent(): bool
    {
        return $this->priority === 'urgent';
    }
    
    public function getPriorityColor(): string
    {
        return match($this->priority) {
            'low' => 'green',
            'medium' => 'yellow',
            'high' => 'orange',
            'urgent' => 'red',
        };
    }
}

2. Register Trait

// In AppServiceProvider
use Grazulex\LaravelArc\Support\Traits\BehavioralTraitRegistry;

public function boot()
{
    BehavioralTraitRegistry::register('HasPriority', [
        'trait_class' => App\Traits\DTO\HasPriority::class,
        'fields' => [
            'priority' => [
                'type' => 'string',
                'default' => 'medium',
                'enum' => ['low', 'medium', 'high', 'urgent'],
                'description' => 'Task priority level'
            ]
        ]
    ]);
}

3. Use in YAML

header:
  class: TaskDto
  namespace: App\DTOs
  traits: [HasTimestamps, HasPriority]

fields:
  title:
    type: string
    required: true
  description:
    type: text

Real-World Examples

User Management DTO

header:
  class: UserDto
  namespace: App\DTOs\Users
  traits: [HasTimestamps, HasUuid, SoftDeletes, HasStatus, HasMetadata]

trait_config:
  HasStatus:
    default: "pending"
    states: [pending, active, inactive, banned, deleted]
    transitions:
      pending: [active, inactive]
      active: [inactive, banned]
      inactive: [active, banned]
      banned: [deleted]

fields:
  name:
    type: string
    required: true
    max_length: 255
    
  email:
    type: email
    required: true
    unique: true
    
  email_verified_at:
    type: datetime
    nullable: true

Usage:

$user = UserDto::from([
    'name' => 'John Doe',
    'email' => '[email protected]'
]);

// Trait functionality
$user->setStatus('active');
$user->setMeta('last_login_ip', '192.168.1.1');
$user->setMeta('preferences', ['theme' => 'dark']);

// Check status
if ($user->isActive()) {
    // User can access system
}

// Soft delete
$user->delete(); // Sets deleted_at timestamp

Content Management DTO

header:
  class: ArticleDto
  namespace: App\DTOs\Content
  traits: [HasTimestamps, HasSlug, HasTags, HasStatus, HasMetadata]

trait_config:
  HasSlug:
    source: "title"
    unique: true
  HasStatus:
    default: "draft"
    states: [draft, review, published, featured, archived]

fields:
  title:
    type: string
    required: true
    max_length: 255
    
  excerpt:
    type: text
    max_length: 500
    
  content:
    type: text
    required: true
    
  author_id:
    type: integer
    required: true
    
  category_id:
    type: integer
    required: true

Usage:

$article = ArticleDto::from([
    'title' => 'Understanding Laravel Arc',
    'content' => 'Laravel Arc is...',
    'author_id' => 1,
    'category_id' => 5
]);

// Auto-generate slug from title
$article->generateSlug(); // "understanding-laravel-arc"

// Add tags
$article->addTags(['laravel', 'php', 'tutorial']);

// Set metadata
$article->setMeta('reading_time', '5 minutes');
$article->setMeta('difficulty', 'beginner');

// Publish article
$article->setStatus('published');

Real-World Generated Example

YAML Definition with Multiple Traits

From examples/modern-traits-comprehensive.yaml:

# Comprehensive example showing all behavioral traits working together
header:
  dto: AdvancedUserDTO
  model: App\Models\User
  namespace: App\DTO
  traits:
    - HasTimestamps    # Adds created_at, updated_at
    - HasUuid         # Adds UUID field
    - HasSoftDeletes  # Adds deleted_at, restore methods
    - HasTagging      # Adds tags field and tag methods
    - HasAuditing     # Adds creator_id, updater_id tracking
    - HasCaching      # Adds cache methods
    - HasSlug         # Adds slug field from name
    - HasStatus       # Adds status field with state management
    - ValidatesData   # Adds validation methods
    - ConvertsData    # Adds export methods

fields:
  name:
    type: string
    required: true
    validation: [required, string, min:2, max:100]
    transformers: [trim, title_case]
  
  email:
    type: string
    required: true
    validation: [required, email, unique:users]
    transformers: [trim, lowercase]
  
  bio:
    type: text
    required: false
    validation: [nullable, string, max:1000]
    transformers: [trim]

# Traits configuration
trait_options:
  HasSlug:
    source_field: name
    unique: true
  HasStatus:
    default: "active"
    allowed: [active, inactive, pending, banned]
  HasCaching:
    ttl: 3600

Generated DTO with All Traits

The above YAML generates a comprehensive DTO:

<?php

declare(strict_types=1);

namespace App\DTO;

use Grazulex\LaravelArc\Support\Traits\HasTimestamps;
use Grazulex\LaravelArc\Support\Traits\HasUuid;
use Grazulex\LaravelArc\Support\Traits\HasSoftDeletes;
use Grazulex\LaravelArc\Support\Traits\HasTagging;
use Grazulex\LaravelArc\Support\Traits\HasAuditing;
use Grazulex\LaravelArc\Support\Traits\HasCaching;
use Grazulex\LaravelArc\Support\Traits\HasSlug;
use Grazulex\LaravelArc\Support\Traits\HasStatus;
use Grazulex\LaravelArc\Support\Traits\ValidatesData;
use Grazulex\LaravelArc\Support\Traits\ConvertsData;

final readonly class AdvancedUserDTO
{
    use HasTimestamps, HasUuid, HasSoftDeletes, HasTagging, 
        HasAuditing, HasCaching, HasSlug, HasStatus,
        ValidatesData, ConvertsData;

    public function __construct(
        public string $name,
        public string $email,
        public ?string $bio = null,
        
        // From traits
        public string $id,              // HasUuid
        public string $slug,            // HasSlug  
        public string $status,          // HasStatus
        public array $tags,             // HasTagging
        public ?\DateTime $created_at = null,   // HasTimestamps
        public ?\DateTime $updated_at = null,   // HasTimestamps
        public ?\DateTime $deleted_at = null,   // HasSoftDeletes
        public ?int $creator_id = null,         // HasAuditing
        public ?int $updater_id = null,         // HasAuditing
    ) {}

    // All trait methods are available:
    // $dto->touch()           // HasTimestamps
    // $dto->addTag('vip')     // HasTagging
    // $dto->setStatus('active') // HasStatus
    // $dto->cache(3600)       // HasCaching
    // $dto->isDeleted()       // HasSoftDeletes
    // $dto->getSlug()         // HasSlug
}

Trait Configuration

Global Configuration

Configure traits globally in config/arc.php:

'behavioral_traits' => [
    'HasTimestamps' => [
        'created_field' => 'created_at',
        'updated_field' => 'updated_at',
        'auto_update' => true,
    ],
    
    'HasUuid' => [
        'field_name' => 'uuid',
        'version' => 4,
        'auto_generate' => true,
    ],
    
    'HasStatus' => [
        'default_states' => ['draft', 'published', 'archived'],
        'field_name' => 'status',
    ],
];

Per-DTO Configuration

Override defaults in individual YAML files:

header:
  traits: [HasTimestamps, HasStatus]

trait_config:
  HasTimestamps:
    created_field: "date_created"
    updated_field: "date_modified"
  HasStatus:
    field_name: "state"
    default: "new"
    states: [new, processing, completed, failed]

What's Next?

Now that you understand behavioral traits:


Behavioral traits eliminate boilerplate code and provide consistent patterns across your DTOs. Use them to build feature-rich data objects effortlessly. ⚑

πŸš€ Laravel Arc Wiki

🏠 Home

πŸš€ Getting Started

πŸ“š Core Concepts

πŸ—οΈ Advanced Features

βš™οΈ Configuration & CLI

🌐 Real-World Examples


🎯 Key Concepts

YAML β†’ DTO β†’ Type-Safe Code

Laravel Arc transforms your YAML definitions into powerful PHP DTOs with automatic validation, field transformers, and behavioral traits.

πŸ”— Quick Links

Clone this wiki locally