Learn how to implement Create, Read, Update, and Delete operations in Laravel with best practices and modern patterns.
Database structure and model setup
// database/migrations/2024_03_20_create_posts_table.php
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('title');
$table->string('slug')->unique();
$table->text('content');
$table->string('image')->nullable();
$table->enum('status', ['draft', 'published'])->default('draft');
$table->timestamp('published_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class Post extends Model
{
use SoftDeletes;
protected $fillable = [
'title',
'content',
'image',
'status',
'published_at'
];
protected $casts = [
'published_at' => 'datetime'
];
// Generate slug before saving
protected static function boot()
{
parent::boot();
static::creating(function ($post) {
$post->slug = Str::slug($post->title);
});
}
// Relationships
public function user()
{
return $this->belongsTo(User::class);
}
// Scopes
public function scopePublished($query)
{
return $query->where('status', 'published')
->whereNotNull('published_at')
->where('published_at', '<=', now());
}
// Accessors & Mutators
public function getExcerptAttribute()
{
return Str::limit(strip_tags($this->content), 150);
}
}
URL and endpoint configuration
// routes/web.php
Route::resource('posts', PostController::class);
Method | URI | Name | Action |
---|---|---|---|
GET | posts | posts.index | index |
GET | posts/create | posts.create | create |
POST | posts | posts.store | store |
GET | posts/{post} | posts.show | show |
GET | posts/{post}/edit | posts.edit | edit |
PUT/PATCH | posts/{post} | posts.update | update |
DELETE | posts/{post} | posts.destroy | destroy |