Installation:
composer require jedrzej/withable:0.0.6
Add the trait to your Eloquent model:
use Jedrzej\Withable\WithableTrait;
class Post extends Model
{
use WithableTrait;
protected $withable = ['author', 'comments'];
}
First Use Case:
/posts?with=author,comments
$posts = Post::all(); // Automatically eager loads 'author' and 'comments'
Where to Look First:
WithableTrait source in vendor/jedrzej/withable/src/WithableTrait.php for core logic.getWithableRelations() method if dynamic relation definitions are needed.Basic Eager Loading:
// URL: /posts?with=author
$posts = Post::all(); // Loads 'author' relation for all posts
Multiple Relations:
// URL: /posts?with=author,comments
$posts = Post::all(); // Loads both relations
Dynamic Relations:
class Post extends Model
{
use WithableTrait;
protected function getWithableRelations()
{
return ['author', 'comments', 'tags']; // Conditionally add relations
}
}
Integration with API Resources:
// In a resource class:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'author' => $this->whenLoaded('author', function () {
return $this->author->name;
}),
];
}
Combining with Other Traits:
use Jedrzej\Sortable\SortableTrait;
use Jedrzej\Searchable\SearchableTrait;
class Post extends Model
{
use WithableTrait, SortableTrait, SearchableTrait;
protected $withable = ['author'];
protected $searchable = ['title', 'body'];
}
API Endpoints:
Use with parameter for client-driven eager loading:
Route::get('/posts', function () {
return Post::all(); // Respects ?with=author,comments
});
Admin Panels: Dynamically load nested relations for detailed views:
// URL: /admin/posts?with=author,comments.user
$posts = Post::all(); // Loads nested relations
Testing:
Mock the withable property or getWithableRelations() in tests:
$post = new Post();
$post->setWithable(['author']); // Force specific relations
Case Sensitivity:
The with parameter is case-sensitive. Ensure relation names match exactly (e.g., author vs Author).
Circular References:
Nested relations (e.g., ?with=author.posts) may cause SQLSTATE[23000] errors. Use withCount or limit depth:
protected $withable = ['author']; // Avoid nested relations
Performance:
with parameters can lead to N+1 queries. Validate input:
public function getWithableRelations()
{
return array_intersect(['author', 'comments'], explode(',', request('with')));
}
with() manually for complex cases:
$posts = Post::with(request('with'))->get();
Laravel Version Quirks:
Illuminate\Database\Eloquent\Model (not Eloquent).Overriding Default Behavior:
The trait hooks into getQuery() and toArray(). Override these methods cautiously to avoid conflicts.
$post->relationLoaded('author'); // Returns bool
DB::enableQueryLog();
$posts = Post::all();
dd(DB::getQueryLog());
with parameter to prevent SQL injection:
$allowed = ['author', 'comments'];
$with = array_intersect(explode(',', request('with')), $allowed);
Default Relations:
Set defaults in getWithableRelations():
protected function getWithableRelations()
{
return ['author']; // Default relation
}
Conditional Loading: Dynamically adjust relations based on user roles:
protected function getWithableRelations()
{
$relations = ['author'];
if (auth()->user()->isAdmin()) {
$relations[] = 'comments';
}
return $relations;
}
Combine with withCount:
// URL: /posts?with=author,comments:count
$posts = Post::withCount('comments')->get();
Extend the Trait:
Add custom logic to the trait’s bootWithable() method:
namespace Jedrzej\Withable;
trait WithableTrait {
public static function bootWithable()
{
static::addGlobalScope('withable', function (Builder $builder) {
// Custom scope logic
});
}
}
Documentation:
Clearly document the with parameter in your API specs (e.g., OpenAPI/Swagger):
parameters:
- name: with
in: query
description: Comma-separated list of relations to eager load (e.g., "author,comments")
schema:
type: string
How can I help you explore Laravel packages today?