Cheatsheets / Laravel

Laravel Cheatsheet

Complete Laravel reference. Hit Ctrl+P to print.

Artisan CLI

php artisan listList all available commands
php artisan help <command>Show help for a command
php artisan serveStart local development server
php artisan make:model Post -mfscMake model with migration, factory, seeder, controller
php artisan make:controller PostController --resourceMake resourceful controller
php artisan make:migration create_posts_tableCreate a new migration
php artisan make:middleware EnsureVerifiedCreate middleware class
php artisan make:job ProcessPodcastCreate a queueable job
php artisan make:event UserRegisteredCreate an event class
php artisan make:listener SendWelcomeEmail --event=UserRegisteredCreate a listener
php artisan make:command SendEmailsCreate an Artisan command
php artisan tinkerInteractive REPL in app context
php artisan cache:clearFlush the application cache
php artisan config:cacheCache config for production
php artisan route:listList all registered routes
php artisan queue:workProcess jobs from the queue
php artisan schedule:runRun scheduled commands (called by cron)

Routing

Route::get("/posts", [PostController::class, "index"])Basic GET route
Route::post("/posts", [PostController::class, "store"])POST route
Route::resource("posts", PostController::class)Register all resourceful routes
Route::apiResource("posts", PostController::class)Resource routes without create/edit views
Route::get("/posts/{post}", ...)->name("posts.show")Named route
route("posts.show", $post)Generate URL for named route
Route::get("/users/{id}", ...)->where("id", "[0-9]+")Route parameter constraint
Route::middleware(["auth", "verified"])->group(fn() => ...)Apply middleware to group
Route::prefix("admin")->group(fn() => ...)Add URL prefix to group
Route::redirect("/old", "/new", 301)Redirect route
Route::fallback(fn() => view("404"))Fallback for unmatched routes

Eloquent ORM

Post::all()Retrieve all records
Post::find(1)Find by primary key (null if not found)
Post::findOrFail(1)Find or throw ModelNotFoundException
Post::where("active", true)->get()Query with constraint
Post::where("views", ">", 100)->orderBy("created_at", "desc")->get()Chained query constraints
Post::first()Get first matching record
Post::firstOrCreate(["email" => $email], ["name" => $name])Find or create with extra attributes
Post::updateOrCreate(["slug" => $slug], $data)Update or create by attributes
Post::create($data)Create and persist new model (mass assignable)
$post->update($data)Update existing model
$post->delete()Delete a model
Post::destroy([1, 2, 3])Delete by primary keys
Post::with("user", "tags")->get()Eager load relationships
Post::withCount("comments")->get()Load relationship count
Post::paginate(15)Paginate results
Post::chunk(200, fn($posts) => ...)Process large results in chunks
Post::whereHas("comments", fn($q) => $q->where("approved", true))Filter by relationship existence
Post::doesntHave("comments")->get()Filter records with no related models
Post::latest()->limit(5)->get()Get 5 most recent
Post::select("id", "title")->get()Select specific columns
Post::whereBetween("views", [100, 1000])->get()Between constraint
Post::whereIn("status", ["draft", "review"])->get()Where in list
Post::whereNull("published_at")->get()Where column is NULL
Post::withTrashed()->find(1)Include soft-deleted records
Post::onlyTrashed()->get()Only soft-deleted records
$post->restore()Restore a soft-deleted record
Post::withSum("items", "price")->get()Load aggregate sum from relationship
Post::scopePublished($query) { return $query->where("status", "published"); }Define a local query scope
Post::published()->latest()->get()Use local scope in query

Relationships

