Laravel devs love expressive syntax—and Laravel 12 just made it even cleaner. In this post, we’ll explore two powerful tools for writing elegant, maintainable Eloquent code:
HasManyThroughfor deep relationships- The new pipe operator (
->) for fluent chaining - Bonus: how to automate cleanups with Rector rules
Whether you’re building a multi-tenant SaaS or just tired of messy relationship logic, this combo will upgrade your Laravel game.
🔗 HasManyThrough: The Underrated Power Move
Let’s say you have:
Country→ has manyUsersUser→ has manyPosts- You want:
Country→ has manyPosts(throughUsers)
Instead of nested loops or joins, use:
class Country extends Model
{
public function posts(): HasManyThrough
{
return $this->hasManyThrough(Post::class, User::class);
}
}
Why it matters:
- ✅ One-liner relationship
- ✅ Optimized SQL joins
- ✅ Works with eager loading, constraints, and pagination
Pro tip: You can chain constraints like:
$country->posts()->where('published', true)->latest()->get();
🧪 Pipe Operator: Laravel 12’s Fluent Secret
Laravel 12 introduces the pipe operator (->) for chaining transformations:
$posts = $country->posts()
->get()
->pipe(fn($collection) => $collection->filter(fn($post) => $post->isFeatured()));
It’s like tap() but for transforming values—not just side effects.
Use cases:
- ✅ Clean up post-query logic
- ✅ Replace nested callbacks
- ✅ Make your code read top-down
🛠️ Automate Clean Code with Rector
Rector Laravel is a tool that refactors your codebase automatically. Want to convert old abort() logic to abort_if()? Done. Want to enforce default values or modern syntax? Easy.
Example rule:
// Before
if (!$user) {
abort(404);
}
// After
abort_unless($user, 404);
Recommended rules for Eloquent cleanup:
AbortIfRectorAddArgumentDefaultValueRectorSimplifyIfReturnBoolRector- Custom rules for relationship naming and query constraints
🧠 Real-World Use Case: Multi-Tenant Blog Platform
Imagine you’re building a blog platform where:
Organization→ has manyUsersUser→ has manyPosts- You want:
Organization→ has manyPosts
Use HasManyThrough:
class Organization extends Model
{
public function posts(): HasManyThrough
{
return $this->hasManyThrough(Post::class, User::class);
}
}
Then filter with pipes:
$org->posts()
->latest()
->get()
->pipe(fn($posts) => $posts->filter(fn($p) => $p->isPublic()));
Clean, readable, and scalable.
✅ Final Thoughts
Laravel 12’s pipe operator + Eloquent’s HasManyThrough = expressive, modern relationship logic. Add Rector to the mix, and you’ve got a clean code pipeline that scales.
If you’re building SaaS, dashboards, or multi-tenant apps—this trio is your secret weapon.
References (3)
1Illuminate\Database\Eloquent\Relations\HasManyThrough | Laravel API. https://api.laravel.com/docs/8.x/Illuminate/Database/Eloquent/Relations/HasManyThrough.html
2rector-laravel/docs/rector_rules_overview.md at main – GitHub. https://github.com/driftingly/rector-laravel/blob/main/docs/rector_rules_overview.md
3Laravel Has Many Through Eloquent Relationship Tutorial. https://www.itsolutionstuff.com/post/laravel-has-many-through-eloquent-relationship-tutorialexample.html
