5 Laravel Eloquent Tricks That Cleaned Up My Codebase

When I started working with Laravel on real projects which introduced juggling multiple stakeholders, admin panels, and third-party integrations, I noticed my controllers and models slowly turning into a mess of repeated queries and bloated conditionals. Eloquent is powerful, but it’s easy to use it in ways that just barely work instead of ways that actually scale.

Here are five Eloquent tricks that made the biggest difference in cleaning up my code where each one solves a problem I ran into more than once.

1. Query Scopes Instead of Repeated where() Chains

If you find yourself writing the same where() conditions in multiple places, that’s a sign you need a scope.

Before:

$activeCustomers = Customer::where('status', 'active')
    ->where('deleted_at', null)
    ->get();

After:

// In the Customer model
public function scopeActive($query)
{
    return $query->where('status', 'active')
        ->whereNull('deleted_at');
}

// Anywhere else in the app
$activeCustomers = Customer::active()->get();

Scopes turn business logic into readable, reusable one-liners. When the definition of “active” changes, you update it in exactly one place instead of hunting through the codebase.

You can also build dynamic scopes that accept parameters:

public function scopeFromRegion($query, $region)
{
    return $query->where('region', $region);
}

// Usage
$customers = Customer::fromRegion('east-kalimantan')->get();

2. whenLoaded() to Avoid Accidental N+1 Queries in API Responses

The problem: you conditionally eager-load a relationship, but your resource always tries to access it which in turn triggers a lazy load (and an N+1 query) whenever it wasn’t loaded.

The fix:

public function toArray($request)
{
    return [
        'id' => $this->id,
        'title' => $this->title,
        'category' => $this->whenLoaded('category'),
        'author' => $this->whenLoaded('author', function () {
            return new AuthorResource($this->author);
        }),
    ];
}

whenLoaded() only includes the relationship in the response if it was actually eager-loaded upstream. No relationship in the payload, no surprise query, no N+1 problem creeping into production.

3. Accessors and Mutators for Data That Needs Shaping

Anytime I catch myself formatting or transforming the same field in multiple views or controllers, I move that logic into the model.

Before (scattered everywhere):

$fullName = $user->first_name . ' ' . $user->last_name;

After (Laravel 9+ attribute syntax):

use Illuminate\Database\Eloquent\Casts\Attribute;

protected function fullName(): Attribute
{
    return Attribute::make(
        get: fn () => "{$this->first_name} {$this->last_name}",
    );
}

// Usage anywhere
$user->full_name;

This is especially handy for things like formatting shipping costs from an API integration, normalizing phone numbers, or masking sensitive fields which are logics that belongs on the model, not scattered across every controller that touches it.

4. chunk() and lazy() for Large Datasets

Pulling thousands of records with ->get() and looping over them in PHP is a fast way to run out of memory.

Before (memory-heavy):

$orders = Order::where('status', 'pending')->get();
foreach ($orders as $order) {
    // process
}

After:

Order::where('status', 'pending')->chunk(200, function ($orders) {
    foreach ($orders as $order) {
        // process
    }
});

Or, if you don’t need control over batch size and just want a memory-efficient iterator:

foreach (Order::where('status', 'pending')->lazy() as $order) {
    // process
}

chunk() pulls records in batches, and lazy() uses a cursor under the hood, both keep memory flat no matter how large the table gets.

5. Eager Loading with Specific Columns to Cut Down Payload Size

Eager loading with with() solves N+1 queries, but people often forget you can also limit which columns come back which matters a lot once your models have dozens of fields.

Before:

$posts = Post::with('author')->get();

After:

$posts = Post::with('author:id,name,email')->get();

Just make sure the foreign key (id here) is included, or the relationship won’t resolve correctly. This trick alone noticeably shrunk response payloads and query time on listing pages where I only needed an author’s name and email, not every column on the users table.

Summary

None of these tricks are exactly new, they’re all first-class Eloquent features. The real win came from recognizing the patterns that kept repeating across different projects (dashboards, APIs, bulk processing) and reaching for the right tool instead of patching around the symptom each time.