public function user(): BelongsTo { return $this->belongsTo(User::class); }Belongs to (post belongs to user)
public function posts(): HasMany { return $this->hasMany(Post::class); }Has many (user has many posts)
public function profile(): HasOne { return $this->hasOne(Profile::class); }Has one
public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class); }Many-to-many with pivot table
public function latestPost(): HasOne { return $this->hasOne(Post::class)->latestOfMany(); }Has one of many
$post->userAccess related model (auto eager-loads)
$user->posts()->where("active", true)->get()Query through relationship
$post->tags()->attach($tagId)Attach pivot record
$post->tags()->detach($tagId)Detach pivot record
$post->tags()->sync([1, 2, 3])Sync pivot IDs (detaches missing)
$post->tags()->syncWithoutDetaching([4, 5])Add pivot IDs without removing existing
$post->tags()->attach($tagId, ["order" => 1])Attach with extra pivot data
public function posts(): HasMany { return $this->hasMany(Post::class)->orderBy("created_at", "desc"); }Relationship with default ordering
public function activeComments(): HasMany { return $this->hasMany(Comment::class)->where("approved", true); }Constrained relationship
public function country(): HasOneThrough { return $this->hasOneThrough(Country::class, User::class); }Has one through
public function posts(): HasManyThrough { return $this->hasManyThrough(Post::class, User::class); }Has many through

Migrations

php artisan migrateRun pending migrations
php artisan migrate:rollbackRoll back last batch
php artisan migrate:fresh --seedDrop all tables and re-run with seeders
$table->id()Auto-incrementing BIGINT primary key
$table->string("title")VARCHAR(255)
$table->text("body")TEXT column
$table->boolean("active")->default(true)BOOLEAN with default
$table->foreignId("user_id")->constrained()->cascadeOnDelete()Foreign key with constraint
$table->timestamps()created_at and updated_at columns
$table->softDeletes()Add deleted_at for soft deletes
$table->index(["user_id", "created_at"])Composite index
$table->unique("email")Unique constraint
$table->unique(["user_id", "post_id"])Composite unique constraint
$table->json("meta")JSON column
$table->decimal("price", total: 8, places: 2)DECIMAL with precision
$table->enum("status", ["draft", "published", "archived"])ENUM column
$table->unsignedBigInteger("views")->default(0)Unsigned BIGINT with default
$table->string("title")->after("id")Add column after another
$table->string("title")->change()Modify existing column (in alter migration)
$table->renameColumn("old", "new")Rename a column
$table->dropColumn("old_field")Drop a column
Schema::dropIfExists("posts")Drop table in down() method

Blade Templates

{{ $variable }}Echo escaped output
{!! $html !!}Echo unescaped output (use with caution)
@if ($condition) ... @elseif (...) ... @else ... @endifConditional
@foreach ($items as $item) ... @endforeachLoop over collection
@forelse ($items as $item) ... @empty ... @endforelseLoop with empty fallback
@for ($i = 0; $i < 10; $i++) ... @endforFor loop
$loop->index / $loop->first / $loop->last / $loop->countLoop variable properties
@extends("layouts.app")Extend a layout
@section("content") ... @endsectionDefine a named section
@yield("content")Render a section in the layout
@include("partials.nav")Include a sub-view
@component("components.alert", ["type" => "error"])Include a component with data
@csrfCSRF hidden input field (required in forms)
@method("PUT")Spoof HTTP method in form
@auth ... @endauth / @guest ... @endguestAuth conditional blocks
@can("update", $post) ... @endcanPolicy authorization check
@push("scripts") <script>...</script> @endpushPush content to a stack
@stack("scripts")Render a stack in layout

Auth & Authorization

Auth::user()Get the authenticated user
Auth::id()Get authenticated user ID
Auth::check()Check if user is authenticated
Auth::attempt(["email" => $email, "password" => $password])Attempt login
Auth::logout()Log out the user
auth()->user()Helper equivalent of Auth::user()
$request->user()Get user from request
Gate::define("update-post", fn($user, $post) => $user->id === $post->user_id)Define a Gate
Gate::allows("update-post", $post)Check a Gate
$this->authorize("update", $post)Authorize in controller (throws 403)

Request & Validation

$request->input("name")Get input value
$request->input("name", "default")Get input with default
$request->all()Get all input as array
$request->only(["name", "email"])Get subset of input
$request->except(["password"])Get all input except keys
$request->has("name")Check if input key exists
$request->file("avatar")Get uploaded file
$request->validate(["title" => "required|max:255", "email" => "required|email|unique:users"])Inline validation (throws on failure)
"required|string|min:3|max:255"Common rule string format
"nullable|image|mimes:jpg,png|max:2048"File validation rules
php artisan make:request StorePostRequestCreate a Form Request class
public function rules(): array { return ["title" => "required"]; }Rules method in Form Request

