Installation:
composer require reinink/advanced-eloquent
Add to config/app.php under providers:
Reinink\AdvancedEloquent\AdvancedEloquentServiceProvider::class,
First Use Case: Enable the package by publishing the config (optional but recommended):
php artisan vendor:publish --provider="Reinink\AdvancedEloquent\AdvancedEloquentServiceProvider"
Test a basic macro in a model:
use Reinink\AdvancedEloquent\Macros\HasManyThrough;
class Post extends Model
{
public static function boot()
{
parent::boot();
static::addMacros(HasManyThrough::class);
}
}
Key Entry Points:
config/advanced-eloquent.php for enabled macros.src/Macros/ for available macros (e.g., HasManyThrough, Orderable, Filterable).HasManyThrough for nested relationships or Orderable for drag-and-drop sorting.User → Posts → Comments).class User extends Model
{
public static function boot()
{
static::addMacros(HasManyThrough::class);
}
public function posts()
{
return $this->hasMany(Post::class);
}
}
// Usage:
$user->postsThrough('comments', Comment::class, 'post_id', 'user_id');
$user->postsThrough('comments', Comment::class, 'post_id', 'user_id', [], true);
class MenuItem extends Model
{
use \Reinink\AdvancedEloquent\Traits\Orderable;
protected $orderable = [
'order_column' => 'position',
'scope' => 'menu_id',
];
}
$item->move(5); // Move to position 5
$item->moveUp();
$item->moveDown();
MenuItem::orderBy('position')->get();
class Product extends Model
{
public static function boot()
{
static::addMacros(\Reinink\AdvancedEloquent\Macros\Filterable::class);
}
}
// Usage:
$products = Product::filter(['price_min' => 10, 'price_max' => 100])->get();
app/Providers/AppServiceProvider:
\Reinink\AdvancedEloquent\Macros\Filterable::extend('price_range', function ($query, $value) {
return $query->whereBetween('price', [$value['min'], $value['max']]);
});
$trashed = Model::trashed()->get();
$active = Model::active()->get(); // Non-trashed
AppServiceProvider@boot() for global availability:
Model::addMacros([
\Reinink\AdvancedEloquent\Macros\HasManyThrough::class,
\Reinink\AdvancedEloquent\Macros\Orderable::class,
]);
boot() in individual models to avoid global bloat.Filterable with Laravel API Resources for dynamic responses:
return new ProductResource(Product::filter($request->all())->paginate());
Macro Conflicts:
orderBy).config/advanced-eloquent.php:
'conflicts' => [
'orderBy' => false, // Disable if using Orderable macro
],
Performance with HasManyThrough:
with() or load() explicitly:
$user->load('postsThrough.comments');
Orderable Scope Misconfiguration:
scope in $orderable causes duplicate positions.scope matches a unique column (e.g., menu_id):
protected $orderable = [
'order_column' => 'position',
'scope' => 'menu_id', // Must be unique per group
];
Filterable Security:
AppServiceProvider:
\Reinink\AdvancedEloquent\Macros\Filterable::allowedFilters(['price_min', 'price_max']);
Macro Not Loading:
config/advanced-eloquent.php under macros.HasManyThrough vs. HasManyThroughMacro).Orderable Not Working:
position column exists and is numeric.Filterable Errors:
config/logging.php to inspect generated SQL:
'default' => env('LOG_CHANNEL', 'single'),
'channels' => [
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
Custom Macros:
HasManyThrough):
class CustomHasManyThrough extends \Reinink\AdvancedEloquent\Macros\HasManyThrough
{
public static function addToBuilder($builder)
{
parent::addToBuilder($builder);
// Add custom logic
}
}
Filterable Extensions:
\Reinink\AdvancedEloquent\Macros\Filterable::extend('custom', function ($query, $value) {
return $query->whereRaw("custom_column LIKE ?", ["%{$value}%"]);
});
Orderable Events:
class MenuItem extends Model
{
protected static function booted()
{
static::updated(function ($item) {
if ($item->isDirty('position')) {
event(new PositionUpdated($item));
}
});
}
}
Disabled Macros:
enabled => false in config/advanced-eloquent.php to disable all macros globally.'macros' => [
'orderable' => false,
'filterable' => true,
],
Database Schema:
position column for Orderable is integer (not string).HasManyThrough, intermediate tables must exist before querying.How can I help you explore Laravel packages today?