Developer Cheat Codes by Jezer Niel Blanca
Laravel Artisan & Eloquent
The Artisan commands and Eloquent patterns I reach for on every Laravel 12 project.
Artisan is Laravel's command-line toolbox and Eloquent is its ORM for talking to the database with PHP models. This sheet is for developers who know basic PHP and want the Laravel 12 shortcuts that save time on every project.
Try it yourself: Run php artisan make:model Project -mfs in a fresh Laravel app, migrate, then open php artisan tinker and try the querying snippets against a few factory-made rows.
Artisan Generators
- Model with migration, factory, seeder
-
Creates the Project model plus its migration, factory and seeder in one go, so a new database table is ready to design, fake and seed.
php artisan make:model Project -mfs
Example output:
INFO Model [app/Models/Project.php] created successfully.
INFO Factory [database/factories/ProjectFactory.php] created successfully.
INFO Migration [database/migrations/2026_09_27_101500_create_projects_table.php] created successfully.
INFO Seeder [database/seeders/ProjectSeeder.php] created successfully.
- Resource controller
-
Generates a controller with the seven CRUD methods (index, create, store, show, edit, update, destroy) that pair with Route::resource().
php artisan make:controller ProjectController --resource
Example output:
INFO Controller [app/Http/Controllers/ProjectController.php] created successfully.
- Form Request
-
Creates a class that holds validation rules and authorization for one request, keeping controllers thin. Type-hint it in a controller method and it validates automatically.
php artisan make:request StoreProjectRequest
Example output:
INFO Request [app/Http/Requests/StoreProjectRequest.php] created successfully.
- Queued job
-
Scaffolds a job class for slow work like emails or PDFs. Dispatch it with SendInvoice::dispatch($order) and a queue worker runs it in the background.
php artisan make:job SendInvoice
Example output:
INFO Job [app/Jobs/SendInvoice.php] created successfully.
- Pest feature test
-
Creates a Pest feature test in tests/Feature. Feature tests hit routes like a real user, which catches more bugs than testing classes in isolation.
php artisan make:test ProjectTest --pest
Example output:
INFO Test [tests/Feature/ProjectTest.php] created successfully.
- Policy for a model
-
Generates a policy with view, create, update and delete methods for Project. Use it to answer "is this user allowed to do this?" in one place.
php artisan make:policy ProjectPolicy --model=Project
Example output:
INFO Policy [app/Policies/ProjectPolicy.php] created successfully.
Database & Maintenance
- Run migrations
-
Runs every migration that has not run yet, creating or changing tables. Laravel records each one so it never runs twice.
php artisan migrate
Example output:
INFO Running migrations.
2026_09_27_101500_create_projects_table ............ 14.52ms DONE
- Roll back last batch
-
Undoes the most recent batch of migrations by running their down() methods. Handy when you spot a mistake in a migration you just ran locally.
php artisan migrate:rollback
Example output:
INFO Rolling back migrations.
2026_09_27_101500_create_projects_table ............. 6.08ms DONE
- Run a single seeder
-
Runs just one seeder instead of the whole DatabaseSeeder, useful for refreshing one kind of demo data without touching the rest.
php artisan db:seed --class=BlogSeeder
Example output:
INFO Seeding database.
- List routes
-
Prints your app's routes with methods, URIs, names and controllers, hiding routes registered by packages. Great for finding a route name fast.
php artisan route:list --except-vendor
Example output:
GET|HEAD / ..................................... home › HomeController
GET|HEAD blog ........................ blog.index › BlogController@index
GET|HEAD blog/{post:slug} .............. blog.show › BlogController@show
Showing [3] routes
- Cache config, routes, views
-
Caches config, events, routes and compiled views so production boots faster. Run it on deploy, not while developing, or your changes will seem ignored.
php artisan optimize
Example output:
INFO Caching framework bootstrap, configuration, and metadata.
config ........................................ 21.40ms DONE
events ......................................... 1.12ms DONE
routes ........................................ 24.87ms DONE
views ......................................... 88.30ms DONE
- Clear all caches
-
Wipes every cached file Laravel created. The first thing to try when a config or route change mysteriously is not showing up.
php artisan optimize:clear
Example output:
INFO Clearing cached bootstrap files.
cache .......................................... 4.02ms DONE
config ......................................... 0.61ms DONE
routes ......................................... 0.58ms DONE
views .......................................... 7.94ms DONE
Querying
- Eager load with constraint
-
Loads projects and only their 5 newest tasks in two queries total, avoiding N+1 queries. Laravel 11+ applies the limit per project, not overall.
Project::query()
->with(['tasks' => fn ($q) => $q->latest()->limit(5)])
->get();
Example output:
= Illuminate\Database\Eloquent\Collection {#6150
all: [
App\Models\Project {#6152 id: 1, name: "Portfolio", tasks: [5 items]},
App\Models\Project {#6153 id: 2, name: "Blog", tasks: [3 items]},
],
}
- Count relations without loading
-
Adds a posts_count attribute to each user using a subquery, so you can show "12 posts" without loading every post into memory.
User::query()->withCount('posts')->get();
Example output:
[{"id":1,"name":"Ana","posts_count":12},{"id":2,"name":"Ben","posts_count":0}]
- Filter by relationship
-
Returns only posts that have at least one approved comment. The callback filters the related rows, not the posts themselves.
Post::query()->whereHas('comments', fn ($q) => $q->where('approved', true))->get();
Example output:
[{"id":3,"title":"Hello Inertia"},{"id":8,"title":"Tailwind v4 tips"}]
- Find or fail
-
Fetches a record by primary key or throws ModelNotFoundException, which Laravel turns into a 404 page. No manual null checks needed.
Project::query()->findOrFail($id);
Example output:
No query results for model [App\Models\Project] 999 (rendered as 404 Not Found)
- Iterate large tables
-
Streams users in chunks of 500 ordered by id, so memory stays flat even with millions of rows. Safer than chunk() when you update rows as you go.
User::query()->lazyById(500)->each(fn (User $user) => $user->touch());
Writing Data
- Create through relationship
-
Creates a project with user_id filled in automatically from the relationship, using only validated input. No chance of forgetting the owner.
$request->user()->projects()->create($request->validated());
Example output:
= App\Models\Project {#6123 id: 7, user_id: 1, name: "Portfolio v2", created_at: "2026-09-27 10:15:02"}
- Update or create
-
Finds the row matching the first array and updates it with the second, or creates it if missing. Perfect for settings and idempotent seeders.
Setting::query()->updateOrCreate(
['key' => 'theme'],
['value' => 'dark'],
);
Example output:
= App\Models\Setting {#6021 id: 1, key: "theme", value: "dark"}
- Bulk upsert
-
Inserts many rows in one query and updates amount on rows whose sku already exists. Needs a unique index on sku and skips model events.
Price::query()->upsert($rows, uniqueBy: ['sku'], update: ['amount']);
Example output:
= 3
- Wrap in a transaction
-
Runs both writes as one unit: if anything throws, every change is rolled back. Use it whenever partial success would leave bad data.
DB::transaction(function () use ($order): void {
$order->markPaid();
$order->items()->update(['status' => 'reserved']);
});
Model Features
- Casts method
-
Tells Eloquent to turn status into a PHP enum and meta JSON into an array when reading, and back again when saving.
protected function casts(): array
{
return ['status' => OrderStatus::class, 'meta' => 'array'];
}
Example output:
$order->status; // App\Enums\OrderStatus::Paid
$order->meta['color']; // "blue"
- Scope attribute
-
Laravel 12's #[Scope] attribute turns this method into a reusable query filter, so you can write User::query()->active()->get().
#[Scope]
protected function active(Builder $query): void
{
$query->where('is_active', true);
}
Example output:
User::query()->active()->toSql(); // select * from `users` where `is_active` = ?
- Accessor
-
Adds a computed full_name attribute built from other columns. Read it like a normal property; it is not stored in the database.
protected function fullName(): Attribute
{
return Attribute::make(get: fn () => "{$this->first} {$this->last}");
}
Example output:
$user->full_name; // "Ana Cruz"
- Strict mode in dev
-
Outside production, throws on lazy loading, unknown attributes and silently discarded fills. Put it in AppServiceProvider::boot() to catch N+1 bugs early.
Model::shouldBeStrict(! app()->isProduction());
Example output:
Attempted to lazy load [tasks] on model [App\Models\Project] but lazy loading is disabled.
Git
Everyday Git commands plus the rescue moves for when things go wrong.
Git tracks every change to your code so you can branch, collaborate and travel back in time. This sheet is for developers who use Git daily and want both the everyday flow and the calm, safe way out of a mess.
Try it yourself: Create a throwaway repo with git init, make three small commits, then try reset --soft, reflog and the stash commands on purpose so they feel familiar before you need them.
Daily Workflow
- Create and switch branch
-
Creates a new branch from where you are and moves onto it. Keeps new work isolated from main until it is ready.
git switch -c feature/login
Example output:
Switched to a new branch 'feature/login'
- Stage interactively by hunk
-
Walks through each change and asks whether to stage it. Great for splitting one messy session into clean, focused commits.
git add -p
Example output:
@@ -12,6 +12,7 @@ class User extends Authenticatable
+ use Notifiable;
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y
- Commit with message
-
Saves the staged changes as a snapshot with a message. Write it as a short imperative sentence describing what the commit does.
git commit -m "Add login form"
Example output:
[feature/login 3f2a9c1] Add login form
2 files changed, 48 insertions(+)
create mode 100644 resources/js/Pages/Auth/Login.vue
- Pull with rebase
-
Fetches new remote commits and replays your local commits on top, giving a straight history without extra merge commits.
git pull --rebase
Example output:
Successfully rebased and updated refs/heads/feature/login.
- Push and set upstream
-
Pushes the branch to GitHub (or any remote) and remembers the link, so future git push and git pull need no arguments.
git push -u origin feature/login
Example output:
To github.com:you/app.git
* [new branch] feature/login -> feature/login
branch 'feature/login' set up to track 'origin/feature/login'.
Inspecting History
- Compact graph log
-
Draws every branch as a compact ASCII graph with one line per commit. The quickest way to see how branches relate.
git log --oneline --graph --decorate --all
Example output:
* 3f2a9c1 (HEAD -> feature/login) Add login form
| * 7d1e4b2 (origin/main, main) Fix footer links
|/
* 9b1e0d4 Add blog index page
- Show changes in a commit
-
Shows a commit's author, date, message and full diff. Use the short hash from git log in place of <sha>.
git show <sha>
Example output:
commit 3f2a9c1e8b...
Author: Ana Cruz <ana@example.com>
Date: Sat Sep 27 10:12:44 2026 +0800
Add login form
diff --git a/routes/web.php b/routes/web.php
- Who changed these lines
-
Shows who last changed each line in the range and in which commit. Use it to find context for code, not to assign blame.
git blame -L 10,30 app/Models/User.php
Example output:
a1b2c3d4 (Ana Cruz 2026-08-14 10:22:05 +0800 10) protected $fillable = [
a1b2c3d4 (Ana Cruz 2026-08-14 10:22:05 +0800 11) 'name',
5e6f7a8b (Ben Reyes 2026-09-02 16:40:11 +0800 12) 'avatar_url',
- Find commits adding/removing a string
-
The "pickaxe" search: lists commits where the number of occurrences of that string changed. Perfect for finding when a function appeared or vanished.
git log -S "calculateTax" --oneline
Example output:
7c4d2e1 Extract calculateTax helper
2b9f0a3 Add tax to invoice totals
- Diff staged changes
-
Shows exactly what will go into your next commit. Plain git diff only shows unstaged changes, which trips up many people.
git diff --staged
Example output:
diff --git a/routes/web.php b/routes/web.php
@@ -8,3 +8,4 @@
Route::get('/', HomeController::class)->name('home');
+Route::get('/login', LoginController::class)->name('login');
Undo & Rescue
- Undo last commit, keep changes
-
Removes the last commit but keeps its changes staged, so you can fix the message or split it up. Only do this on commits you have not pushed.
git reset --soft HEAD~1
Example output:
(no output) git status now shows:
Changes to be committed:
modified: resources/js/Pages/Auth/Login.vue
- Amend last commit
-
Folds your staged changes into the previous commit and keeps its message. Ideal for "oops, forgot a file". Rewrites history, so avoid on pushed commits.
git commit --amend --no-edit
Example output:
[feature/login 5e6f7a8] Add login form
Date: Sat Sep 27 10:12:44 2026 +0800
3 files changed, 52 insertions(+)
- Revert a pushed commit
-
Creates a new commit that undoes an old one without rewriting history. The safe way to undo something already shared with your team.
git revert <sha>
Example output:
[main 8d7c6b5] Revert "Add login form"
2 files changed, 48 deletions(-)
- Discard file changes
-
Throws away uncommitted edits to a file and restores the last committed version. There is no undo for this, so double-check first.
git restore path/to/file
- Unstage a file
-
Moves a file out of the staging area but keeps your edits in the working tree. The opposite of git add.
git restore --staged path/to/file
- Recover lost commit
-
The reflog records everywhere HEAD has been, even after a bad reset. Find the lost commit in the list and put a new branch on it.
git reflog
git switch -c rescue HEAD@{2}
Example output:
7d1e4b2 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
3f2a9c1 HEAD@{1}: commit: Add login form
9b1e0d4 HEAD@{2}: commit: Add payment webhook
Switched to a new branch 'rescue'
Stash & Cherry-pick
- Stash including untracked
-
Shelves all uncommitted work, including new files, so you get a clean tree to switch branches or pull. The message helps you find it later.
git stash push -u -m "wip"
Example output:
Saved working directory and index state On feature/login: wip
- List stashes
-
Shows your saved stashes, newest first as stash@{0}. Use git stash show -p stash@{1} to peek inside one.
git stash list
Example output:
stash@{0}: On feature/login: wip
stash@{1}: WIP on main: 7d1e4b2 Fix footer links
- Apply and drop stash
-
Re-applies the newest stash and removes it from the list. If it conflicts, the stash is kept so nothing is lost.
git stash pop
Example output:
On branch feature/login
Changes not staged for commit:
modified: app/Models/User.php
Dropped refs/stash@{0} (c0ffee12ab34cd56ef7890ab12cd34ef56789012)
- Copy a commit to this branch
-
Applies one specific commit from another branch onto the current one. Handy for shipping a hotfix without merging a whole feature.
git cherry-pick <sha>
Example output:
[main 4a5b6c7] Fix rounding in tax calculation
Date: Fri Sep 26 15:03:18 2026 +0800
1 file changed, 2 insertions(+), 1 deletion(-)
Remote & Cleanup
- Safer force push
-
Force-pushes after a rebase or amend, but refuses if someone else pushed in the meantime. Always prefer it over a plain --force.
git push --force-with-lease
Example output:
To github.com:you/app.git
+ 3f2a9c1...5e6f7a8 feature/login -> feature/login (forced update)
- Prune deleted remote branches
-
Fetches updates and removes local references to branches that were deleted on the remote, keeping git branch -r tidy.
git fetch --prune
Example output:
From github.com:you/app
- [deleted] (none) -> origin/feature/old-nav
- Delete local branch
-
Deletes a local branch only if it has been merged, protecting unmerged work. Use -D if you really want to throw it away.
git branch -d feature/login
Example output:
Deleted branch feature/login (was 3f2a9c1).
- Preview untracked file cleanup
-
Dry run (-n) that lists untracked files and folders git clean would delete. Check the list, then drop the n to actually delete.
git clean -fdn
Example output:
Would remove storage/debug.log
Would remove tmp/
- Tag a release
-
Creates an annotated tag marking a release and pushes it, since tags are not pushed by default. GitHub can build a release from it.
git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0
Example output:
To github.com:you/app.git
* [new tag] v1.2.0 -> v1.2.0
Docker & Compose
The Docker and Compose commands that cover almost all day-to-day web development.
Docker packages an app and everything it needs into containers, and Compose runs several containers together from one YAML file. This sheet is for web developers who want a repeatable local stack without installing every service by hand.
Try it yourself: Run docker run --rm -it -p 8080:80 nginx:alpine, open http://localhost:8080, then use docker ps, logs and exec against it from a second terminal.
Containers
- Run and remove on exit
-
Starts an Nginx container, maps port 8080 on your machine to port 80 inside, and deletes the container when you press Ctrl+C.
docker run --rm -it -p 8080:80 nginx:alpine
Example output:
/docker-entrypoint.sh: Configuration complete; ready for start up
172.17.0.1 - - [27/Sep/2026:02:15:04 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0"
- List running containers
-
Lists running containers with their IDs, images, ports and names. The name or ID is what you pass to other commands.
docker ps
Example output:
CONTAINER ID IMAGE STATUS PORTS NAMES
a1b2c3d4e5f6 nginx:alpine Up 2 minutes 0.0.0.0:8080->80/tcp quirky_hopper
- List all containers
-
Includes stopped containers too. Useful for finding one that crashed so you can read its logs.
docker ps -a
Example output:
CONTAINER ID IMAGE STATUS NAMES
a1b2c3d4e5f6 nginx:alpine Up 2 minutes quirky_hopper
9f8e7d6c5b4a mysql:8.4 Exited (1) 1 hour ago old_mysql
- Follow logs
-
Streams a container's stdout and stderr live, like tail -f. Add --tail 50 to skip old history.
docker logs -f <container>
Example output:
172.17.0.1 - - [27/Sep/2026:02:16:10 +0000] "GET /favicon.ico HTTP/1.1" 404 153
172.17.0.1 - - [27/Sep/2026:02:16:12 +0000] "GET / HTTP/1.1" 200 615
- Open a shell inside
-
Opens an interactive shell inside a running container so you can poke around files and run commands. Use bash if the image has it.
docker exec -it <container> sh
Example output:
/ # ls /usr/share/nginx/html
50x.html index.html
- Stop and remove
-
Gracefully stops the container, then deletes it. Each command echoes back the name or ID you gave it.
docker stop <container>
docker rm <container>
Example output:
quirky_hopper
quirky_hopper
Images
- Build and tag
-
Builds an image from the Dockerfile in the current folder and names it my-app:latest. Unchanged steps are reused from cache.
docker build -t my-app:latest .
Example output:
[+] Building 12.4s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> [2/5] RUN apk add --no-cache php83
=> exporting to image
=> => naming to docker.io/library/my-app:latest
- List images
-
Shows images stored on your machine with tag and size. Big sizes are a hint to use smaller base images or multi-stage builds.
docker images
Example output:
REPOSITORY TAG IMAGE ID CREATED SIZE
my-app latest 4f1c2d3e4a5b 5 minutes ago 182MB
nginx alpine b2c3d4e5f6a7 2 weeks ago 52.5MB
- Pull an image
-
Downloads an image from Docker Hub without running it. Pinning a version tag like 8.4 is safer than relying on latest.
docker pull mysql:8.4
Example output:
8.4: Pulling from library/mysql
Digest: sha256:3c1d...
Status: Downloaded newer image for mysql:8.4
docker.io/library/mysql:8.4
- Remove an image
-
Deletes an image to free disk space. It fails if a container (even a stopped one) still uses it.
docker rmi my-app:latest
Example output:
Untagged: my-app:latest
Deleted: sha256:4f1c2d3e4a5b...
- Build without cache
-
Rebuilds every layer from scratch. Use it when a cached step hides a change, like a package update behind the same install command.
docker build --no-cache -t my-app .
Example output:
[+] Building 58.9s (10/10) FINISHED
=> [2/5] RUN apk add --no-cache php83
=> => naming to docker.io/library/my-app:latest
Compose
- Start in background
-
Starts every service in compose.yaml in detached mode, creating the network and volumes if needed. Your terminal stays free.
docker compose up -d
Example output:
[+] Running 3/3
✔ Network app_default Created
✔ Container app-mysql-1 Started
✔ Container app-app-1 Started
- Rebuild and start
-
Rebuilds images for services with a build section before starting. Run it after editing a Dockerfile or dependencies.
docker compose up -d --build
Example output:
[+] Building 8.1s (12/12) FINISHED
[+] Running 2/2
✔ Container app-mysql-1 Running
✔ Container app-app-1 Started
- Service status
-
Lists the containers for this project with their state and published ports. The first check when something is not reachable.
docker compose ps
Example output:
NAME SERVICE STATUS PORTS
app-app-1 app Up 3 minutes 0.0.0.0:8000->80/tcp
app-mysql-1 mysql Up 3 minutes 3306/tcp, 33060/tcp
- Tail logs for a service
-
Follows the logs for just the app service, with each line prefixed by the container name. Leave off the name to see all services.
docker compose logs -f app
Example output:
app-1 | NOTICE: fpm is running, pid 1
app-1 | NOTICE: ready to handle connections
- Run a command in a service
-
Runs a one-off command inside the already running app container, so it uses the container's PHP and database connection.
docker compose exec app php artisan migrate
Example output:
INFO Running migrations.
2026_09_27_101500_create_projects_table ............ 14.52ms DONE
- Stop and remove
-
Stops and removes the project's containers and network. Named volumes, and so your database data, are kept.
docker compose down
Example output:
[+] Running 3/3
✔ Container app-app-1 Removed
✔ Container app-mysql-1 Removed
✔ Network app_default Removed
Volumes & Cleanup
- List volumes
-
Lists volumes, which is where containers keep data that must survive restarts, like MySQL tables.
docker volume ls
Example output:
DRIVER VOLUME NAME
local app_mysql-data
- Down and delete volumes (wipes data)
-
Like down, but also deletes the project's volumes. Your local database starts empty next time, so only use it when you mean it.
docker compose down -v
Example output:
[+] Running 4/4
✔ Container app-app-1 Removed
✔ Container app-mysql-1 Removed
✔ Volume app_mysql-data Removed
✔ Network app_default Removed
- Disk usage summary
-
Shows how much disk images, containers, volumes and build cache use, and how much could be reclaimed.
docker system df
Example output:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 12 3 4.21GB 3.05GB (72%)
Containers 3 2 12.4MB 1.1MB (8%)
Local Volumes 5 2 1.8GB 640MB (35%)
Build Cache 48 0 2.3GB 2.3GB
- Remove dangling images
-
Deletes untagged leftover images from old builds after asking for confirmation. Add -a to remove every unused image.
docker image prune
Example output:
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:7a8b9c...
Total reclaimed space: 412.6MB
- Clear build cache
-
Removes unused build cache, often the biggest hidden disk hog. Your next build will be slower while the cache refills.
docker builder prune
Example output:
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Total: 2.3GB
SQL
Core SQL queries, joins, aggregates, and window functions for MySQL 8+ and PostgreSQL.
SQL is the language relational databases like MySQL and PostgreSQL speak. This sheet is for developers who use an ORM day to day but want to read, write and debug the raw queries underneath with confidence.
Try it yourself: Create customers and orders tables with a handful of rows in a local database, then run each join and window function query and predict the result before you hit enter.
Basics
- Select with filter and sort
-
Returns the 10 newest active users, only the columns you need. Selecting specific columns instead of * is faster and clearer.
SELECT id, name FROM users
WHERE is_active = true
ORDER BY created_at DESC
LIMIT 10;
Example output:
id | name
----+-----------
42 | Ana Cruz
41 | Ben Reyes
39 | Carla Lim
(3 rows)
- Pattern match
-
The % wildcard matches any characters, so this finds every example.com email. A leading % cannot use an index, so it is slow on big tables.
SELECT * FROM users WHERE email LIKE '%@example.com';
Example output:
id | name | email
----+----------+-----------------
1 | Ana Cruz | ana@example.com
4 | Dan Uy | dan@example.com
- Range and list
-
BETWEEN is inclusive on both ends, and IN matches any value in the list. Much cleaner than chaining OR conditions.
SELECT * FROM orders
WHERE total BETWEEN 100 AND 500
AND status IN ('paid', 'shipped');
Example output:
id | total | status
----+--------+---------
7 | 120.00 | paid
12 | 500.00 | shipped
- Distinct values
-
Returns each unique value once. Handy for filling a dropdown or checking what values actually exist in a column.
SELECT DISTINCT country FROM customers;
Example output:
country
---------
PH
SG
US
- Handle NULLs
-
COALESCE returns the first non-NULL value, so users without a nickname fall back to their name. Remember NULL = NULL is never true in SQL.
SELECT COALESCE(nickname, name) AS display_name FROM users;
Example output:
display_name
--------------
Annie
Ben Reyes
Joins
- Inner join
-
Combines rows from both tables where the ON condition matches. Rows without a match on either side are left out.
SELECT o.id, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id;
Example output:
id | name
----+-----------
1 | Ana Cruz
2 | Ana Cruz
3 | Ben Reyes
- Left join (keep all left rows)
-
Keeps every customer even with no orders, filling order columns with NULL. Use it when "none" is a valid answer.
SELECT c.name, o.id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;
Example output:
name | id
-----------+------
Ana Cruz | 1
Ben Reyes | 3
Carla Lim | NULL
- Rows with no match
-
Finds customers who have never ordered. NOT EXISTS is NULL-safe and usually faster than NOT IN with a subquery.
SELECT c.* FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);
Example output:
id | name | country
----+-----------+---------
3 | Carla Lim | SG
- Self join
-
Joins a table to itself using two aliases, here pairing each employee with their manager. The LEFT JOIN keeps the boss who has no manager.
SELECT e.name, m.name AS manager
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id;
Example output:
name | manager
-----------+----------
Ana Cruz | NULL
Ben Reyes | Ana Cruz
Carla Lim | Ana Cruz
Aggregates
- Group and count
-
Collapses rows into one per status and counts each group. Every selected column must be grouped or aggregated.
SELECT status, COUNT(*) AS total
FROM orders
GROUP BY status;
Example output:
status | total
----------+-------
paid | 128
refunded | 6
shipped | 97
- Filter groups
-
WHERE filters rows before grouping; HAVING filters groups after. Here only customers who spent over 1000 remain.
SELECT customer_id, SUM(total) AS spent
FROM orders
GROUP BY customer_id
HAVING SUM(total) > 1000;
Example output:
customer_id | spent
-------------+---------
1 | 1840.50
7 | 1205.00
- Conditional count
-
Counts several categories in a single pass by summing 1s and 0s. Great for dashboard numbers side by side in one row.
SELECT
SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid,
SUM(CASE WHEN status = 'refunded' THEN 1 ELSE 0 END) AS refunded
FROM orders;
Example output:
paid | refunded
------+----------
128 | 6
- Find duplicates
-
Lists values that appear more than once. Run it before adding a unique index, which would fail on existing duplicates.
SELECT email, COUNT(*) FROM users
GROUP BY email
HAVING COUNT(*) > 1;
Example output:
email | count
-----------------+-------
ana@example.com | 2
CTEs & Window Functions
- Common table expression
-
WITH names a subquery so you can read the query top to bottom like steps. Perfect for breaking a monster query into pieces.
WITH big_spenders AS (
SELECT customer_id FROM orders
GROUP BY customer_id HAVING SUM(total) > 1000
)
SELECT * FROM big_spenders;
Example output:
customer_id
-------------
1
7
- Rank within groups
-
Ranks products by price inside each category without collapsing rows. Ties share a rank and the next rank is skipped.
SELECT name, category_id,
RANK() OVER (PARTITION BY category_id ORDER BY price DESC) AS price_rank
FROM products;
Example output:
name | category_id | price_rank
-------------+-------------+------------
Pro Laptop | 1 | 1
Air Laptop | 1 | 2
Desk Lamp | 2 | 1
- Latest row per group
-
Numbers each user's orders newest first, then keeps number 1, giving the latest order per user. A classic interview and dashboard query.
SELECT * FROM (
SELECT o.*, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders o
) t WHERE rn = 1;
Example output:
id | user_id | total | created_at | rn
----+---------+--------+---------------------+----
58 | 1 | 240.00 | 2026-09-26 18:02:11 | 1
61 | 2 | 89.50 | 2026-09-27 09:14:40 | 1
- Running total
-
Adds each order to the sum of all previous ones, giving a cumulative total per row. Great for revenue-over-time charts.
SELECT id, total,
SUM(total) OVER (ORDER BY created_at) AS running_total
FROM orders;
Example output:
id | total | running_total
----+--------+---------------
1 | 100.00 | 100.00
2 | 50.00 | 150.00
3 | 200.00 | 350.00
- Previous row value
-
LAG reads a value from the previous row, so you can compare today with yesterday. The first row has no previous value, so it is NULL.
SELECT day, revenue,
LAG(revenue) OVER (ORDER BY day) AS prev_revenue
FROM daily_sales;
Example output:
day | revenue | prev_revenue
------------+---------+--------------
2026-09-25 | 1200 | NULL
2026-09-26 | 1450 | 1200
2026-09-27 | 980 | 1450
Writes & Performance
- Upsert (PostgreSQL)
-
Inserts the row, or updates it if the sku already exists. EXCLUDED refers to the values you tried to insert. Needs a unique constraint on sku.
INSERT INTO prices (sku, amount) VALUES ('A-1', 1200)
ON CONFLICT (sku) DO UPDATE SET amount = EXCLUDED.amount;
Example output:
INSERT 0 1
- Upsert (MySQL)
-
MySQL's upsert: updates the row when a unique key collides. The AS new alias (MySQL 8.0.19+) replaces the deprecated VALUES() function.
INSERT INTO prices (sku, amount) VALUES ('A-1', 1200) AS new
ON DUPLICATE KEY UPDATE amount = new.amount;
Example output:
Query OK, 2 rows affected (0.01 sec) -- 1 = inserted, 2 = existing row updated
- Update with filter
-
Deactivates users who have not logged in since 2025. Always run the WHERE as a SELECT first; an UPDATE without it hits every row.
UPDATE users SET is_active = false
WHERE last_login_at < '2025-01-01';
Example output:
Query OK, 37 rows affected (0.02 sec)
Rows matched: 37 Changed: 37 Warnings: 0
- Composite index
-
Speeds up queries that filter by customer_id and sort or filter by created_at. Column order matters: the leftmost column must be used.
CREATE INDEX orders_customer_created_idx ON orders (customer_id, created_at);
Example output:
Query OK, 0 rows affected (0.84 sec)
Records: 0 Duplicates: 0 Warnings: 0
- Inspect query plan
-
Actually runs the query and shows how the database executed it and how long each step took. Look for index scans instead of full table scans.
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42;
Example output:
Index Scan using orders_customer_created_idx on orders (cost=0.29..8.45 rows=8 width=64) (actual time=0.021..0.030 rows=8 loops=1)
Index Cond: (customer_id = 42)
Planning Time: 0.110 ms
Execution Time: 0.052 ms
JavaScript ES2024+
Modern JavaScript syntax and built-ins, from destructuring to the newest ES2024 additions.
JavaScript is the language of the browser and, with Node, the server too. This sheet is for developers who learned JS a few years ago and want the modern syntax and built-ins that replace old helper libraries.
Try it yourself: Open your browser console or run node in a terminal, paste each snippet with a small sample array like [3, 1, 2], and compare the result with what you expected.
Syntax Essentials
- Destructuring with defaults
-
Pulls values out of objects and arrays into variables in one line. The default only kicks in when the property is undefined.
const { name, role = 'user' } = user;
const [first, ...rest] = items;
Example output:
console.log(name, role); // Ana user
console.log(first, rest); // 1 [ 2, 3 ]
- Optional chaining
-
Stops and returns undefined if anything in the chain is null or undefined, instead of throwing "Cannot read properties of undefined".
const city = user?.address?.city;
Example output:
// user = { name: 'Ana' }
console.log(city); // undefined
- Nullish coalescing
-
Falls back only when the value is null or undefined, so a real 0 or empty string is kept. That is the key difference from ||.
const limit = options.limit ?? 10;
Example output:
// options.limit = 0
console.log(limit); // 0 (with || it would be 10)
- Logical assignment
-
??= assigns only if the value is null or undefined; ||= assigns if it is any falsy value. Short and clear way to set defaults.
settings.theme ??= 'light';
count ||= 1;
Example output:
// settings = {}, count = 0
console.log(settings.theme, count); // light 1
- Spread to merge objects
-
Copies properties left to right into a new object, so later keys win. A shallow merge: nested objects are shared, not copied.
const merged = { ...defaults, ...overrides };
Example output:
// defaults = { theme: 'light', perPage: 20 }, overrides = { theme: 'dark' }
console.log(merged); // { theme: 'dark', perPage: 20 }
- Numeric separators
-
Underscores make big numbers readable in code and are ignored at runtime. Purely for humans.
const budget = 1_000_000;
Example output:
console.log(budget); // 1000000
Arrays
- Last element
-
at() accepts negative indexes, counting from the end. Much nicer than items[items.length - 1].
const last = items.at(-1);
Example output:
// items = ['apple', 'banana', 'cherry']
console.log(last); // cherry
- Non-mutating sort (ES2023)
-
Returns a new sorted array and leaves the original untouched, unlike sort(). Safer with Vue or React state.
const sorted = items.toSorted((a, b) => a.price - b.price);
Example output:
console.log(sorted.map((i) => i.price)); // [ 5, 12, 30 ]
console.log(items.map((i) => i.price)); // [ 12, 30, 5 ]
- Non-mutating reverse/splice
-
Copy-returning versions of reverse() and splice(). toSpliced(1, 1) gives a new array with the item at index 1 removed.
const reversed = items.toReversed();
const without = items.toSpliced(1, 1);
Example output:
// items = [1, 2, 3, 4]
console.log(reversed); // [ 4, 3, 2, 1 ]
console.log(without); // [ 1, 3, 4 ]
- Find from the end
-
Like find(), but searches from the last element backwards. Great for "most recent matching" lookups on chronological lists.
const lastPaid = orders.findLast((o) => o.status === 'paid');
Example output:
console.log(lastPaid); // { id: 9, status: 'paid' }
- Group by key (ES2024)
-
Groups items into an object keyed by whatever the callback returns. Replaces a hand-written reduce() or lodash groupBy.
const byStatus = Object.groupBy(orders, (o) => o.status);
Example output:
[Object: null prototype] {
paid: [ { id: 1, status: 'paid' }, { id: 3, status: 'paid' } ],
refunded: [ { id: 2, status: 'refunded' } ]
}
- Unique values
-
A Set only keeps unique values, and spreading it back gives a deduplicated array in original order. Works for primitives, not objects.
const unique = [...new Set(tags)];
Example output:
// tags = ['vue', 'php', 'vue', 'css']
console.log(unique); // [ 'vue', 'php', 'css' ]
Async
- Fetch JSON
-
Requests a URL and parses the JSON body. fetch only rejects on network errors, so check response.ok for 404s and 500s.
const response = await fetch('/api/posts');
const posts = await response.json();
Example output:
console.log(posts);
// [ { id: 1, title: 'Hello Inertia' }, { id: 2, title: 'Tailwind v4 tips' } ]
- Run in parallel
-
Starts both requests at once and waits for both, so total time is the slowest one, not the sum. Rejects as soon as any one fails.
const [users, posts] = await Promise.all([getUsers(), getPosts()]);
Example output:
console.log(users.length, posts.length); // 3 10
- Settle all, ignore failures
-
Waits for every promise whether it succeeds or fails, then you decide what to do. Ideal for batch jobs where some failures are fine.
const results = await Promise.allSettled(tasks);
const ok = results.filter((r) => r.status === 'fulfilled');
Example output:
[
{ status: 'fulfilled', value: 'a' },
{ status: 'rejected', reason: Error: timeout }
]
console.log(ok.length); // 1
- Deferred promise (ES2024)
-
Creates a promise plus its resolve and reject functions, so you can settle it from outside, like in an event handler or callback API.
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('done'), 1000);
Example output:
console.log(await promise); // done (after 1 second)
- Abort after timeout
-
Cancels the request if it takes longer than 5 seconds. Without it, a hung server can leave your UI spinning forever.
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
Example output:
TimeoutError: The operation was aborted due to timeout
Objects & Utilities
- Deep clone
-
Makes a true deep copy, including nested objects, Dates, Maps and Sets. Replaces the old JSON.parse(JSON.stringify()) trick. Functions cannot be cloned.
const copy = structuredClone(original);
Example output:
copy.nested.count = 99;
console.log(original.nested.count); // 1 (unchanged)
- Object to entries and back
-
Turns an object into [key, value] pairs, transforms them with array methods, and rebuilds an object. A "map" for objects.
const doubled = Object.fromEntries(
Object.entries(prices).map(([k, v]) => [k, v * 2]),
);
Example output:
// prices = { coffee: 120, tea: 90 }
console.log(doubled); // { coffee: 240, tea: 180 }
- Own property check
-
Checks whether the object itself has the key, ignoring inherited ones. The modern, safer replacement for hasOwnProperty.
Object.hasOwn(user, 'email');
Example output:
true
- Random UUID
-
Generates a random v4 UUID with no library. Available in modern browsers (secure contexts) and Node.
const id = crypto.randomUUID();
Example output:
console.log(id); // 3b241101-e2bb-4255-8caf-4136c566a962
- Format currency
-
Formats numbers as money for a locale, with the right symbol, separators and decimals. No manual string building.
new Intl.NumberFormat('en-PH', { style: 'currency', currency: 'PHP' }).format(1500);
Example output:
'₱1,500.00'
Modules & Classes
- Named and default exports
-
A module can have many named exports and one default export. Import them with import main, { formatDate } from './utils.js'.
export const formatDate = (d) => d.toISOString();
export default function main() {}
- Dynamic import
-
Loads a module only when this line runs. Bundlers like Vite split it into its own file, keeping the first page load small.
const { Chart } = await import('./chart.js');
Example output:
Network tab: GET /build/assets/chart-B4x9Qk2a.js (loaded on demand)
- Private fields and methods
-
Fields starting with # are truly private: code outside the class cannot read or change them, not even by accident.
class Counter {
#count = 0;
increment() { return ++this.#count; }
}
Example output:
const c = new Counter();
c.increment(); // 1
c.increment(); // 2
c.#count; // SyntaxError: Private field '#count' must be declared in an enclosing class
- Static class block
-
A static block runs once when the class is defined, for setup that needs more than a one-line assignment.
class Config {
static defaults;
static { Config.defaults = { debug: false }; }
}
Example output:
console.log(Config.defaults); // { debug: false }
Vue 3 + Inertia v2
Script setup essentials, Inertia forms, navigation, and v2 features like deferred props and polling.
Vue 3 is a reactive UI framework and Inertia v2 glues it to a Laravel backend so you build a single-page app without writing an API. This sheet is for Laravel developers building Vue pages with <script setup>.
Try it yourself: In a Laravel starter kit, add a counter with ref and computed to a page, then turn a plain link into a prefetching <Link> and watch the Network tab as you hover it.
Reactivity
- Ref for a value
-
ref wraps a value so Vue can track changes and re-render. Use .value in script; templates unwrap it automatically.
import { ref } from 'vue';
const count = ref(0);
count.value++;
Example output:
console.log(count.value); // 1
<p>{{ count }}</p> renders <p>1</p>
- Computed value
-
Derives a value from other reactive state and caches it until those dependencies change. Use it instead of recalculating in the template.
const total = computed(() => items.value.reduce((sum, i) => sum + i.price, 0));
Example output:
console.log(total.value); // 42.5
items.value.push({ price: 10 }); // total.value -> 52.5
- Watch a source
-
Runs a callback when a specific ref changes, with the new and old values. Good for side effects like fetching search results.
watch(search, (value, oldValue) => {
console.log(value, oldValue);
});
Example output:
lar la
lara lar
- Run effect on dependencies
-
Runs immediately and again whenever any reactive value it reads changes. No need to list dependencies by hand.
watchEffect(() => (document.title = `${count.value} items`));
Example output:
Browser tab title: "3 items"
- Template ref
-
Vue 3.5's useTemplateRef grabs the element marked ref="search" in the template. Here it focuses the input once the component mounts.
const input = useTemplateRef('search');
onMounted(() => input.value.focus());
Example output:
The <input ref="search"> is focused with the cursor blinking as soon as the page loads.
Components
- Props with defaults
-
Declares the data a parent passes in, with types, required flags and defaults. Vue warns in dev when a prop is missing or the wrong type.
const props = defineProps({
title: { type: String, required: true },
size: { type: String, default: 'md' },
});
Example output:
<Card title="Hi" /> -> props.size === 'md'
<Card /> -> [Vue warn]: Missing required prop: "title"
- Emit events
-
Declares events this component sends up to its parent. The parent listens with @save="handleSave" and receives form as the argument.
const emit = defineEmits(['save', 'cancel']);
emit('save', form);
Example output:
<Editor @save="(form) => console.log(form)" /> logs { title: "Draft" }
- Two-way binding with defineModel
-
Makes the component work with v-model. Setting model.value inside the child updates the parent's variable automatically.
const model = defineModel({ type: String, default: '' });
Example output:
<SearchBox v-model="query" /> typing "vue" in the child sets query to "vue" in the parent
- Slot with fallback
-
A named slot lets the parent inject its own markup. If the parent passes nothing for #footer, the default Close button shows.
<slot name="footer">
<button>Close</button>
</slot>
Example output:
<Modal /> renders <button>Close</button>
<Modal><template #footer><button>Save</button></template></Modal> renders <button>Save</button>
- List rendering
-
Renders one element per item. A stable, unique :key helps Vue update the right rows when the list changes.
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
Example output:
<li>Hello Inertia</li>
<li>Tailwind v4 tips</li>
Inertia Navigation
- Link to a named route
-
Inertia's Link renders a normal anchor but swaps pages without a full reload. Ziggy's route() builds the URL from your Laravel route name.
<Link :href="route('posts.show', post.slug)">Read more</Link>
Example output:
<a href="/posts/hello-inertia">Read more</a> (click loads the next page via XHR, no full refresh)
- Prefetch on hover
-
Inertia v2 loads the page data in the background when you hover the link, so the click feels instant. The result is cached briefly.
<Link :href="route('blog.index')" prefetch>Blog</Link>
Example output:
Hover: GET /blog (X-Inertia: true) fires in the background. Click: page appears with no request.
- Programmatic visit
-
Navigates from code, for example from a search box. preserveState keeps local component state, and replace avoids piling up history entries.
router.get(route('posts.index'), { search: query.value }, {
preserveState: true,
replace: true,
});
Example output:
URL becomes /posts?search=vue and the post list updates; the input keeps focus and its text.
- Reload only some props
-
Re-requests the current page but asks the server for just the notifications prop. Saves work when only one piece of data is stale.
router.reload({ only: ['notifications'] });
Example output:
Request header X-Inertia-Partial-Data: notifications (other props are not re-sent)
- Set page title
-
Sets the document title (and other head tags) for this page. Your createInertiaApp title callback can append the app name.
<Head title="Dashboard" />
Example output:
<title>Dashboard - My App</title>
Inertia Forms
- useForm helper
-
Creates a reactive form object that tracks field values, validation errors, processing state and whether anything changed.
const form = useForm({ title: '', body: '' });
Example output:
form.title // ''
form.processing // false
form.isDirty // true after typing
- Submit and reset
-
Sends the form data as a POST request. On success the fields clear; on validation failure form.errors fills automatically.
form.post(route('posts.store'), {
onSuccess: () => form.reset(),
});
Example output:
POST /posts -> 302 redirect -> form fields cleared, new post appears in the list
- Show validation error
-
Displays Laravel's validation message for a field. The errors come straight from your Form Request rules.
<p v-if="form.errors.title">{{ form.errors.title }}</p>
Example output:
<p>The title field is required.</p>
- Disable while processing
-
Disables the button while the request is in flight, preventing double submits from impatient clicks.
<button :disabled="form.processing">Save</button>
Example output:
<button disabled>Save</button> (while the request is pending)
- Form component
-
Inertia v2's Form component submits a plain HTML form through Inertia, reading inputs by name. Less setup than useForm for simple forms.
<Form :action="route('posts.store')" method="post" #default="{ errors, processing }">
<input name="title" />
<button :disabled="processing">Save</button>
</Form>
Example output:
POST /posts { title: "My first post" }
errors.title // "The title field is required." when empty
Inertia v2 Features
- Deferred prop (server)
-
Sends the page without stats first, then Inertia fetches stats in a follow-up request. The page appears fast even if the query is slow.
'stats' => Inertia::defer(fn () => Stats::build()),
Example output:
1st response: props without stats
2nd request: X-Inertia-Partial-Data: stats -> { stats: {...} }
- Deferred prop with skeleton
-
Shows the fallback until the deferred stats prop arrives, then renders the real content. A pulsing skeleton feels faster than a spinner.
<Deferred data="stats">
<template #fallback>
<div class="h-24 animate-pulse rounded bg-gray-200" />
</template>
<StatsGrid :stats="stats" />
</Deferred>
Example output:
A gray pulsing box appears first, then swaps to the stats grid about half a second later.
- Poll every 5 seconds
-
Reloads the jobs prop every 5 seconds while the component is mounted, and stops automatically when you leave the page.
usePoll(5000, { only: ['jobs'] });
Example output:
Network tab: GET /jobs (X-Inertia-Partial-Data: jobs) every 5s
- Load when visible
-
Fetches the comments prop only when this block scrolls into view. Pair it with Inertia::optional() on the server so it is skipped initially.
<WhenVisible data="comments">
<template #fallback>Loading...</template>
<CommentList :comments="comments" />
</WhenVisible>
Example output:
"Loading..." shows until you scroll down, then the comment list appears.
- Merge prop for infinite scroll
-
On partial reloads, new posts are appended to the existing array instead of replacing it. The building block for "load more" and infinite scroll.
'posts' => Inertia::merge(fn () => Post::query()->paginate(10)->items()),
Example output:
page 1: posts.length === 10
router.reload({ only: ['posts'], data: { page: 2 } }) -> posts.length === 20
Tailwind CSS v4
Tailwind v4 utilities, responsive and dark mode variants, and CSS-first configuration with @theme.
Tailwind is a utility-first CSS framework: you style elements by combining small classes right in your markup. Version 4 moves configuration into CSS with @theme, and this sheet is for anyone building UIs with it.
Try it yourself: Open play.tailwindcss.com, build a small card using the layout, typography and dark mode snippets, then resize the preview to watch the responsive classes kick in.
Setup & Theme
- Import Tailwind
-
The single line that loads Tailwind v4 in your main CSS file. It replaces the three @tailwind directives from v3.
@import "tailwindcss";
Example output:
Generates the base reset, theme variables and only the utility classes your templates actually use.
- Custom theme tokens
-
Adds design tokens as CSS variables. Tailwind creates matching utilities from the names, no tailwind.config.js needed.
@theme {
--color-brand-500: oklch(0.62 0.19 264);
--font-display: "Inter", sans-serif;
}
Example output:
New utilities: bg-brand-500, text-brand-500, border-brand-500, font-display
:root { --color-brand-500: oklch(0.62 0.19 264); }
- Scan extra sources
-
Tells Tailwind to also scan a folder it would skip, such as a package in vendor or node_modules, so classes used there get generated.
@source "../../vendor/some/package";
- Custom utility
-
Registers your own utility class that works with variants like hover: and md:, just like a built-in one.
@utility content-auto {
content-visibility: auto;
}
Example output:
<div class="content-auto md:content-auto"> -> content-visibility: auto;
- Class-based dark mode
-
Makes dark: utilities follow a .dark class on <html> instead of the OS setting, so users can toggle a theme switch.
@custom-variant dark (&:where(.dark, .dark *));
Example output:
<html class="dark"> -> every dark:bg-gray-900 and dark:text-gray-100 class activates
Layout & Spacing
- Centered container
-
Centers content, caps its width and adds side padding that grows on larger screens. The go-to page wrapper.
mx-auto max-w-7xl px-4 sm:px-6 lg:px-8
Example output:
Content max 80rem (1280px) wide, centered; side padding 16px on mobile, 24px from sm, 32px from lg.
- Flex row with gap
-
Lays children in a row, vertically centered, pushed to opposite ends with 1rem of space between. Perfect for navbars and card headers.
flex items-center justify-between gap-4
Example output:
[Logo] ........................ [Menu] (vertically aligned, at least 16px apart)
- Responsive grid
-
One column on phones, two from the md breakpoint and three from lg, with 1.5rem gaps. A card grid in one line.
grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3
Example output:
Mobile: [A]
[B]
Desktop: [A] [B] [C]
- Full-screen centered
-
Makes the element at least as tall as the viewport and centers its child both ways. Ideal for login pages and empty states.
grid min-h-screen place-items-center
Example output:
The child sits dead center of the screen, horizontally and vertically.
- Size shorthand
-
size-* sets width and height together. With rounded-full it makes a perfect circle, great for avatars and icon buttons.
size-10 rounded-full
Example output:
A 40px x 40px circle (width: 2.5rem; height: 2.5rem; border-radius: calc(infinity * 1px)).
Typography & Color
- Heading
-
Large, bold, slightly tightened text in near-black. Tight tracking makes big headings look more polished.
text-3xl font-bold tracking-tight text-gray-900
Example output:
font-size: 1.875rem (30px); font-weight: 700; letter-spacing: -0.025em; dark gray color.
- Gradient text
-
Paints a left-to-right gradient behind the text, clips it to the letters and hides the text color. bg-linear-to-r is the v4 name for bg-gradient-to-r.
bg-linear-to-r from-indigo-500 to-purple-600 bg-clip-text text-transparent
Example output:
The text fades from indigo on the left to purple on the right.
- Opacity modifier
-
The /50 suffix sets color opacity inline, here a 50% black background with 80% white text. Great for overlays on images.
bg-black/50 text-white/80
Example output:
Semi-transparent dark overlay; slightly soft white text on top.
- Truncate or clamp
-
truncate keeps text on one line with an ellipsis; line-clamp-3 allows up to three lines before cutting off. Use them on titles and excerpts.
truncate
line-clamp-3
Example output:
truncate: A very long blog post title that keeps goi…
line-clamp-3: shows 3 lines, then ends with …
- Use a theme variable
-
v4 shorthand for using any CSS variable as a value, replacing bg-[var(--color-brand-500)]. Handy for runtime-themed colors.
bg-(--color-brand-500)
Example output:
background-color: var(--color-brand-500);
Responsive & States
- Mobile-first breakpoints
-
Unprefixed classes apply everywhere; md: and lg: override from those widths up. Design for phones first, then add larger screens.
text-sm md:text-base lg:text-lg
Example output:
14px on phones, 16px from 48rem (768px), 18px from 64rem (1024px).
- Hover and focus
-
Darkens on hover and shows an outline only for keyboard focus, not mouse clicks. Good looks and good accessibility.
hover:bg-indigo-600 focus-visible:outline-2 focus-visible:outline-indigo-500
Example output:
Mouse hover: darker background. Tab key: 2px indigo outline appears.
- Parent hover with group
-
Mark the parent with group, then style children with group-hover:. Here an Edit label appears when you hover anywhere on the card.
<div class="group">
<span class="opacity-0 group-hover:opacity-100">Edit</span>
</div>
Example output:
Edit is invisible until the pointer enters the card, then it fades in.
- Container queries
-
Responds to the parent's width instead of the screen's, so a component adapts wherever you drop it. Built into v4, no plugin needed.
<div class="@container">
<div class="flex flex-col @md:flex-row">...</div>
</div>
Example output:
Stacked vertically in a narrow sidebar; side by side once the container is 28rem (448px) wide.
- Disabled state
-
Styles a button or input only while it has the disabled attribute, so users can see it is not clickable.
disabled:cursor-not-allowed disabled:opacity-50
Example output:
Disabled button fades to 50% and shows the not-allowed cursor on hover.
Dark Mode & Effects
- Dark variant
-
Light colors by default, swapped for dark ones when dark mode is active (OS setting by default, or a .dark class if configured).
bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100
Example output:
Light mode: white card, dark text. Dark mode: near-black card, light text.
- Card shadow and ring
-
A soft shadow plus a barely visible 1px ring gives cards crisp edges without a heavy border.
rounded-xl bg-white shadow-sm ring-1 ring-gray-900/5
Example output:
A white card with rounded corners, a subtle drop shadow and a faint outline.
- Smooth transition
-
Animates property changes over 200ms. On hover the element lifts 4px, a small touch that makes cards feel interactive.
transition duration-200 ease-out hover:-translate-y-1
Example output:
Hover: the card glides up 0.25rem (4px) in 200ms, and settles back on mouse out.
- Glass effect
-
A mostly transparent background that blurs whatever is behind it, the frosted glass look for navbars over images.
bg-white/10 backdrop-blur-md
Example output:
backdrop-filter: blur(12px); content behind the panel looks frosted.
- Loading skeleton
-
A gray bar that gently pulses while content loads. Stack a few with different widths to mimic lines of text.
h-4 w-32 animate-pulse rounded bg-gray-200 dark:bg-gray-700
Example output:
A 16px x 128px rounded gray bar fading in and out every 2 seconds.
Prompt Cheat Codes
Reusable AI prompt patterns for coding: context, constraints, reviews, tests, and debugging.
AI coding assistants give far better answers when you tell them who to be, what you are working with and what a good result looks like. This sheet is for developers who want copy-paste prompt patterns that turn vague replies into useful code.
Try it yourself: Pick a small function from your project and ask an AI assistant about it twice: once with a bare question, once with the role, context and constraints prompts combined, then compare the answers.
Framing the Request
- Assign a role
-
Setting a role nudges the AI toward the vocabulary, standards and priorities of that expert. Put it at the very start of your prompt.
You are a senior Laravel 12 developer who values readable, tested code.
Example output:
Sure. I will keep controllers thin, validate with a Form Request, and include a Pest test for the new endpoint.
- Give project context
-
Listing your stack and versions stops the AI from suggesting outdated APIs, like Options API Vue or Tailwind v3 config files.
Context: Laravel 12, PHP 8.3, Inertia v2 + Vue 3 <script setup>,
Tailwind v4, Pest 4 for tests.
Example output:
Got it. I will use defineProps and useForm from @inertiajs/vue3, and style with Tailwind v4 utilities (no tailwind.config.js).
- State the goal, not just the task
-
Explaining why helps the AI make sensible choices you did not spell out, like streaming large exports or scoping to the current user.
Goal: let users export their invoices as CSV.
Task: write the controller action and route.
Example output:
Since invoices can be large, I will stream the CSV with response()->streamDownload()
and only include invoices belonging to the authenticated user.
- Specify the output format
-
Telling the AI exactly how to shape its reply saves you scrolling through essays to find the code.
Return only the code in one block, then a 3-bullet summary of decisions.
Example output:
```php
// InvoiceExportController.php ...
```
- Streams rows to keep memory low
- Scoped to the logged-in user
- Route named invoices.export
Constraints & Examples
- Set hard constraints
-
Explicit rules keep the AI inside your project's conventions instead of reaching for a random package or a 100-line method.
Constraints:
- No new dependencies
- Use Form Requests for validation
- Keep methods under 20 lines
Example output:
Understood. I will use only built-in Laravel features, move validation into StoreInvoiceRequest, and split the export into two small methods.
- Show an example to follow
-
An example beats a description. Pasting a sibling file makes the AI copy your naming, structure and formatting.
Follow the style of this existing class:
<paste sibling file>
Example output:
I matched your existing pattern: invokable controller, constructor property promotion, and a PHPDoc array shape on the return type.
- Few-shot input/output
-
A few input and output pairs teach the AI the exact pattern and format you want, with no long explanation needed.
Input: "2 hrs" -> Output: 120
Input: "45 min" -> Output: 45
Now convert: "1 hr 30 min"
Example output:
Output: 90
- Say what NOT to do
-
Negative instructions protect code other parts of the app depend on. Especially useful when asking for a refactor.
Don't change public method signatures or rename existing files.
Example output:
I kept calculateTotal(Order $order): int unchanged and only extracted private helpers inside the same class.
Thinking Step by Step
- Plan before code
-
Reviewing a short plan is much cheaper than reviewing a wrong implementation. You catch misunderstandings before any code is written.
Before writing code, outline your approach in numbered steps and wait for my OK.
Example output:
1. Add a GET /invoices/export route
2. Create an invokable InvoiceExportController
3. Stream the CSV with streamDownload()
4. Add a Pest test for auth and headers
Shall I proceed?
- Ask clarifying questions
-
Gives the AI permission to ask instead of guessing. Capping the number keeps it from interrogating you.
If anything is ambiguous, ask me up to 3 questions before answering.
Example output:
1. Should the export include cancelled invoices?
2. Which date format do you want in the CSV?
3. Is there a maximum number of rows?
- Compare options
-
Forces the AI to think beyond its first idea and shows you the trade-offs, so you make the final call with context.
Give me 2-3 approaches with trade-offs, then recommend one and explain why.
Example output:
A) Queue a job and email the file: scales, but slower feedback.
B) Stream directly: instant, fine up to ~100k rows.
Recommendation: B for now, since your invoice counts are small.
- Explain like a teammate
-
Turns the AI into a patient tutor for unfamiliar code. Great for legacy files or a library you have never used.
Explain this code line by line as if onboarding a new developer.
Example output:
Line 1: the class extends FormRequest, so Laravel validates before the controller runs.
Line 5: authorize() returns true, meaning any logged-in user may submit.
Review, Test, Debug
- Review my code
-
A focused checklist and a severity order give you an actionable review instead of vague praise.
Review this code for bugs, security issues, and N+1 queries.
List issues by severity with a suggested fix for each.
Example output:
High: invoices are not scoped to the user, so anyone can export all invoices. Fix: use $request->user()->invoices().
Medium: N+1 on customer inside the loop. Fix: ->with('customer').
- Write tests
-
Naming the scenarios you care about gets you meaningful tests instead of one shallow "it returns 200" check.
Write Pest feature tests for this controller.
Cover the happy path, validation errors, and authorization.
Example output:
it('exports invoices as csv for the owner')
it('rejects guests with a redirect to login')
it('returns 403 for another user's invoice')
- Find edge cases
-
Giving categories prompts the AI to hunt systematically, surfacing bugs you would only find in production.
List edge cases this function does not handle yet (empty, null, huge, unicode, concurrency).
Example output:
- Empty array returns 0 but should throw
- Names with emoji break the CSV encoding
- Two requests at once can create duplicate invoice numbers
- Debug an error
-
Sharing the full error plus code, and asking for a ranked cause with a way to verify it, turns guessing into a quick diagnosis.
Here is the error, stack trace, and relevant code.
Give the most likely cause first, then how to confirm it.
Example output:
Most likely: $invoice->customer is null for invoices whose customer was deleted.
Confirm: run Invoice::query()->doesntHave('customer')->count() in tinker.
- Refactor safely
-
A diff with explanations makes it easy to review exactly what moved, so a "cleanup" does not sneak in behavior changes.
Refactor for readability without changing behavior. Show a diff and explain each change.
Example output:
- if ($user->role === 'admin' || $user->role === 'owner') {
+ if ($user->canManageBilling()) {
Extracted the role check into a named method on User.
Iterating on Answers
- Tighten the result
-
AI answers tend to over-engineer. This trims extra abstractions, options and comments you never asked for.
Make it shorter and simpler. Remove anything not required by the task.
Example output:
Removed the custom exporter interface and config options; the controller is now 14 lines.
- Check versions
-
Asks the AI to verify against your versions, catching deprecated or invented methods before you paste them in.
Double-check that every API you used exists in Laravel 12 and Vue 3.5.
Example output:
Correction: I used a v3-style tailwind.config.js theme. In Tailwind v4 this belongs in an @theme block in app.css instead.
- Self-critique
-
A second pass in reviewer mode often catches the AI's own mistakes, missing tests or unhandled errors.
Review your previous answer as a strict senior reviewer. What would you change?
Example output:
1. The export has no test for an empty invoice list.
2. The filename should include the date for easier sorting.
- Summarize for handoff
-
Turns a long chat into a clean PR description or commit message your teammates can skim in seconds.
Summarize what we changed and why, as a short PR description.
Example output:
Add CSV export for invoices
- New GET /invoices/export route, scoped to the current user
- Streams rows to keep memory low
- Pest tests for owner, guest and forbidden cases