Models 
Models in AvelPress are classes that represent tables in your database. They provide an elegant and simple ActiveRecord implementation for working with your database, following Laravel's Eloquent conventions while being optimized for WordPress.
Introduction 
Every model class corresponds to a database table. Models allow you to query, insert, update, and delete records from their corresponding tables using an expressive, fluent syntax.
Creating Models 
Using the CLI 
The easiest way to create a model is using the AvelPress CLI:
# Basic model
avel make:model User
# Model with fillable attributes and timestamps
avel make:model User --fillable=name,email,status --timestamps
# Model in a specific module
avel make:model User --module=Auth --fillable=name,email --timestamps
# Model with custom table name and prefix
avel make:model User --table=wp_custom_users --prefix=custom_ --fillable=name,emailThe CLI will automatically:
- Create the model file with proper namespace
 - Set up fillable attributes if provided
 - Enable timestamps if specified
 - Add table prefix if specified
 - Use proper WordPress coding standards
 - Place the model in the correct directory structure
 
Manual Model Creation 
You can also create models manually by extending the base Model class:
<?php
namespace App\Models;
use AvelPress\Database\Eloquent\Model;
defined( 'ABSPATH' ) || exit;
class User extends Model {
    protected $table = 'users';
    protected $primaryKey = 'id';
    public $timestamps = true;
    protected $fillable = [
        'name',
        'email',
        'status',
    ];
    protected $hidden = [
        'password',
        'remember_token',
    ];
}Model Properties 
Table Name 
By convention, models use the pluralized, snake_case version of the class name as the table name:
class User extends Model
{
    // Uses table: users (auto-generated)
}
class ProductCategory extends Model
{
    // Uses table: product_categories (auto-generated)
}
// Custom table name
class User extends Model
{
    protected $table = 'custom_users';
}Table Prefix 
If your tables use a custom prefix, you can specify it:
class User extends Model
{
    protected $prefix = 'custom_';
}Primary Key 
Models assume the primary key is named id and is an auto-incrementing integer:
class User extends Model
{
    // Custom primary key
    protected $primaryKey = 'user_id';
    // Non-incrementing primary key
    public $incrementing = false;
    // String primary key
    protected $keyType = 'string';
}Table Prefix 
Set a custom table prefix for your model:
class User extends Model
{
    protected $prefix = 'my_plugin_';
    // Table name will be: my_plugin_users
}Timestamps 
Enable automatic timestamp management:
class User extends Model
{
    public $timestamps = true;
    // Automatically manages created_at and updated_at
}
// Custom timestamp columns
class User extends Model
{
    public $timestamps = true;
    protected $createdAtColumn = 'created_on';
    protected $updatedAtColumn = 'modified_on';
}Mass Assignment 
Fillable Attributes 
Define which attributes can be mass-assigned for security:
class User extends Model
{
    protected $fillable = [
        'name',
        'email',
        'status',
        'bio',
    ];
}
// Mass assignment works
$user = User::create([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'status' => 'active'
]);Guarded Attributes 
Alternatively, specify which attributes should be protected:
class User extends Model
{
    protected $guarded = [
        'id',
        'password',
        'admin_level',
    ];
    // All other attributes are fillable
}Retrieving Models 
All Records 
// Get all users
$users = User::all();
// Get all with specific columns
$users = User::all(['name', 'email']);Finding Records 
// Find by primary key
$user = User::find(1);
// Find multiple records
$users = User::find([1, 2, 3]);
// Find or fail (throws exception)
$user = User::findOrFail(1);
// First record matching criteria
$user = User::where('email', 'john@example.com')->first();
// First or fail
$user = User::where('active', true)->firstOrFail();Query Conditions 
// Basic where
$activeUsers = User::where('status', 'active')->get();
// Multiple conditions
$users = User::where('status', 'active')
    ->where('age', '>', 18)
    ->get();
// Or conditions
$users = User::where('status', 'active')
    ->orWhere('status', 'premium')
    ->get();
// Where in
$users = User::whereIn('status', ['active', 'premium'])->get();
// Date queries
$recentUsers = User::where('created_at', '>', '2024-01-01')->get();
// Null checks
$unverifiedUsers = User::whereNull('email_verified_at')->get();Ordering and Limiting 
// Order by
$users = User::orderBy('created_at', 'desc')->get();
// Multiple ordering
$users = User::orderBy('status')
    ->orderBy('name', 'asc')
    ->get();
