You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何拆分Laravel Eloquent大型查询并实现逻辑复用?

Refactoring Large Laravel Eloquent Queries for Reusability (AJAX & Beyond)

Great question—let's break down how to split up this bulky query into reusable components, clean up that Blade-included logic, and make it easy to reuse across AJAX requests. Here's a practical, maintainable approach:

1. Extract Reusable Logic into Query Scopes

Laravel's local query scopes are perfect for wrapping repeatable query conditions directly on your models. This keeps your controller clean and centralizes logic where it belongs.

Add Scopes to Your Hotel Model

// app/Models/Hotel.php
use Illuminate\Http\Request;

class Hotel extends Model
{
    // ... existing relationships ...

    // Scope for hotels with active, filtered rooms
    public function scopeWithActiveRooms($query, Request $request)
    {
        return $query->has('room')
            ->whereHas('room', fn($q) => $q->applyFilters($request))
            ->with(['room' => fn($q) => $q->applyFilters($request)
                ->with('roomtype', 'floorroomcount', 'image', 'amenities')
                ->orderBy('price')
                ->where('astatus', 1)
                ->where('status', 0)
            ]);
    }

    // Scope for hotels with active clients
    public function scopeForActiveClients($query)
    {
        return $query->whereHas('client', fn($q) => $q->where('status', 1));
    }

    // Scope for rating filtering
    public function scopeFilterByRating($query, $rating)
    {
        return $rating > 0 ? $query->where('star', $rating) : $query;
    }

    // Scope for location filtering
    public function scopeFilterByLocation($query, $locationId)
    {
        return $query->where('city', $locationId);
    }

    // Scope for area filtering (replaces the Blade include logic)
    public function scopeFilterByArea($query, Request $request)
    {
        if ($request->filled('type') && $request->type === 'Area' && $request->filled('location_id')) {
            // Cache this to avoid repeated DB hits for the same location
            $area = cache()->remember("hotel_area_{$request->location_id}", 3600, function () use ($request) {
                return Hotel::where('city', $request->location_id)->value('area');
            });

            if ($area) {
                $query->where('area', $area);
            }
        }
        return $query;
    }

    // Scope for active hotels
    public function scopeIsActive($query)
    {
        return $query->where('astatus', 1)->where('status', 0);
    }
}

Add a Scope to Your Room Model

Since the room filtering logic repeats twice (in whereHas and with), extract it into the Room model to avoid duplication:

// app/Models/Room.php
use Illuminate\Http\Request;

class Room extends Model
{
    // ... existing relationships ...

    public function scopeApplyFilters($query, Request $request)
    {
        // Handle amenities filter
        if ($request->filled('amenities')) {
            $amenities = $request->amenities;
            $count = count($amenities);
            $query->withCount(['amenities' => fn($q) => $q->whereIn('amenities_id', $amenities)])
                ->having('amenities_count', $count);
        }

        // Handle guest count filter
        $memberCount = $request->filled('guestsCount') 
            ? $request->guestsCount + $request->childCount 
            : 1;

        return $query->where('capacity', '>=', $memberCount);
    }
}

2. Create a Query Builder Service

For even better reusability across controllers or AJAX endpoints, wrap the query assembly in a dedicated service class. This keeps your controller thin and makes it trivial to reuse the exact query anywhere:

// app/Queries/HotelRoomQuery.php
namespace App\Queries;

use App\Models\Hotel;
use Illuminate\Http\Request;

class HotelRoomQuery
{
    public static function build(Request $request)
    {
        return Hotel::query()
            ->withActiveRooms($request)
            ->forActiveClients()
            ->when($request->filled('rating'), fn($q) => $q->filterByRating($request->rating))
            ->when($request->filled('location_id'), fn($q) => $q->filterByLocation($request->location_id))
            ->filterByArea($request)
            ->isActive();
    }
}

3. Simplify Your Controller

Your controller method now becomes clean, readable, and easy to maintain:

// app/Http/Controllers/YourController.php
use App\Queries\HotelRoomQuery;
use Illuminate\Http\Request;

class YourController extends Controller
{
    public function loadMore(Request $request)
    {
        // Build the reusable query
        $hotelsQuery = HotelRoomQuery::build($request);

        // Execute and post-process
        $hotels = $hotelsQuery->simplePaginate(20);
        $hotels = $this->addRates($hotels, $request->checkin_date, $request->checkout_date);
        $hotels = $this->addAvailableCount($hotels, $request->checkin_date, $request->checkout_date);
        
        $hotels = $hotels->transform(function (Hotel $hotel) {
            $hotel->setRelation('room', $hotel->room->sortBy('price')->flatten());
            return $hotel;
        });

        return view('roomlist.loadmore', compact('hotels'));
    }
}

4. Ditch the Blade Query Logic

Including query logic in Blade is an anti-practice—it mixes presentation and business logic, making it hard to debug and reuse. By moving that area-filtering logic into the Hotel model's scope, you can completely remove the @include('roomlist.area') line.

Bonus Optimizations

  • Cache Frequently Used Data: As shown in the FilterByArea scope, cache the area lookup to reduce unnecessary DB queries.
  • Validate Request Data: Use a FormRequest class to validate inputs like guestsCount, amenities, and location_id before they reach your query logic.
  • Encapsulate Post-Processing: Move addRates and addAvailableCount into a service class or model method for even more reusability.

This approach makes your query logic modular, reusable across any AJAX request (or non-AJAX routes), and follows Laravel's best practices for clean, maintainable code.

内容的提问来源于stack exchange,提问作者arun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:02:48