If you’re maintaining a growing Laravel codebase, I’d start with scopes and whenLoaded() which tend to catch the most common messes with the least amount of refactoring.

Book a Free Consultation

Haven’t found the right solution yet?
Tell us about your needs, and let’s discuss the best solution together.

Project description.

Built a private marketplace research and purchase planning platform for Shopee. The system tracks product performance over time, estimates sales velocity and gross revenue, analyzes demand across product variants, and turns the research into actionable purchasing plans. It also calculates supplier costs, shipping, platform fees, margins, planned investment, and estimated earnings to support data-driven sourcing decisions.

Skills and deliverables

Full-Stack Development, Web Application Development, Data Analysis, Business Process Automation, Business Intelligence

Shopee Marketplace Research & Purchase Planning Dashboard

We built this application to solve a common problem in marketplace sourcing: finding products is easy, but deciding what is actually worth buying, which variant to prioritize, and how much inventory to purchase requires much more analysis. Instead of relying on a single product snapshot, the system organizes products into research collections and tracks their marketplace performance over time.

Product research & tracking

Products can be added individually or collected from marketplace stores and organized into dedicated research collections. The system keeps historical observations so users can compare changes between captures instead of relying only on the latest marketplace data. This makes it possible to estimate metrics such as: – Sales velocity – Estimated daily gross revenue – Price changes – Product movement over time – Variant-level demand

Variant demand analysis

One of the most important parts of the system is identifying which variants are actually driving sales. Instead of treating a product as a single item, the application analyzes sales movement at variant level and shows each variant’s contribution to recent demand. This helps identify which sizes, models, or variations should be prioritized when purchasing inventory.

Purchase planning

Research data can then be converted directly into a purchasing plan. For every product and variant, the user can enter supplier pricing, shipping costs, purchase quantities, exchange rates, and other operational costs. The system automatically calculates: – Planned inventory investment – Shipping cost – Marketplace/platform fees – Net margin per unit – Net margin percentage – Estimated earnings – Total batch investment This allows purchasing decisions to be evaluated before committing capital.

Why We built it

The goal was to replace a workflow that would normally require multiple spreadsheets, manual marketplace checks, and repeated calculations. The resulting workflow became: Discover products → Track performance → Analyze demand → Select variants → Plan purchasing → Estimate margins Everything is kept in one application, making the research and sourcing process easier to review, repeat, and scale.

Result

The platform turns raw marketplace observations into actionable purchasing information. Instead of choosing products based only on intuition or current sales numbers, users can evaluate sales movement, variant demand, sourcing cost, expected margin, and required investment before making a purchasing decision. Built as an end-to-end internal tool, from data collection and analysis to the final purchasing workflow.

Project description.

Built a browser-based tool that converts Shopee/SPX order exports into clean, print-ready shipping labels. It supports multiple Excel/CSV files, combines orders into one preview, and automatically generates QR codes, barcodes, tracking numbers, and product information for 68 mm thermal printers. All order data is processed locally in the browser for better privacy, eliminating repetitive manual label formatting.

Skills and deliverables

Laravel, Business Automation, CSV, Microsoft Excel

Shopee SPX Order Label Maker for Thermal Printing

This tool was built to simplify the shipping label preparation process for Shopee SPX orders. The existing workflow required order data to be exported from Shopee and manually formatted before it could be printed on a thermal printer. This became repetitive and time-consuming, especially when processing multiple orders at once.

We built a browser-based label generator that reads Shopee Excel/CSV exports and automatically converts the order data into clean, print-ready SPX labels. The tool automatically handles: – Tracking numbers – QR codes – Barcodes – Product names and variants – Order quantities – Multiple order files at once – 68 mm thermal printer formatting Users can review all generated labels before printing, without having to manually copy or reorganize the order information.

The order files are processed directly inside the browser instead of being uploaded to a server. This keeps customer and shipment data private while also making the tool fast and simple to use.