Collections

collect([1, 2, 3])Create a collection
$col->map(fn($item) => $item * 2)Transform each item
$col->filter(fn($item) => $item > 1)Filter items (keeps keys)
$col->reject(fn($item) => $item > 1)Inverse of filter
$col->each(fn($item) => ...)Iterate without transforming
$col->pluck("name")Extract a single field from each item
$col->keyBy("id")Key collection by field value
$col->groupBy("status")Group items by field value
$col->first() / ->last()Get first or last item
$col->count() / ->sum("price") / ->avg("score")Aggregate methods
$col->sortBy("name") / ->sortByDesc("created_at")Sort collection
$col->unique("email")Remove duplicates by field
$col->values()Reset keys to 0-indexed
$col->toArray() / ->toJson()Convert to array or JSON

Queues & Jobs

dispatch(new ProcessPodcast($podcast))Dispatch a job
ProcessPodcast::dispatch($podcast)Static dispatch helper
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10))Delay job execution
ProcessPodcast::dispatch($podcast)->onQueue("high")Dispatch to specific queue
Bus::chain([new A, new B, new C])->dispatch()Chain jobs sequentially
Bus::batch([...])->dispatch()Dispatch a batch of jobs
public $tries = 3;Max retry attempts on job class
public $backoff = [1, 5, 10];Retry backoff in seconds
php artisan queue:work --queue=high,defaultWorker processing priority queues
php artisan queue:failedList failed jobs
php artisan queue:retry allRetry all failed jobs

Cache, Config & Helpers

Cache::get("key", "default")Get cached value
Cache::put("key", $value, now()->addHours(1))Store in cache with expiry
Cache::remember("key", 3600, fn() => DB::table("users")->get())Get or store if missing
Cache::forget("key")Remove a cache entry
Cache::flush()Clear all cache
config("app.name")Read config value
config(["app.name" => "My App"])Set config at runtime
env("APP_KEY")Read environment variable
url("/path")Generate absolute URL
asset("css/app.css")Generate URL for public asset
now() / today() / Carbon::parse("2025-01-01")Date helpers
str("hello world")->title()->slug()Fluent string helpers
Str::uuid() / Str::random(32)Generate UUID or random string

HTTP Client

Http::get("https://api.example.com/users")GET request
Http::post("https://api.example.com/users", ["name" => "Alice"])POST with JSON body
Http::put("https://api.example.com/users/1", $data)PUT request
Http::delete("https://api.example.com/users/1")DELETE request
Http::get(url)->json()Get response as array
Http::get(url)->body()Get raw response string
Http::get(url)->status()Get HTTP status code
Http::get(url)->successful()True if 2xx response
Http::get(url)->throw()Throw exception on 4xx/5xx
Http::withToken($token)->get(url)Bearer token auth
Http::withBasicAuth($user, $pass)->get(url)Basic auth
Http::withHeaders(["X-Custom" => "value"])->get(url)Custom headers
Http::timeout(30)->get(url)Set request timeout in seconds
Http::retry(3, 100)->get(url)Retry up to 3 times with 100ms delay
Http::withQueryParameters(["page" => 2])->get(url)Append query string params
Http::asForm()->post(url, $data)Send as form-encoded instead of JSON
Http::fake(["example.com/*" => Http::response(["ok" => true], 200)])Fake responses in tests

Storage & Files

