Eloquent ORM Setup for Laravel Web Application
Eloquent — Active Record implementation over PDO supporting PostgreSQL, MySQL, SQLite, and SQL Server. In Laravel 10/11 it's already included, but correct configuration is more than just .env. We cover the full path: from connection config to advanced techniques.
Connection Configuration
config/database.php — only change critical parameters, rest from .env:
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'search_path' => 'public',
'sslmode' => env('DB_SSLMODE', 'prefer'),
]
For production behind reverse proxy, set persistent connections to false — they conflict with pgBouncer.
Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;
class Product extends Model
{
use SoftDeletes;
protected $table = 'products';
protected $fillable = ['title', 'slug', 'price', 'status', 'category_id'];
protected $casts = [
'price' => 'decimal:2',
'meta' => 'array',
'published_at' => 'datetime',
];
public function scopePublished(Builder $query): Builder
{
return $query->where('status', 'published');
}
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class, 'product_tags')
->withTimestamps();
}
}
Migrations
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('title', 500);
$table->string('slug', 520)->unique();
$table->foreignId('category_id')->constrained()->restrictOnDelete();
$table->decimal('price', 12, 2);
$table->string('status', 20)->default('draft');
$table->timestamps();
$table->index(['status', 'created_at']);
});
}
};
Eager Loading
$products = Product::query()
->published()
->with(['category', 'tags'])
->orderBy('created_at', 'desc')
->paginate(24);
Use preventLazyLoading() in local development to catch N+1 issues:
if (app()->isLocal()) {
Model::preventLazyLoading();
}
Transactions
DB::transaction(function () use ($orderId, $items) {
$order = Order::findOrFail($orderId);
$order->update(['status' => 'processing']);
foreach ($items as $item) {
Product::find($item['product_id'])
->decrement('stock', $item['quantity']);
}
});
Timeline
Setup Eloquent for new Laravel project: 1 day. Audit and refactor existing project with N+1 problems: 1–2 days.