// Limit and offset
$users = User::limit(10)->offset(20)->get();
// Latest and oldest
$latestUsers = User::latest()->get(); // Orders by created_at desc
$oldestUsers = User::oldest()->get(); // Orders by created_at ascAggregates 
// Count
$count = User::count();
$activeCount = User::where('status', 'active')->count();
// Exists
$exists = User::where('email', 'john@example.com')->exists();
// Max, min, average, sum
$maxAge = User::max('age');
$minAge = User::min('age');
$avgAge = User::avg('age');
$totalPoints = User::sum('points');Creating and Updating 
Creating Records 
// Method 1: New instance
$user = new User;
$user->name = 'John Doe';
$user->email = 'john@example.com';
$user->save();
// Method 2: Mass assignment
$user = User::create([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'status' => 'active'
]);
// Method 3: First or create
$user = User::firstOrCreate(
    ['email' => 'john@example.com'], // Search criteria
    ['name' => 'John Doe', 'status' => 'active'] // Additional data if creating
);
// Method 4: Update or create
$user = User::updateOrCreate(
    ['email' => 'john@example.com'], // Search criteria  
    ['name' => 'John Smith', 'status' => 'active'] // Data to update/create
);Updating Records 
// Update single model
$user = User::find(1);
$user->name = 'Jane Doe';
$user->save();
// Mass update single model
$user->update(['name' => 'Jane Doe', 'status' => 'inactive']);
// Update multiple models
User::where('status', 'pending')
    ->update(['status' => 'active']);
