// app/Http/Controllers/PostController.phpnamespace App\Http\Controllers;use App\Models\Post;use App\Http\Requests\StorePostRequest;use Illuminate\Http\Request;class PostController extends Controller{ public function index() { $posts = Post::latest()->paginate(10); return view('posts.index', compact('posts')); } public function show(Post $post) // route model binding { return view('posts.show', compact('post')); } public function store(StorePostRequest $request) { $post = Post::create($request->validated()); return redirect()->route('posts.show', $post)->with('success', 'Created!'); } public function update(StorePostRequest $request, Post $post) { $post->update($request->validated()); return redirect()->route('posts.show', $post); } public function destroy(Post $post) { $post->delete(); return redirect()->route('posts.index'); }}
JSON / API Response
// API controllerpublic function index(){ return Post::paginate(15); // auto JSON}public function show(Post $post){ return response()->json($post, 200);}public function store(Request $request){ $post = Post::create($request->validated()); return response()->json($post, 201);}
4. Request & Validation
Form Request
// app/Http/Requests/StorePostRequest.phpnamespace App\Http\Requests;use Illuminate\Foundation\Http\FormRequest;class StorePostRequest extends FormRequest{ public function authorize(): bool { return true; // or: $this->user()->can('create', Post::class) } public function rules(): array { return [ 'title' => ['required', 'string', 'max:255'], 'body' => ['required', 'string'], 'status' => ['required', 'in:draft,published'], 'tags' => ['nullable', 'array'], 'tags.*' => ['string', 'max:50'], 'thumbnail' => ['nullable', 'image', 'max:2048'], // max KB ]; } public function messages(): array { return [ 'title.required' => 'A title is required.', ]; }}
// app/Models/Post.phpnamespace App\Models;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\SoftDeletes;class Post extends Model{ use SoftDeletes; protected $fillable = ['title', 'body', 'status', 'user_id']; // or block mass assignment with $guarded: // protected $guarded = []; protected $casts = [ 'published_at' => 'datetime', 'is_featured' => 'boolean', 'metadata' => 'array', ];}
Query Builder
// RetrievePost::all();Post::find(1); // null if not foundPost::findOrFail(1); // 404 if not foundPost::first();Post::where('status', 'published')->get();Post::where('views', '>=', 100)->orderBy('created_at', 'desc')->get();Post::whereIn('status', ['draft', 'published'])->get();Post::whereBetween('created_at', [now()->subMonth(), now()])->get();Post::latest()->take(5)->get();Post::count();// CreatePost::create(['title' => 'Hello', 'body' => '...']);// UpdatePost::where('status', 'draft')->update(['status' => 'published']);$post->update(['title' => 'Updated']);// Delete$post->delete();Post::where('status', 'archived')->delete();// UpsertPost::updateOrCreate( ['slug' => 'hello-world'], // match criteria ['title' => 'Hello World', 'body' => '...'], // values to set);// Chunk for large datasetsPost::chunk(100, function ($posts) { foreach ($posts as $post) { // process }});
Scopes
// In the modelpublic function scopePublished(Builder $query): Builder{ return $query->where('status', 'published');}// UsagePost::published()->latest()->get();
6. Migrations
php artisan migratephp artisan migrate:rollbackphp artisan migrate:fresh # drop all + re-migrate (dev only)php artisan migrate:status
// database/migrations/xxxx_create_posts_table.phpuse 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->string('title'); $table->text('body'); $table->string('status')->default('draft'); $table->string('slug')->unique(); $table->boolean('is_featured')->default(false); $table->json('metadata')->nullable(); $table->timestamp('published_at')->nullable(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->softDeletes(); $table->timestamps(); // created_at, updated_at }); } public function down(): void { Schema::dropIfExists('posts'); }};
Common Column Types
Method
SQL type
$table->id()
BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT
$table->string('col')
VARCHAR(255)
$table->text('col')
TEXT
$table->integer('col')
INT
$table->unsignedBigInteger('col')
BIGINT UNSIGNED
$table->boolean('col')
TINYINT(1)
$table->decimal('col', 8, 2)
DECIMAL(8,2)
$table->json('col')
JSON
$table->timestamp('col')
TIMESTAMP
$table->foreignId('user_id')->constrained()
FK → users.id
$table->softDeletes()
deleted_at TIMESTAMP NULL
$table->timestamps()
created_at, updated_at
7. Relationships
// One to Manyclass User extends Model{ public function posts(): HasMany { return $this->hasMany(Post::class); }}class Post extends Model{ public function user(): BelongsTo { return $this->belongsTo(User::class); }}// Many to Manyclass Post extends Model{ public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class)->withTimestamps(); }}// Has Many Throughclass Country extends Model{ public function posts(): HasManyThrough { return $this->hasManyThrough(Post::class, User::class); }}
// Posts that have at least one commentPost::has('comments')->get();// Posts that have 3 or more commentsPost::has('comments', '>=', 3)->get();// Posts that have a comment by a specific user (with constraint)Post::whereHas('comments', function ($q) { $q->where('user_id', 5);})->get();// Posts that have NO commentsPost::doesntHave('comments')->get();// NestedPost::whereHas('comments.user', function ($q) { $q->where('role', 'admin');})->get();
Counting Related Records (withCount)
// Add comments_count attribute to each post$posts = Post::withCount('comments')->get();foreach ($posts as $post) { echo $post->comments_count;}// Multiple counts + alias$posts = Post::withCount([ 'comments', 'comments as approved_count' => fn($q) => $q->where('approved', true),])->get();// withCount + has combinedPost::withCount('comments')->has('comments')->orderByDesc('comments_count')->get();
Attach / Detach (Many to Many)
$post->tags()->attach([1, 2, 3]);$post->tags()->detach([2]);$post->tags()->sync([1, 3]); // replace all with these IDs
// .envAPP_ENV=localAPP_DEBUG=trueDB_CONNECTION=mysql// Access in codeenv('APP_ENV', 'production'); // default as second argconfig('app.env'); // via config/app.php