mohammad-fouladgar/eloquent-builder
Installation:
composer require mohammad-fouladgar/eloquent-builder
Add the service provider to config/app.php under providers:
MohammadFouladgar\EloquentBuilder\EloquentBuilderServiceProvider::class,
First Use Case:
Define a query builder class for your model (e.g., User). Create a file at app/Builders/UserBuilder.php:
namespace App\Builders;
use MohammadFouladgar\EloquentBuilder\EloquentBuilder;
class UserBuilder extends EloquentBuilder
{
protected $model = \App\Models\User::class;
public function defaultScope()
{
return $this->model::where('is_active', true);
}
public function applyFilters($filters)
{
if (isset($filters['age_more_than'])) {
return $this->where('age', '>', $filters['age_more_than']);
}
if (isset($filters['gender'])) {
return $this->where('gender', $filters['gender']);
}
if (isset($filters['has_published_post'])) {
return $this->whereHas('posts', function ($query) {
$query->where('published', true);
});
}
}
}
Usage in Controller:
use App\Builders\UserBuilder;
public function index(Request $request)
{
$users = (new UserBuilder)->get($request->all());
return response()->json($users);
}
Filtering:
applyFilters() to handle request parameters dynamically.public function applyFilters($filters)
{
if (isset($filters['search'])) {
return $this->where('name', 'like', "%{$filters['search']}%")
->orWhere('email', 'like', "%{$filters['search']}%");
}
}
Sorting:
applySort() to handle sort_by and sort_direction:
public function applySort($sortBy, $sortDirection = 'asc')
{
if (!empty($sortBy)) {
return $this->orderBy($sortBy, $sortDirection);
}
}
Pagination:
$users = (new UserBuilder)->paginate($request->all());
Relationships:
with() or whereHas() in defaultScope() or applyFilters():
public function defaultScope()
{
return $this->model::with(['posts', 'profile']);
}
Custom Query Logic:
applyFilters() for complex logic (e.g., date ranges):
public function applyFilters($filters)
{
if (isset($filters['created_after'])) {
return $this->where('created_at', '>', $filters['created_after']);
}
}
Request Validation:
$validated = $request->validate([
'age_more_than' => 'sometimes|integer',
'gender' => 'sometimes|in:male,female',
]);
$users = (new UserBuilder)->get($validated);
API Resources:
return UserResource::collection($users);
Testing:
$builder = $this->app->make(UserBuilder::class);
$result = $builder->get(['age_more_than' => 25]);
$this->assertCount(1, $result);
Caching:
Cache::remember):
return Cache::remember("users_{$request->queryString}", now()->addHours(1), function () use ($request) {
return (new UserBuilder)->get($request->all());
});
N+1 Queries:
applyFilters() without with(). Use with() in defaultScope() or manually in the builder:
// Bad: Causes N+1 queries
public function applyFilters($filters) {
if (isset($filters['post_id'])) {
return $this->whereHas('posts', fn($q) => $q->where('id', $filters['post_id']));
}
}
// Good: Use with() in defaultScope()
SQL Injection:
applyFilters(). The package handles basic cases, but custom logic may expose risks:
// Risky: Directly interpolating user input
public function applyFilters($filters) {
return $this->where('name', 'like', "%{$filters['name']}%"); // Safe if $filters['name'] is validated
}
Overriding Methods:
parent::applyFilters() if extending existing logic:
public function applyFilters($filters)
{
parent::applyFilters($filters); // Preserve parent behavior
// Add custom logic
}
Deprecated Methods:
whereHas syntax changes in Laravel 9+).Log Queries:
\DB::enableQueryLog();
$users = (new UserBuilder)->get($request->all());
dd(\DB::getQueryLog());
Validate Filters:
$filters in applyFilters() to verify input:
public function applyFilters($filters) {
\Log::debug('Filters received:', $filters);
// ...
}
Test Edge Cases:
null values, and invalid inputs:
$this->get('/users?invalid_param=123')->assertOk(); // Ensure graceful handling
Custom Directives:
range, in) by overriding applyFilters():
public function applyFilters($filters) {
if (isset($filters['age_range'])) {
[$min, $max] = explode(',', $filters['age_range']);
return $this->whereBetween('age', [$min, $max]);
}
}
Conditional Scopes:
when() for dynamic scopes:
public function defaultScope()
{
return $this->model::when($this->request->has('active_only'), fn($q) => $q->where('is_active', true));
}
Reusable Builders:
BaseBuilder with common methods):
abstract class BaseBuilder extends EloquentBuilder {
protected function applySearch($searchTerm) {
return $this->where('name', 'like', "%{$searchTerm}%");
}
}
Event Hooks:
public function get($filters = [])
{
event(new QueryBuilding($this->model, $filters));
return parent::get($filters);
}
How can I help you explore Laravel packages today?