Storage::put("file.txt", $contents)Write file to default disk
Storage::get("file.txt")Read file contents
Storage::exists("file.txt")Check if file exists
Storage::delete("file.txt")Delete a file
Storage::url("images/photo.jpg")Get public URL for file
Storage::temporaryUrl("file.txt", now()->addMinutes(5))Temporary signed URL (S3 etc.)
Storage::disk("s3")->put("file.txt", $contents)Use a specific disk
Storage::disk("local")->files("uploads/")List files in directory
Storage::makeDirectory("uploads/2025")Create directory
Storage::copy("old.txt", "new.txt")Copy a file
Storage::move("old.txt", "new.txt")Move / rename a file
Storage::size("file.txt")Get file size in bytes
$request->file("avatar")->store("avatars")Store uploaded file, auto-named
$request->file("avatar")->storeAs("avatars", "user-1.jpg")Store uploaded file with explicit name
$request->file("avatar")->store("avatars", "s3")Store uploaded file on specific disk

Task Scheduling

$schedule->command("emails:send")->daily()Run Artisan command daily at midnight
$schedule->command("report")->dailyAt("08:00")Run daily at specific time
$schedule->command("sync")->hourly()Run every hour
$schedule->command("sync")->everyFiveMinutes()Run every 5 minutes
$schedule->command("sync")->everyMinute()Run every minute
$schedule->command("report")->weekly()->mondays()->at("09:00")Weekly on Mondays at 9am
$schedule->command("backup")->monthly()Run once a month
$schedule->command("sync")->cron("0 */6 * * *")Custom cron expression
$schedule->job(new ProcessPodcast)->daily()Schedule a queued job
$schedule->call(fn() => Cache::flush())->nightly()Schedule a closure
->withoutOverlapping()Prevent running if previous instance still active
->runInBackground()Run in background, not blocking other tasks
->onOneServer()Only run on one server when using multiple workers
->environments(["production"])Only run in specified environments
->when(fn() => Feature::active("sync"))Conditional execution
->sendOutputTo(storage_path("logs/schedule.log"))Write output to file
* * * * * php /var/www/html/artisan schedule:run >> /dev/null 2>&1Cron entry to drive the scheduler

DB Facade & Transactions

DB::table("users")->get()Query builder - get all rows
DB::table("users")->where("active", 1)->first()Query builder with constraint
DB::table("users")->insert(["name" => "Alice", "email" => "a@b.com"])Insert a row
DB::table("users")->where("id", 1)->update(["name" => "Bob"])Update rows
DB::table("users")->where("id", 1)->delete()Delete rows
DB::select("SELECT * FROM users WHERE id = ?", [1])Raw SELECT query
DB::statement("ALTER TABLE users ADD COLUMN bio TEXT")Run arbitrary SQL statement
DB::transaction(function () { ... })Wrap operations in a transaction (auto rollback on exception)
DB::beginTransaction(); DB::commit(); DB::rollBack();Manual transaction control
DB::transaction(function () { ... }, attempts: 3)Retry transaction on deadlock
DB::listen(fn($query) => logger($query->sql))Listen to all executed queries
DB::getQueryLog()Get log of executed queries (requires enableQueryLog())

Logging

Log::info("User logged in", ["id" => $user->id])Log info with context array
Log::warning("Disk space low")Log a warning
Log::error("Payment failed", ["error" => $e->getMessage()])Log an error with context
Log::debug("Query result", ["rows" => $rows])Log debug - filtered out in production
Log::critical("Database unreachable")Log critical - triggers alerts
logger("Something happened")Helper shorthand for Log::debug()
logger()->error("Oops", ["trace" => $e])Helper with level method
Log::channel("slack")->error("Alert!")Log to a specific channel
Log::stack(["daily", "slack"])->info("Deployed")Log to multiple channels at once
Log::withContext(["request_id" => $id])Add persistent context to all subsequent log entries

Sanctum (API Tokens)

php artisan install:apiInstall Sanctum (Laravel 11+, adds api.php routes)
$user->createToken('name')Create personal access token
$user->createToken('name', ['read'])Create token with abilities
$token->accessTokenThe plaintext token (only available at creation)
$request->user()->tokenCan('read')Check token ability in request
$user->tokens()->delete()Revoke all user tokens
$user->currentAccessToken()->delete()Revoke current token
HasApiTokens trait on User modelRequired to use Sanctum token methods

Pest Testing

