Skip to main content
Working With Database

Working With Database

Laravel offers two database layers. The query builder creates SQL through method calls, and Eloquent maps rows to model objects with relationships and domain behavior. Both share the same connection configuration.

Migrations

Migrations are version-controlled schema changes. Create one with Artisan, define the table, and apply it.

php artisan make:migration create_posts_table
php artisan migrate
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('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('title');
            $table->string('slug')->unique();
            $table->text('body');
            $table->timestamp('published_at')->nullable();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

The query builder

use Illuminate\Support\Facades\DB;

$posts = DB::table('posts')
    ->where('published_at', '<=', now())
    ->orderByDesc('published_at')
    ->limit(10)
    ->get();

DB::table('posts')->insert([
    'title' => 'Hello',
    'slug'  => 'hello',
    'body'  => 'First post',
    'created_at' => now(),
]);

Use the query builder for reports and bulk operations where a model adds no value. Always bind values through the builder instead of concatenating user input into SQL.

Eloquent models

php artisan make:model Post -mf
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    protected $fillable = ['title', 'slug', 'body', 'published_at'];

    protected $casts = [
        'published_at' => 'datetime',
    ];

    public function author(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }

    public function scopePublished($query)
    {
        return $query->whereNotNull('published_at')
            ->where('published_at', '<=', now());
    }
}
$post = Post::published()->latest()->first();

$post->update(['title' => 'Updated']);

$post->comments()->create(['body' => 'Nice article']);

echo $post->author->name;

Eager loading

Accessing relationships inside a loop causes one query per row. Eager load them up front.

$posts = Post::with(['author', 'comments'])
    ->published()
    ->paginate(20);

Transactions

DB::transaction(function () use ($data) {
    $order = Order::create($data);

    foreach ($data['items'] as $item) {
        $order->items()->create($item);
    }
});

Seeding and factories

Post::factory()->count(30)->create([
    'user_id' => $user->id,
]);

Factories make tests and local demos reproducible. Seeders run them through php artisan db:seed. Never run demo seeders against production data.