// Increment/decrement
$user = User::find(1);
$user->increment('points'); // Increment by 1
$user->increment('points', 5); // Increment by 5
$user->decrement('points', 2); // Decrement by 2Bulk Operations 
// Insert multiple records
User::createMany([
    ['name' => 'John', 'email' => 'john@example.com'],
    ['name' => 'Jane', 'email' => 'jane@example.com'],
    ['name' => 'Bob', 'email' => 'bob@example.com']
]);Deleting Models 
Basic Deletion 
// Delete single model
$user = User::find(1);
$user->delete();
// Delete by ID
User::destroy(1);
// Delete multiple by ID
User::destroy([1, 2, 3]);
// Delete by query
User::where('status', 'inactive')->delete();Soft Deletes 
Enable soft deletes to mark records as deleted without removing them:
<?php
namespace App\Models;
use AvelPress\Database\Eloquent\Model;
use AvelPress\Database\Eloquent\SoftDeletes;
class User extends Model
{
    use SoftDeletes;
    protected $fillable = ['name', 'email'];
}// Soft delete (sets deleted_at timestamp)
$user = User::find(1);
$user->delete();
// Include soft deleted models
$users = User::withTrashed()->get();
// Only soft deleted models
$deletedUsers = User::onlyTrashed()->get();
// Restore soft deleted model
$user = User::withTrashed()->find(1);
$user->restore();
// Force delete (permanent deletion)
$user->forceDelete();Relationships 
One to One 
class User extends Model
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}
class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
// Usage
$user = User::find(1);
$profile = $user->profile;
$profile = Profile::find(1);
$user = $profile->user;One to Many 
class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}
class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
// Usage
$user = User::find(1);
$posts = $user->posts;
foreach ($posts as $post) {
    echo $post->title;
}
// Create related model
$user = User::find(1);
$post = $user->posts()->create([
    'title' => 'New Post',
    'content' => 'Post content...'
]);Custom Foreign Keys 
class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class, 'author_id', 'id');
    }
}
class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }
}Eager Loading 
Prevent N+1 query problems by eager loading relationships:
// Lazy loading (N+1 problem)
$users = User::all();
foreach ($users as $user) {
    echo $user->profile->bio; // Executes a query for each user
}
// Eager loading (efficient)
$users = User::with('profile')->get();
foreach ($users as $user) {
    echo $user->profile->bio; // Profile already loaded
}
// Multiple relationships
$users = User::with(['posts', 'profile'])->get();
// Nested relationships
$users = User::with('posts.comments')->get();
// Conditional eager loading
$users = User::with(['posts' => function ($query) {
    $query->where('status', 'published');
}])->get();Relationship Queries 
// Query through relationships
$users = User::whereHas('posts', function ($query) {
    $query->where('status', 'published');
})->get();
// Count related models
$users = User::withCount('posts')->get();
foreach ($users as $user) {
    echo $user->posts_count;
}
// Exists queries
$hasPublishedPosts = User::whereHas('posts', function ($query) {
    $query->where('status', 'published');
})->exists();Accessors and Mutators 
Accessors 
Transform data when retrieving from the database:
class User extends Model
{
    // Get first name from full name
    public function getFirstNameAttribute()
    {
        return explode(' ', $this->name)[0];
    }
    // Format email as lowercase
    public function getEmailAttribute($value)
    {
        return strtolower($value);
    }
    // Get full URL for avatar
    public function getAvatarUrlAttribute()
    {
        if ($this->avatar) {
            return wp_upload_dir()['baseurl'] . '/avatars/' . $this->avatar;
        }
        return get_avatar_url($this->email);
    }
    // WordPress integration
    public function getGravatarAttribute()
    {
        return get_avatar_url($this->email, 80);
    }
}$user = User::find(1);
echo $user->first_name; // Calls getFirstNameAttribute()
echo $user->avatar_url; // Calls getAvatarUrlAttribute()
echo $user->gravatar; // WordPress gravatar URLMutators 
Transform data when saving to the database:
class User extends Model
{
    // Hash password automatically
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = wp_hash_password($value);
    }
    // Store email as lowercase
    public function setEmailAttribute($value)
    {
        $this->attributes['email'] = strtolower(trim($value));
    }
    // Auto-generate slug from title
    public function setTitleAttribute($value)
    {
        $this->attributes['title'] = $value;
        $this->attributes['slug'] = sanitize_title($value);
    }
    // WordPress meta integration
    public function setBioAttribute($value)
    {
        $this->attributes['bio'] = wp_kses_post($value);
    }
}$user = new User;
$user->password = 'plaintext'; // Automatically hashed
$user->email = 'JOHN@EXAMPLE.COM'; // Stored as lowercase
$user->title = 'My Blog Post'; // Generates slug automatically
$user->save();Attribute Casting 
Cast attributes to specific types automatically:
use AvelPress\Database\Eloquent\Casts\MoneyCast;
use AvelPress\Database\Eloquent\Casts\Attribute;
class Product extends Model
{
    protected function casts(): array
    {
        return [
            'price' => MoneyCast::class,
            'is_featured' => 'boolean',
            'published_at' => 'datetime',
            'metadata' => 'array',
        ];
    }
    // Custom attribute casting
    protected function description(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => ucfirst($value),
            set: fn ($value) => strtolower($value),
        );
    }
}$product = Product::find(1);
echo $product->price; // Automatically formatted as money
echo $product->is_featured; // Boolean value
echo $product->published_at->format('Y-m-d'); // Carbon instance
print_r($product->metadata); // Unserialized arrayAvailable Cast Types 
protected function casts(): array
{
    return [
        'price' => 'decimal:2',
        'is_active' => 'boolean',
        'options' => 'array',
        'settings' => 'object',
        'published_at' => 'datetime',
        'metadata' => 'json',
        'amount' => MoneyCast::class, // Custom cast
    ];
}Model Events 
Hook into model lifecycle events:
class User extends Model
{
    protected static function boot()
    {
        parent::boot();
        // Before creating
        static::creating(function ($user) {
            $user->uuid = wp_generate_uuid4();
            
            // WordPress integration
            if (!$user->slug) {
                $user->slug = sanitize_title($user->name);
            }
        });
        // After creating
        static::created(function ($user) {
            // Send welcome email
            wp_mail($user->email, 'Welcome!', 'Welcome to our site!');
            
            // Create user meta
            add_user_meta($user->wp_user_id, 'custom_field', 'value');
        });
        // Before updating
        static::updating(function ($user) {
            error_log("User {$user->id} is being updated");
        });
        // After updating
        static::updated(function ($user) {
            // Clear WordPress cache
            wp_cache_delete("user_{$user->id}");
            clean_user_cache($user->wp_user_id);
        });
        // Before deleting
        static::deleting(function ($user) {
            // Delete related data
            $user->posts()->delete();
            
            // WordPress cleanup
            wp_delete_user($user->wp_user_id);
        });
        // After deleting
        static::deleted(function ($user) {
            // Log deletion
            error_log("User {$user->id} was deleted");
        });
    }
}Available Events 
creating- Before a new record is createdcreated- After a new record is createdupdating- Before an existing record is updatedupdated- After an existing record is updatedsaving- Before creating or updating (both)saved- After creating or updating (both)deleting- Before a record is deleteddeleted- After a record is deletedrestoring- Before a soft-deleted record is restoredrestored- After a soft-deleted record is restored
Serialization 
Array and JSON Conversion 
$user = User::find(1);
// Convert to array
$array = $user->toArray();
// Convert to JSON
$json = $user->toJson();
// When returning from API routes, automatic conversion happens
return $user; // Automatically converts to JSON for API responsesControlling Serialization 
class User extends Model
{
    // Hide sensitive attributes
    protected $hidden = ['password', 'remember_token', 'api_key'];
    // Or specify only visible attributes
    protected $visible = ['id', 'name', 'email', 'created_at'];
    // Append accessor attributes
    protected $appends = ['first_name', 'avatar_url', 'gravatar'];
}$user = User::find(1);
$array = $user->toArray();
// Will not include 'password' or 'remember_token'
// Will include 'first_name', 'avatar_url', and 'gravatar' from accessorsTemporary Visibility Changes 
// Temporarily show hidden attributes
$user = User::find(1);
$array = $user->makeVisible(['password'])->toArray();
// Temporarily hide visible attributes
$array = $user->makeHidden(['email'])->toArray();WordPress Integration 
User Model Integration 
class User extends Model
{
    protected $fillable = [
        'name', 'email', 'wp_user_id', 'status'
    ];
    // Link to WordPress user
    public function wordpressUser()
    {
        return get_user_by('id', $this->wp_user_id);
    }
    // WordPress capabilities
    public function can($capability)
    {
        $wp_user = $this->wordpressUser();
        return $wp_user ? user_can($wp_user, $capability) : false;
    }
    // WordPress meta
    public function getMeta($key, $single = true)
    {
        return get_user_meta($this->wp_user_id, $key, $single);
    }
    public function updateMeta($key, $value)
    {
        return update_user_meta($this->wp_user_id, $key, $value);
    }
    // WordPress avatar
    public function getWpAvatarAttribute()
    {
        return get_avatar_url($this->email, 80);
    }
}Post Model Integration 
class Post extends Model
{
    protected $fillable = [
        'title', 'content', 'status', 'wp_post_id'
    ];
    protected function casts(): array
    {
        return [
            'published_at' => 'datetime',
        ];
    }
    // Link to WordPress post
    public function wordpressPost()
    {
        return get_post($this->wp_post_id);
    }
    // WordPress meta
    public function getMeta($key, $single = true)
    {
        return get_post_meta($this->wp_post_id, $key, $single);
    }
    public function updateMeta($key, $value)
    {
        return update_post_meta($this->wp_post_id, $key, $value);
    }
    // WordPress permalink
    public function getPermalinkAttribute()
    {
        return get_permalink($this->wp_post_id);
    }
    // WordPress excerpt
    public function getExcerptAttribute()
    {
        $wp_post = $this->wordpressPost();
        return $wp_post ? wp_trim_excerpt('', $wp_post) : null;
    }
}Advanced Features 
Pagination 
// Paginate results
$users = User::paginate(15); // 15 per page
// Custom pagination
$users = User::where('status', 'active')->paginate(10);
// Get pagination info
echo $users->currentPage();
echo $users->lastPage();
echo $users->total();
echo $users->count();
// Render pagination links (WordPress style)
echo $users->links();Collections 
Models return Collection instances that provide additional methods:
$users = User::where('status', 'active')->get();
// Collection methods
$names = $users->pluck('name');
$grouped = $users->groupBy('status');
$filtered = $users->filter(function ($user) {
    return $user->age > 18;
});
// Transform
$userEmails = $users->map(function ($user) {
    return strtolower($user->email);
});
// WordPress integration
$wpUserIds = $users->pluck('wp_user_id');
$capabilities = $users->map(function ($user) {
    return user_can($user->wp_user_id, 'edit_posts');
});Raw Queries 
When you need more complex queries:
use AvelPress\Facades\DB;
// Raw where clauses
$users = User::whereRaw('age > ? AND status = ?', [18, 'active'])->get();
// Raw selects
$stats = User::selectRaw('COUNT(*) as total, AVG(age) as avg_age')->first();
// Raw queries with DB facade
$results = DB::select('SELECT * FROM users WHERE custom_field = ?', ['value']);Complete Example 
Here's a complete User model showcasing various features:
<?php
namespace App\Models;
use AvelPress\Database\Eloquent\Model;
use AvelPress\Database\Eloquent\SoftDeletes;
use AvelPress\Database\Eloquent\Casts\Attribute;
class User extends Model
{
    use SoftDeletes;
    protected $table = 'users';
    protected $primaryKey = 'id';
    public $timestamps = true;
    protected $fillable = [
        'name',
        'email', 
        'status',
        'avatar',
        'wp_user_id',
        'bio',
    ];
    protected $hidden = [
        'password',
        'remember_token',
    ];
    protected $appends = [
        'first_name',
        'avatar_url',
        'wp_avatar',
    ];
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'settings' => 'array',
            'is_admin' => 'boolean',
        ];
    }
    // Accessors
    public function getFirstNameAttribute()
    {
        return explode(' ', $this->name)[0];
    }
    public function getAvatarUrlAttribute()
    {
        if ($this->avatar) {
            return wp_upload_dir()['baseurl'] . '/avatars/' . $this->avatar;
        }
        return null;
    }
    public function getWpAvatarAttribute()
    {
        return get_avatar_url($this->email, 80);
    }
    // Mutators
    public function setEmailAttribute($value)
    {
        $this->attributes['email'] = strtolower(trim($value));
    }
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = wp_hash_password($value);
    }
    protected function bio(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => wp_kses_post($value),
            set: fn ($value) => wp_kses_post($value),
        );
    }
    // Relationships
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
    // WordPress Integration
    public function wordpressUser()
    {
        return get_user_by('id', $this->wp_user_id);
    }
    public function can($capability)
    {
        $wp_user = $this->wordpressUser();
        return $wp_user ? user_can($wp_user, $capability) : false;
    }
    public function getMeta($key, $single = true)
    {
        return get_user_meta($this->wp_user_id, $key, $single);
    }
    public function updateMeta($key, $value)
    {
        return update_user_meta($this->wp_user_id, $key, $value);
    }
    // Model events
    protected static function boot()
    {
        parent::boot();
        static::creating(function ($user) {
            // Generate UUID if not provided
            if (!$user->uuid) {
                $user->uuid = wp_generate_uuid4();
            }
            // Generate slug from name
            if (!$user->slug) {
                $user->slug = sanitize_title($user->name);
            }
        });
        static::created(function ($user) {
            // Send welcome email
            wp_mail($user->email, 'Welcome!', 'Welcome to our application!');
            // Create default profile
            $user->profile()->create([
                'bio' => '',
                'location' => '',
                'website' => '',
            ]);
            // Set user meta if WordPress user exists
            if ($user->wp_user_id) {
                update_user_meta($user->wp_user_id, 'custom_user_id', $user->id);
            }
        });
        static::updated(function ($user) {
            // Clear caches
            wp_cache_delete("user_{$user->id}");
            if ($user->wp_user_id) {
                clean_user_cache($user->wp_user_id);
            }
        });
        static::deleting(function ($user) {
            // Delete related records
            $user->posts()->delete();
            // WordPress cleanup
            if ($user->wp_user_id) {
                delete_user_meta($user->wp_user_id, 'custom_user_id');
            }
        });
    }
}This model demonstrates:
- Table configuration and mass assignment
 - Timestamps and soft deletes
 - Accessors and mutators for data transformation
 - Attribute casting including custom casts
 - Relationships with other models
 - WordPress integration (users, meta, capabilities)
 - Model events for lifecycle hooks
 - Serialization control
 
Models in AvelPress provide a powerful and elegant way to work with your database while maintaining full WordPress compatibility and following modern PHP practices.
