- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Laravel Validation Rules You're Probably Not Using (But Should Be)
About Post
Most Laravel validation I review looks the same: required, string, max:255, email, and then a pile of manual checks in the controller for everything else. "If the type is periodic, make sure there's no end date." "Check that this unit actually belongs to this property." "Make sure the email is unique, except for this user."
Laravel already has rules for almost all of that. They're in the docs, in a long alphabetical list that nobody reads from top to bottom. So here are the ones I reach for most, each with the problem it solves, all for Laravel 12.
Unique, except for me
The problem: on an "edit profile" form, unique:users,email fails because the user's own email is already in the table.
use Illuminate\Validation\Rule;
'email' => [
'required',
'email',
Rule::unique('users')->ignore($this->user()),
],
ignore() accepts the model or its id. Two things worth knowing: never pass a value straight from the request into ignore() (the docs warn about this; use the model you loaded), and if the model uses soft deletes, add ->whereNull('deleted_at') so a deleted user's email doesn't block a new account forever.
Exists, but only where it should
The problem: exists:units,id checks that the unit exists. It doesn't check that it belongs to the property the user is working on. That's not just a data bug, it's an authorization hole: send any id, attach any unit.
'unit_id' => [
'required',
Rule::exists('units', 'id')
->where('property_id', $this->route('property')->id)
->whereNull('archived_at'),
],
Scoping exists to the current parent (or the current team or tenant in a multi-tenant app) is one of the cheapest security wins in Laravel. Policies still matter, but this stops a whole class of "I changed the id in the request" tricks at the door.
sometimes: validate it only if it's sent
The problem: a PATCH endpoint where the client sends only the fields it wants to change. required would demand everything; dropping required would let someone send an empty name.
'name' => ['sometimes', 'required', 'string', 'max:120'],
'phone' => ['sometimes', 'nullable', 'string', 'max:20'],
Read it as "if name is present, it must be filled in". Compare that with nullable, which means "present, but may be empty". Mixing up the two is behind a lot of "why did my API accept that?" moments.
bail: stop at the first failure
The problem: a field fails the cheap email check, and Laravel still runs the unique rule, which queries the database with a value you already know is garbage. The user also gets three error messages about one field.
'email' => ['bail', 'required', 'email', Rule::unique('users')],
bail works per field. If you want the whole request to stop at the first failing field, set protected $stopOnFirstFailure = true; on the form request.
Conditional rules: required_if, prohibited_if, exclude_if
The problem: rules that depend on another field. A fixed-term contract needs an end date. A periodic one must not have one. This logic usually ends up as if statements in the controller.
'type' => ['required', Rule::in(['fixed', 'periodic'])],
'end_date' => [
'nullable',
'required_if:type,fixed',
'prohibited_if:type,periodic',
'date',
'after:start_date',
],
prohibited_if is the underrated one. It turns "the client sent something that makes no sense" into a clear 422 error, instead of storing contradictory data. When you'd rather silently drop the field, use exclude_if:type,periodic: it's removed from validated() entirely. There are closure versions too, like Rule::requiredIf(fn () => $this->user()->isAdmin()), for conditions that aren't just "field equals value".
The Password rule
The problem: password rules copied between forms, slightly different each time.
use Illuminate\Validation\Rules\Password;
// In AppServiceProvider::boot()
Password::defaults(fn () => Password::min(10)
->letters()
->mixedCase()
->numbers()
->uncompromised());
// In any form request
'password' => ['required', 'confirmed', Password::defaults()],
Define the policy once, use it everywhere. uncompromised() checks the password against the Have I Been Pwned breach database using k-anonymity, so only the first five characters of the password's SHA-1 hash leave your server, never the password itself.
The enum rule
The problem: a status field validated with in:draft,active,ended, a list that drifts out of sync with the enum in your code.
'status' => [
'required',
Rule::enum(ContractStatus::class)
->only([ContractStatus::Draft, ContractStatus::Active]),
],
The enum is the single source of truth. only() and except() let you restrict the choice for a specific form: a user can create a draft or active contract, but "ended" only happens through the end-contract flow.
Three small ones that punch above their weight
'items.*.amount' => ['required', 'decimal:0,2']: wildcard validation for array rows, and a money-friendly rule that limits decimal places.'emails.*' => ['distinct:ignore_case']: no duplicates inside the same submitted array.'meta' => ['array:source,campaign']: the array may only contain those keys, so nobody sneaks extra data into a JSON column.
A good rule of thumb: if your controller has an if that checks the shape or relationship of incoming data and returns an error, it probably belongs in the form request as a validation rule. Controllers should receive data that's already known to be valid.
The quick list
Rule::unique()->ignore()for edit forms.Rule::exists()->where()to keep ids inside the right parent.sometimesfor partial updates.bailto stop wasted checks.required_if,prohibited_if,exclude_iffor conditional fields.Password::defaults()andRule::enum()for one source of truth.
The full list is in the Laravel validation docs, and it's worth one slow read. Which validation rule did you discover embarrassingly late? Mine was prohibited_if.

Be first to comment it...