Model Relationships
AvelPress supports the following Eloquent-style relationships:
One to One
php
use AvelPress\Database\Eloquent\Relations\HasOne;
use AvelPress\Database\Eloquent\Relations\BelongsTo;
class User extends Model
{
public function profile(): HasOne
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}One to Many
php
use AvelPress\Database\Eloquent\Relations\HasMany;
use AvelPress\Database\Eloquent\Relations\BelongsTo;
class User extends Model
{
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
}
class Post extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}Many to Many
php
use AvelPress\Database\Eloquent\Relations\BelongsToMany;
class User extends Model
{
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
}
class Role extends Model
{
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}Note: Always specify the correct return type (
HasOne,HasMany,BelongsTo,BelongsToMany) in your relationship methods.
Usage Example
php
$user = User::find(1);
$profile = $user->profile; // One to one
$posts = $user->posts; // One to many
$roles = $user->roles; // Many to manyFor more details, see the Eloquent Models Guide.
Has Many Through
Access distant relationships through intermediate models.
php
class Country extends Model
{
public function users()
{
return $this->hasMany(User::class);
}
public function posts()
{
return $this->hasManyThrough(Post::class, User::class);
}
}
class User extends Model
{
public function country()
{
return $this->belongsTo(Country::class);
}
public function posts()
{
return $this->hasMany(Post::class);
}
}
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// Usage
$country = Country::find(1);
$posts = $country->posts; // All posts from users in this countryPolymorphic Relationships
Allow a model to belong to more than one other model on a single association.
One to Many Polymorphic
php
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
class Post extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Video extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}Using Polymorphic Relationships
php
// Create comments for different models
$post = Post::find(1);
$post->comments()->create(['content' => 'Great post!']);
$video = Video::find(1);
$video->comments()->create(['content' => 'Nice video!']);
// Access the parent model
$comment = Comment::find(1);
$commentable = $comment->commentable; // Could be Post or Video
// Check the type
if ($comment->commentable_type === 'App\\Models\\Post') {
// It's a post comment
}Eager Loading
Prevent N+1 query problems by loading relationships efficiently.
Basic Eager Loading
php
// N+1 problem (bad)
$users = User::all();
foreach ($users as $user) {
echo $user->profile->bio; // Executes a query for each user
}
// Eager loading (good)
$users = User::with('profile')->get();
foreach ($users as $user) {
echo $user->profile->bio; // Profile already loaded
}Multiple Relationships
php
// Load multiple relationships
$users = User::with(['posts', 'profile', 'roles'])->get();
// Nested relationships
$users = User::with('posts.comments')->get();
// Mixed relationships
$users = User::with(['posts.comments.author', 'profile'])->get();Conditional Eager Loading
php
// Load posts with conditions
$users = User::with(['posts' => function ($query) {
$query->where('status', 'published')
->orderBy('created_at', 'desc');
}])->get();
// Load multiple relationships with conditions
$users = User::with([
'posts' => function ($query) {
$query->where('status', 'published');
},
'comments' => function ($query) {
$query->where('approved', true);
}
])->get();Lazy Eager Loading
Load relationships after the model is retrieved:
php
$users = User::all();
// Later, load relationships
$users->load('posts');
$users->load(['posts.comments', 'profile']);
// With conditions
$users->load(['posts' => function ($query) {
$query->where('status', 'published');
}]);Counting Related Models
php
// Count related models
$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->posts_count;
}
// Multiple counts
$users = User::withCount(['posts', 'comments'])->get();
// Conditional counts
$users = User::withCount([
'posts',
'posts as published_posts_count' => function ($query) {
$query->where('status', 'published');
}
])->get();
// Average, sum, max, min
$users = User::withAvg('posts', 'views')->get();
$users = User::withSum('orders', 'amount')->get();Querying Relationships
Has Queries
php
// Users who have posts
$users = User::has('posts')->get();
// Users who have at least 3 posts
$users = User::has('posts', '>=', 3)->get();
// Users who have published posts
$users = User::whereHas('posts', function ($query) {
$query->where('status', 'published');
})->get();
// Users who don't have posts
$users = User::doesntHave('posts')->get();
// Users who don't have published posts
$users = User::whereDoesntHave('posts', function ($query) {
$query->where('status', 'published');
})->get();Relationship Queries
php
// Query through relationships
$posts = Post::whereRelation('user', 'status', 'active')->get();
// Or using joins
$posts = Post::join('users', 'posts.user_id', '=', 'users.id')
->where('users.status', 'active')
->select('posts.*')
->get();WordPress Integration
WordPress User Relationships
php
class User extends Model
{
public function wordpressUser()
{
return $this->hasOne(WP_User::class, 'ID', 'wp_user_id');
}
public function posts()
{
return $this->hasMany(Post::class);
}
// Get WordPress posts
public function wordpressPosts()
{
$wp_user = get_user_by('id', $this->wp_user_id);
if ($wp_user) {
return get_posts([
'author' => $this->wp_user_id,
'post_status' => 'any',
'numberposts' => -1
]);
}
return [];
}
}WordPress Post Relationships
php
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
// Get WordPress post
public function wordpressPost()
{
return get_post($this->wp_post_id);
}
// Get post meta
public function getMeta($key, $single = true)
{
return get_post_meta($this->wp_post_id, $key, $single);
}
// WordPress categories (taxonomy)
public function categories()
{
$wp_post = $this->wordpressPost();
if ($wp_post) {
return wp_get_post_categories($this->wp_post_id, ['fields' => 'all']);
}
return [];
}
}Custom WordPress Relationships
php
class Product extends Model
{
// Related WordPress posts
public function relatedPosts()
{
$related_ids = $this->getMeta('related_post_ids');
if ($related_ids) {
return get_posts([
'include' => $related_ids,
'post_type' => 'post',
'post_status' => 'publish'
]);
}
return [];
}
// WooCommerce integration
public function woocommerceProduct()
{
if ($this->wc_product_id) {
return wc_get_product($this->wc_product_id);
}
return null;
}
}Performance Tips
Optimize Eager Loading
php
// Load only needed columns
$users = User::with(['posts:id,user_id,title,status'])->get();
// Use select to limit columns on main model too
$users = User::select(['id', 'name', 'email'])
->with(['posts:id,user_id,title'])
->get();Use Constraints Wisely
php
// Instead of loading all posts and filtering in PHP
$users = User::with('posts')->get();
$publishedPosts = $users->flatMap(function ($user) {
return $user->posts->where('status', 'published');
});
// Load only published posts
$users = User::with(['posts' => function ($query) {
$query->where('status', 'published');
}])->get();Consider Chunking
php
// For large datasets, use chunking with relationships
User::with('posts')->chunk(100, function ($users) {
foreach ($users as $user) {
// Process user and their posts
}
});