it('does something', fn() => ...)Functional test style
test('name', fn() => ...)Alternative syntax
expect($val)->toBe($expected)Fluent assertion (strict equality)
expect($val)->toEqual($expected)Deep equality assertion
expect($val)->toBeTrue()Assert strictly true
expect($val)->toBeFalse()Assert strictly false
expect($val)->toBeNull()Assert null
expect($val)->toContain($needle)String or array contains
expect($val)->toMatchArray([...])Array subset match
expect($val)->toHaveKey('key')Array/object has key
expect($fn)->toThrow(Exception::class)Assert throws exception
expect($val)->not->toBe($x)Negate any expectation
beforeEach(fn() => ...)Setup hook before each test
afterEach(fn() => ...)Teardown hook after each test
dataset('name', [...])Shared dataset for parameterized tests
uses(RefreshDatabase::class)Apply Laravel trait to test file

Collections Pipeline

collect([...])->map(fn($x) => $x * 2)Transform each item
->filter(fn($x) => $x > 2)Keep items matching the callback
->reject(fn($x) => $x > 2)Remove items matching the callback
->reduce(fn($carry, $item) => $carry + $item, 0)Reduce collection to a single value
->each(fn($item) => ...)Iterate without transforming
->tap(fn($col) => ...)Inspect collection without interrupting chain
->when($condition, fn($col) => ...)Conditional pipeline step
->unless($condition, fn($col) => ...)Inverse conditional step
->pipe(fn($col) => ...)Pass collection to callback, return result
->flatMap(fn($x) => [...])Map then flatten one level
->groupBy('key')Group items by key or callback
->sortBy('key')Sort by key or callback
->unique('key')Remove duplicates by key
->values()Re-index keys sequentially
->keyBy('id')Re-key collection by field value
->pluck('name')Extract a field from each item
->chunk(3)Split into chunks of N items
->flatten()Flatten nested arrays
->zip([1,2,3])Merge with another array element-by-element
->combine(['a','b'])Use collection as keys, argument as values
->mapWithKeys(fn($item) => [$item->id => $item->name])Remap with custom keys
->first(fn($x) => $x > 2)First item matching callback
->last(fn($x) => $x > 2)Last item matching callback
->contains('key', 'value')Check if collection has item matching key/value
->count()Number of items
->sum('amount')Sum a field across all items
->avg('score')Average of a field
->min('price') / ->max('price')Min or max of a field

Helper Methods

$query->when($cond, fn($q) => ...)Conditional method chaining on Eloquent Builder
$query->tap(fn($q) => ...)Inspect query without interrupting chain
$query->unless($cond, fn($q) => ...)Inverse conditional on query
$query->withCount('relation')Add COUNT sub-query as attribute
$query->withSum('relation', 'col')Add SUM sub-query as attribute
$query->withAvg('relation', 'col')Add AVG sub-query as attribute
$query->withMax('relation', 'col')Add MAX sub-query as attribute
$query->withMin('relation', 'col')Add MIN sub-query as attribute
$query->withExists('relation')Add EXISTS check as boolean attribute
Str::of('hello world')Start a fluent string chain
->upper() / ->lower() / ->title()Case conversion (fluent)
->trim() / ->ltrim() / ->rtrim()Trim whitespace (fluent)
->slug('-') / ->snake() / ->camel()Case/slug conversion (fluent)
->contains('hello')Check string contains
->startsWith('he') / ->endsWith('d')Prefix/suffix check
->replace('old', 'new')Replace substring
->split('/\s+/')Split string to collection
->words(10, '...')Truncate to N words
->limit(50, '...')Truncate to N characters
Arr::get($array, 'a.b.c', $default)Dot-notation array access with default
Arr::set($array, 'a.b', $val)Dot-notation set
Arr::has($array, 'a.b')Dot-notation existence check
Arr::only($array, ['a','b'])Keep only specified keys
Arr::except($array, ['a','b'])Remove specified keys
Arr::pluck($array, 'name')Extract field from each item
Arr::flatten($array, $depth)Flatten nested array to given depth
Arr::wrap($val)Wrap scalar in array (no-op for arrays)