reedware/laravel-relation-joins
Installation:
composer require reedware/laravel-relation-joins
Publish the config (if needed) with:
php artisan vendor:publish --provider="Reedware\RelationJoins\RelationJoinsServiceProvider"
First Use Case:
Join a Post model with its author (a User relationship) directly in a query:
$posts = Post::joinRelation('author')
->select('posts.*', 'users.name as author_name')
->get();
Reedware\RelationJoins\QueryBuilder.php for core logic and app/Providers/RelationJoinsServiceProvider.php for bootstrapping.Basic Joins:
Replace eager loading (with()) with direct joins for performance-critical queries:
// Before (N+1 queries)
$posts = Post::with('author')->get();
// After (Single query)
$posts = Post::joinRelation('author')
->select('posts.*', 'users.name as author_name')
->get();
Nested Relationships:
Join through multiple levels (e.g., Post → author → department):
$posts = Post::joinRelation('author.department')
->select('posts.*', 'departments.name as department_name')
->get();
Conditional Joins:
Use when() to conditionally join relationships:
$query = Post::query();
if ($withAuthors) {
$query->joinRelation('author');
}
Integration with Existing Queries:
Chain with other query builders (e.g., where, orderBy):
$posts = Post::joinRelation('author')
->where('users.active', true)
->orderBy('posts.created_at', 'desc')
->get();
Custom Selects:
Explicitly define columns to avoid SELECT *:
$posts = Post::joinRelation('author')
->select([
'posts.id',
'posts.title',
'users.name as author_name',
'users.email as author_email'
])
->get();
Dynamic Joins: Use closures for dynamic relationship names:
$relation = $user->isAdmin() ? 'admin' : 'user';
$posts = Post::joinRelation($relation)->get();
Left Joins:
Force left joins with leftJoinRelation():
$posts = Post::leftJoinRelation('author')->get();
Custom Join Constraints: Override default join conditions:
$posts = Post::joinRelation('author', fn($query) => $query->where('users.verified', true))
->get();
Column Conflicts:
id in both tables) cause SQL errors.select() to alias columns explicitly:
->select('posts.id as post_id', 'users.id as user_id')
Missing Foreign Keys:
foreignKey and ownerKey in your relationship definitions or use joinRelation() with a custom closure to specify the join condition manually.Performance Overhead:
DB::enableQueryLog() and use joinRelation() only for frequently accessed relationships.Nested Relationship Ambiguity:
author.id vs. department.id).select():
->select('posts.*', 'users.id as author_id', 'departments.id as department_id')
Polymorphic Relationships:
morphTo) may not work out-of-the-box.->joinRelation('author', fn($query) => $query->where('users.id', '=', 'posts.author_id'))
Query Logs: Enable query logging to inspect generated SQL:
DB::enableQueryLog();
$posts = Post::joinRelation('author')->get();
dd(DB::getQueryLog());
Relationship Introspection:
Use getRelation() to verify relationship definitions:
$post = Post::first();
dd($post->getRelation('author')); // Check if the relationship exists
Common Errors:
public function author() { return $this->belongsTo(User::class); }).joinRelation('author', fn($q) => $q->orWhere(...)) may fail; use where() separately).Custom Join Logic:
Extend the package by publishing and overriding the RelationJoinsServiceProvider:
// app/Providers/RelationJoinsServiceProvider.php
public function boot()
{
RelationJoins::extend('custom', function ($query, $relation) {
// Custom join logic here
});
}
Macros: Add global query builder macros for reusable join patterns:
use Illuminate\Database\Query\Builder;
Builder::macro('joinAuthorWithDepartment', function () {
return $this->joinRelation('author.department');
});
Testing:
Mock joins in tests using RelationJoins::shouldReceive('joinRelation'):
RelationJoins::shouldReceive('joinRelation')
->once()
->with('author')
->andReturnSelf();
Configuration:
Override default behavior in config/relation-joins.php:
'default_join_type' => 'left', // Default to left joins
'strict_relationship_check' => false, // Skip validation in tests
How can I help you explore Laravel packages today?