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.