- 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
Five Eloquent Mistakes That Quietly Slow Down Your Laravel App
About Post
Eloquent's greatest strength is that database code reads like English. $tenant->contracts->count(). Lovely. Obvious. Done.
That's also its trap. The slowest patterns look exactly as innocent as the fast ones. They work perfectly in development with 30 rows, pass code review, and then get a little slower every month in production until someone opens a ticket that just says "the dashboard is slow".
Here are five mistakes I've seen in many codebases (and, early on, written myself), each with why it hurts and the fix.
Mistake 1: all(), then filter in PHP
$active = Tenant::all()->where('status', 'active');
Why it hurts: all() runs SELECT * FROM tenants and builds a model object for every single row. The where() after it isn't SQL at all. It's a Collection method filtering in PHP memory. The database did the most work possible, then PHP threw most of it away.
The confusing part is that Collections and the query builder share method names like where, sortBy and first, so the code looks right.
The fix: filter before you fetch.
$active = Tenant::where('status', 'active')->get();
A simple test: if get() or all() appears before your filtering, the filtering is happening in PHP.
Mistake 2: counting by loading everything
$open = Ticket::where('status', 'open')->get()->count();
if ($tenant->contracts->count() > 0) { /* ... */ }
Why it hurts: the first line loads every open ticket into memory just to count them. The second is sneakier: $tenant->contracts (no brackets) loads the whole relation, all columns, all rows, to answer a yes/no question.
The fix: ask the database the question you actually have.
$open = Ticket::where('status', 'open')->count(); // SELECT COUNT(*)
if ($tenant->contracts()->exists()) { /* ... */ } // SELECT EXISTS(...)
$tenants = Tenant::withCount('contracts')->paginate(25); // count per row, one query
Note the brackets: contracts() returns a query you can keep building on, while contracts returns the loaded results. One pair of brackets is the difference between a count and a full load. And exists() beats count() > 0 because the database can stop at the first match.
Mistake 3: selecting every column, every time
$contracts = Contract::with('tenant')->latest()->paginate(50);
Why it hurts: this fetches every column of every contract and every tenant. If those tables have a long terms text column, a JSON metadata blob, or a stored document payload, you're moving all of it across the network and hydrating it into models, only to display a number, a name and a date.
The fix: select what the screen needs.
$contracts = Contract::query()
->select(['id', 'tenant_id', 'number', 'end_date'])
->with('tenant:id,name')
->latest()
->paginate(50);
The gotcha: always include the keys the relationship needs. Leave out tenant_id in the parent, or id in the tenant:id,name list, and Eloquent can't match them up. Every tenant silently comes back as null, with no error. Also note that latest() orders by created_at, which doesn't need to be selected to sort by it.
For list screens and API index endpoints, this one change is often bigger than people expect. For a single-record detail page, don't bother; the gain is too small to justify the extra code.
Mistake 4: loading relationships inside a loop
$contracts = Contract::where('status', 'active')->get();
foreach ($contracts as $contract) {
echo $contract->tenant->name; // one query per contract
echo $contract->unit->code; // and another one
}
Why it hurts: this is the famous N+1 problem. One query for the contracts, then one more per contract for each relationship you touch. Two relationships and a few hundred rows means hundreds of queries for one page, each one fast on its own and painfully slow together. It hides especially well in Blade views and API resources, far from the controller that ran the query.
The fix: eager load, and make Laravel shout when you forget.
$contracts = Contract::with(['tenant', 'unit'])->where('status', 'active')->get();
// AppServiceProvider::boot()
Model::preventLazyLoading(! app()->isProduction());
With preventLazyLoading, any accidental lazy load throws an exception in development and tests, so the N+1 is caught on your machine instead of in production. It's off in production on purpose: there, a slow page is better than a broken one.
Mistake 5: processing big tables in one go
// Nightly job: mark expired contracts
Contract::where('end_date', '<', now())->where('status', 'active')
->get()
->each(fn ($c) => $c->markExpired());
Why it hurts: fine on day one, an out-of-memory error on day four hundred. get() loads every matching row at once, and as the table grows, so does the memory needed to run your job.
The fix: work in batches.
Contract::where('end_date', '<', now())->where('status', 'active')
->chunkById(500, function ($contracts) {
$contracts->each(fn ($c) => $c->markExpired());
});
The gotcha: use chunkById(), not plain chunk(), when you update the rows you're filtering on. Plain chunk() uses LIMIT and OFFSET. As you mark rows expired, they drop out of the result set, the offset skips ahead, and roughly half your rows never get processed. chunkById() pages by "id greater than the last one seen", which doesn't care that the set is shrinking. If you prefer a simple loop, lazyById() gives you the same safety one model at a time.
And if the update is the same for every row, skip models entirely: Contract::where(...)->update(['status' => 'expired']) is a single query. Just remember that bulk updates don't fire model events or observers.
The habit behind all five: before you call get(), ask "what does the database need to return for this to work?" Then make the query return exactly that, and nothing more.
Cheat sheet
| Instead of | Use |
|---|---|
Model::all()->where(...) | Model::where(...)->get() |
->get()->count() | ->count() or withCount() |
->count() > 0 | ->exists() |
SELECT * on list screens | select([...]) + with('rel:id,name') |
| Relations in a loop | with() + preventLazyLoading() |
->get() on big jobs | chunkById(), lazyById() or a bulk update() |
To find these in your own app, install Laravel Debugbar or Telescope locally and look at the query count on your busiest pages. The numbers usually tell the story on their own.
Which of these five have you found in a real codebase? And is there a sixth you'd add to the list?

Be first to comment it...