Tips(21)
Short notes from real Laravel and PHP work: one idea, one snippet.
RSS feedNew
- Laravel
- rate-limiting
- security
- http
Count only the responses you choose in a rate limit
Limit::after() decides after the response which requests count: only 404s for someone guessing ids, and never 422s, so a user fixing a form does not spend their own limit.- Published on
- Laravel
- cache
- concurrency
Lock the rebuild, not the read
Wrap the expensive rebuild in Cache::lock() so one request fills the key while the others wait a moment and read the result.- Published on
- Laravel
- enums
- php
One details() match instead of label(), color() and hint()
Put everything the UI needs about a case in one array per case, and read label() and meta() from it; adding a case means one new arm, and match still throws when it is missing.- Published on
- Laravel
- enums
- php
Give every enum the same ten lines
A small trait adds values(), names() and options() to every backed enum, so whereIn clauses, validation lists and select inputs read the cases instead of a copied array.- Published on
- Laravel
- database
- enums
- migrations
A plain string column, not a MySQL enum
Adding a case to a MySQL ENUM column is an ALTER TABLE that can lock a big table for minutes. A string column with an index stores the same values, and the PHP enum does the checking.- Published on
- Laravel
- rate-limiting
- http
Prefix the by() keys when one limiter returns several limits
Three limits with the same by() key share one counter: the hour limit fills up at the minute's pace. A prefix per window keeps the counters apart.- Published on
- Laravel
- enums
- architecture
Rules about a status live in the status
canBeCancelled() and isFinal() as methods on the enum turn a three-way string compare copied in four places into one method in one place.- Published on
- PHP
- enums
- validation
tryFrom() at the edges, from() inside
OrderStatus::from('xyz') throws a ValueError; tryFrom('xyz') returns null. Use tryFrom for query strings, webhooks and imports, and from where a wrong value is a bug.- Published on
- Laravel
- eloquent
- concurrency
- database
lockForUpdate() only works inside a transaction
A SELECT … FOR UPDATE outside a transaction releases its lock at once, so the check-then-write gap stays open. Wrap the read and the write in DB::transaction().- Published on
- Laravel
- cache
- performance
Serve stale data with Cache::flexible()
flexible() returns a fresh value while it is young, serves the old one and rebuilds in the background during a grace window, and only blocks once that window has passed.- Published on
- Laravel
- dates
Make Carbon immutable by default
One line in a service provider makes now() and every date attribute return CarbonImmutable, so addDay() stops changing the date you passed in.- Published on
- Laravel
- eloquent
- database
Use sole() when exactly one row must match
first() hides a duplicate and firstOrFail() hides nothing more; sole() throws when there is no row and also when there is more than one.- Published on
- Laravel
- eloquent
- performance
- database
Use chunkById() when the loop changes the rows it reads
chunk() pages with OFFSET, so updating a row that leaves the WHERE clause shifts the next page and skips rows. chunkById() pages by the primary key and does not.- Published on
- Laravel
- eloquent
- performance
Turn lazy loading into an exception in development
Model::preventLazyLoading() makes every accidental N+1 throw while you develop, so missing with() calls are found before they reach production.- Published on
- Laravel
- queue
- concurrency
Keep one copy of a job in the queue with ShouldBeUnique
A job that implements ShouldBeUnique is not dispatched again while an identical one is waiting or running, so a burst of events produces one run instead of fifty.- Published on
- Laravel
- queue
- concurrency
Run jobs for the same record one at a time
The WithoutOverlapping middleware lets two workers process different orders in parallel but never the same order twice at once.- Published on
- Laravel
- validation
- enums
Validate and cast a status with one backed enum
Rule::enum() accepts only the enum's values, and an enum cast on the model gives you the case everywhere else, so the list of valid statuses lives in one place.- Published on
- Laravel
- database
- concurrency
Let DB::transaction() retry a deadlock
The second argument of DB::transaction() is the number of attempts: a deadlock is retried that many times before the exception reaches you.- Published on
- Laravel
- queue
- performance
Run small work after the response without a queue worker
dispatchAfterResponse() sends the HTTP response first and runs the job in the same process afterwards, which is enough for an email or a log line.- Published on
- PHP
- syntax
Reach for match() instead of switch
match compares strictly, returns a value and throws on an unhandled case, which removes the three usual switch bugs in one move.- Published on
- PHP
- syntax
- collections
Pass methods with first-class callable syntax
strtoupper(...) and $this->toDto(...) create a Closure from a function or method, so callbacks stop being strings or arrow-function wrappers.- Published on