wpdiggerstudio/wpzylos-model

Lightweight ORM/Model layer for WPZylos Framework

Maintainers

Package info

github.com/WPDiggerStudio/wpzylos-model

Documentation

pkg:composer/wpdiggerstudio/wpzylos-model

Fund package maintenance!

Paypal

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-06-14 08:19 UTC

This package is auto-updated.

Last update: 2026-06-14 11:48:27 UTC


README

PHP Version License GitHub

Lightweight ORM/Model layer for the WPZylos Framework — an elegant Active Record implementation built on top of WordPress's $wpdb, giving you the expressive model syntax you love without the overhead.

✨ Features

  • 🏗️ Active Record Pattern — Intuitive model classes that map to database tables
  • 🔄 Attribute Casting — Automatic type casting for int, float, bool, string, array, json, datetime
  • 🔧 Accessors & Mutators — Custom getXxxAttribute / setXxxAttribute methods
  • 🔗 RelationshipshasOne, hasMany, belongsTo with lazy loading and caching
  • 🗑️ Soft Deletes — Trash records instead of permanently deleting them
  • ⏱️ Timestamps — Automatic created_at / updated_at management
  • 📡 Model Events — Lifecycle hooks: creating, created, updating, updated, deleting, deleted
  • 🔍 Query Scopes — Reusable local scopes with scopeXxx() convention
  • 📦 ModelCollection — Rich collection wrapper with pluck, map, filter, each, contains
  • 🛡️ Mass Assignment Protection$fillable and $guarded for security
  • 📋 JSON SerializationtoArray(), toJson() with $hidden attribute support

📋 Requirements

Requirement Version
PHP >= 8.0
WordPress >= 6.0
wpzylos-core ^1.0
wpzylos-database ^1.0

📦 Installation

composer require wpdiggerstudio/wpzylos-model

🚀 Quick Start

Define a Model

<?php

declare(strict_types=1);

namespace App\Models;

use WPZylos\Framework\Model\Model;

class User extends Model
{
    protected static string $table = 'users';

    protected array $fillable = ['name', 'email', 'role'];

    protected array $casts = [
        'is_active' => 'bool',
        'metadata'  => 'json',
    ];

    protected array $hidden = ['password'];
}

Basic CRUD Operations

use App\Models\User;

// Create a new user
$user = User::create([
    'name'  => 'John Doe',
    'email' => 'john@example.com',
    'role'  => 'admin',
]);

// Find by primary key
$user = User::find(1);

// Find or throw an exception
$user = User::findOrFail(42);

// Update attributes
$user->update(['name' => 'Jane Doe']);

// Delete
$user->delete();

Query with Scopes

// Static where() proxies to QueryBuilder
$admins = User::where('role', 'admin')->get();

// Get all records
$users = User::all();

// Define and use local scopes
class User extends Model
{
    protected static string $table = 'users';
    protected array $fillable = ['name', 'email', 'role', 'is_active'];

    public function scopeActive($query)
    {
        return $query->where('is_active', 1);
    }

    public function scopeByRole($query, string $role)
    {
        return $query->where('role', $role);
    }
}

// Use scopes
$activeAdmins = User::active()->where('role', 'admin')->get();

🔧 Core Features

Attribute Casting

class Order extends Model
{
    protected static string $table = 'orders';

    protected array $fillable = ['customer_id', 'total', 'is_paid', 'items', 'shipped_at'];

    protected array $casts = [
        'customer_id' => 'integer',
        'total'       => 'float',
        'is_paid'     => 'bool',
        'items'       => 'json',
        'shipped_at'  => 'datetime',
    ];
}

$order = Order::find(1);
$order->total;      // float: 99.50
$order->is_paid;    // bool: true
$order->items;      // array: ['widget', 'gadget']
$order->shipped_at; // DateTimeImmutable

Accessors & Mutators

class User extends Model
{
    protected static string $table = 'users';
    protected array $fillable = ['first_name', 'last_name', 'email', 'password'];

    // Accessor: $user->full_name
    public function getFullNameAttribute($value): string
    {
        return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
    }

    // Mutator: $user->password = 'secret'
    public function setPasswordAttribute($value): void
    {
        $this->attributes['password'] = wp_hash_password($value);
    }
}

Relationships

class Post extends Model
{
    protected static string $table = 'posts';
    protected array $fillable = ['title', 'body', 'user_id'];

    // Post belongs to a User
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    // Post has many Comments
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

class User extends Model
{
    protected static string $table = 'users';
    protected array $fillable = ['name', 'email'];

    // User has one Profile
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }

    // User has many Posts
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

// Lazy-loaded, cached on first access
$post = Post::find(1);
$author = $post->user;         // belongsTo
$comments = $post->comments;   // hasMany (ModelCollection)

Soft Deletes

use WPZylos\Framework\Model\Model;
use WPZylos\Framework\Model\Concerns\SoftDeletes;

class Article extends Model
{
    use SoftDeletes;

    protected static string $table = 'articles';
    protected array $fillable = ['title', 'body'];
}

$article = Article::find(1);

// Soft delete (sets deleted_at timestamp)
$article->delete();

// Check if trashed
$article->trashed(); // true

// Restore
$article->restore();

// Permanently delete
$article->forceDelete();

// Query trashed records
$trashed = Article::onlyTrashed()->get();
$all     = Article::withTrashed()->get();

Model Events

class User extends Model
{
    protected static string $table = 'users';
    protected array $fillable = ['name', 'email', 'slug'];

    protected static function boot(): void
    {
        // Generate slug before creating
        static::creating(function (User $user) {
            $user->slug = sanitize_title($user->name);
        });

        // Log after creation
        static::created(function (User $user) {
            error_log("User created: {$user->name}");
        });

        // Prevent deletion of admins
        static::deleting(function (User $user) {
            if ($user->role === 'admin') {
                return false; // Cancel deletion
            }
        });
    }
}

ModelCollection

$users = User::all();

// Pluck a single attribute
$names = $users->pluck('name'); // ['John', 'Jane', 'Bob']

// Map to transform
$emails = $users->map(fn($u) => strtolower($u->email));

// Filter
$admins = $users->filter(fn($u) => $u->role === 'admin');

// Iteration
foreach ($users as $user) {
    echo $user->name;
}

// Count and check
$users->count();      // 3
$users->isEmpty();    // false
$users->isNotEmpty(); // true
$users->first();      // First user
$users->last();       // Last user

// Serialize
$array = $users->toArray();
$json  = $users->toJson();

Service Provider Registration

// In your plugin bootstrap
use WPZylos\Framework\Model\ModelServiceProvider;

$app->register(new ModelServiceProvider());

📦 Related Packages

Package Description
wpzylos-core Application foundation and container
wpzylos-database Query builder and database connection
wpzylos-migrations Database schema management

📖 Documentation

Full documentation is available at: https://wpzylos.com/docs/latest/packages/wpzylos-model

💬 Support

Support the Project

📄 License

This package is open-sourced software licensed under the MIT License.

🤝 Contributing

Please see CONTRIBUTING.md for details.

Made with ❤️ by WPDiggerStudio