Examples
E-Commerce Order System
A complete example modeling an e-commerce system with orders, customers, and line items.
Models
<?php
declare(strict_types=1);
namespace App\Models;
use WPZylos\Framework\Model\Model;
use WPZylos\Framework\Model\Concerns\SoftDeletes;
class Customer extends Model
{
protected static string $table = 'customers';
protected array $fillable = ['name', 'email', 'phone', 'is_vip'];
protected array $casts = [
'is_vip' => 'bool',
];
protected array $hidden = ['password_hash'];
public function orders()
{
return $this->hasMany(Order::class);
}
// Accessor: $customer->display_name
public function getDisplayNameAttribute($value): string
{
$name = $this->attributes['name'];
return $this->attributes['is_vip'] ? "⭐ {$name}" : $name;
}
// Scope: Customer::vip()->get()
public function scopeVip($query)
{
return $query->where('is_vip', 1);
}
}
class Order extends Model
{
use SoftDeletes;
protected static string $table = 'orders';
protected array $fillable = [
'customer_id', 'total', 'tax', 'status', 'notes', 'shipped_at',
];
protected array $casts = [
'customer_id' => 'integer',
'total' => 'float',
'tax' => 'float',
'shipped_at' => 'datetime',
];
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function lineItems()
{
return $this->hasMany(LineItem::class);
}
// Accessor: $order->grand_total
public function getGrandTotalAttribute($value): float
{
return $this->attributes['total'] + $this->attributes['tax'];
}
// Scopes
public function scopePending($query)
{
return $query->where('status', 'pending');
}
public function scopeShipped($query)
{
return $query->where('status', 'shipped');
}
public function scopeHighValue($query, float $min = 100.0)
{
return $query->where('total', '>=', $min);
}
// Events
protected static function boot(): void
{
static::creating(function (Order $order) {
if ($order->total <= 0) {
return false;
}
});
static::updating(function (Order $order) {
if ($order->isDirty('status') && $order->status === 'shipped') {
$order->shipped_at = gmdate('Y-m-d H:i:s');
}
});
}
}
class LineItem extends Model
{
protected static string $table = 'line_items';
protected array $fillable = ['order_id', 'product_name', 'quantity', 'unit_price'];
protected array $casts = [
'order_id' => 'integer',
'quantity' => 'integer',
'unit_price' => 'float',
];
public function order()
{
return $this->belongsTo(Order::class);
}
// Accessor: $item->subtotal
public function getSubtotalAttribute($value): float
{
return $this->attributes['quantity'] * $this->attributes['unit_price'];
}
}
Usage
// Create a customer
$customer = Customer::create([
'name' => 'Jane Smith',
'email' => '[email protected]',
'is_vip' => true,
]);
echo $customer->display_name; // "⭐ Jane Smith"
// Create an order
$order = Order::create([
'customer_id' => $customer->getKey(),
'total' => 249.99,
'tax' => 20.00,
'status' => 'pending',
]);
echo $order->grand_total; // 269.99
// Query with scopes
$pendingOrders = Order::pending()->get();
$bigOrders = Order::highValue(500)->get();
$vipCustomers = Customer::vip()->get();
// Relationships
$customer = $order->customer;
$orders = $customer->orders;
$items = $order->lineItems;
// Soft delete and restore
$order->delete(); // Soft delete
$order->trashed(); // true
$order->restore(); // Restore
$order->forceDelete(); // Permanently delete
// Collections
$orderTotals = $customer->orders->pluck('total');
$highOrders = $customer->orders->filter(fn($o) => $o->total > 100);
$customer->orders->each(function ($order) {
echo "Order #{$order->getKey()}: \${$order->total}\n";
});
Blog System
<?php
declare(strict_types=1);
namespace App\Models;
use WPZylos\Framework\Model\Model;
use WPZylos\Framework\Model\Concerns\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected static string $table = 'blog_posts';
protected array $fillable = ['title', 'slug', 'body', 'author_id', 'status', 'published_at'];
protected array $casts = [
'author_id' => 'integer',
'published_at' => 'datetime',
'view_count' => 'integer',
];
public function author()
{
return $this->belongsTo(Author::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
// Mutator: auto-generate slug
public function setTitleAttribute($value): void
{
$this->attributes['title'] = $value;
$this->attributes['slug'] = sanitize_title($value);
}
public function scopePublished($query)
{
return $query->where('status', 'published');
}
public function scopeDraft($query)
{
return $query->where('status', 'draft');
}
protected static function boot(): void
{
static::creating(function (Post $post) {
if (empty($post->slug)) {
$post->slug = sanitize_title($post->title);
}
});
}
}
class Comment extends Model
{
protected static string $table = 'blog_comments';
protected array $fillable = ['post_id', 'author_name', 'email', 'body', 'is_approved'];
protected array $casts = [
'post_id' => 'integer',
'is_approved' => 'bool',
];
public function post()
{
return $this->belongsTo(Post::class);
}
public function scopeApproved($query)
{
return $query->where('is_approved', 1);
}
}
Blog Usage
// Create a post
$post = Post::create([
'title' => 'Getting Started with WPZylos Model',
'body' => 'The Model package provides an elegant...',
'author_id' => 1,
'status' => 'draft',
]);
// slug is auto-generated: "getting-started-with-wpzylos-model"
echo $post->slug;
// Publish
$post->update(['status' => 'published', 'published_at' => gmdate('Y-m-d H:i:s')]);
// Get published posts
$published = Post::published()->get();
// Get post with comments
$post = Post::find(1);
$comments = $post->comments;
$approvedComments = $comments->filter(fn($c) => $c->is_approved);
// Serialize for API response
echo json_encode([
'post' => $post->toArray(),
'comments' => $approvedComments->toArray(),
]);
Settings / Key-Value Store
class Setting extends Model
{
protected static string $table = 'settings';
protected static string $primaryKey = 'key';
protected array $fillable = ['key', 'value', 'group'];
protected array $casts = [
'value' => 'json',
];
protected bool $timestamps = false;
public function scopeGroup($query, string $group)
{
return $query->where('group', $group);
}
public static function get(string $key, mixed $default = null): mixed
{
$setting = static::find($key);
return $setting?->value ?? $default;
}
public static function set(string $key, mixed $value, string $group = 'general'): static
{
$setting = static::find($key);
if ($setting) {
$setting->update(['value' => $value]);
return $setting;
}
return static::create([
'key' => $key,
'value' => $value,
'group' => $group,
]);
}
}
// Usage
Setting::set('site_name', 'My WP Site');
Setting::set('smtp_config', ['host' => 'smtp.example.com', 'port' => 587], 'email');
$name = Setting::get('site_name'); // "My WP Site"
$emailSettings = Setting::group('email')->get();