Laravel-在关系eagerload中集成大型原始查询

时间:2019-04-23 11:32:28

标签: php laravel eloquent eager-loading laravel-query-builder

我已经使用laravel大约4年了,但是现在我面临着最大的挑战。我正在重构和扩展用于映射我工作的办公室的旧PHP应用程序。有一个巨大的SQL查询,我需要以某种方式将其集成到Laravel的QueryBuilder中,以提供渴望的加载关系。

流程是这样的

Building => hasMany: Floor => hasMany: Seat => hasMany: BookedSeat => belongsTo: User

其中BuildingFloorSeatBookedSeatEloquent模型。

我的庞大查询基于许多其他条件(例如预订座位的人是在家办公,度假等)从BookedSeat中选择Seat当前日期的预订。存储在其他一些表中),并在名为BookedSeat的{​​{1}}实例上设置一个属性,以了解在当日是否使用了Status

现在,我正在尝试将此原始查询集成到构建Seat层次结构中,然后将该层次结构发送给在前端运行的JSON应用程序。

层次结构类似于:

Vue.js

庞大的查询返回了一个对象数组,我可以使用它们与 { "buildings": [ { // properties "floors" : [ { //properties "seats": [ { //properties "booked": [ { "user": "some user model", "Status": "some booked status" } ] }, // other seats ] }, // other floors ] }, //other buildings ] } 集合进行水合,但是我不知道如何使用该集合或直接使用庞大的查询来渴望加载每个{{1}的每个BookedSeat的每个BookedSeat的{​​{1}},并让框架为我做繁重的工作。

我尝试构建的方法如下:

Seat

并这样称呼它:

Floor

但是我需要使用Buildingpublic static function bookedSeatsForFloor(Relation $seatQuery, Relation $bookedQuery, Carbon $forDate) { $format = $forDate->format('Y-m-d H:m:i'); $bindings = $seatQuery->getBindings(); /** @var AvailableSeatsQuerySimple $availableSeats */ $availableSeats = new AvailableSeatsQuerySimple($bindings, $format); // bindings are my floor id's and I'm feeding them to my big query in order to have control over which floors to load depending on the user's rights return DB::raw($availableSeats->getRawQuery()); } 以某种方式修改Floor::where('id', $someId)->with(['seats' => static function ($seatQuery) use ($_that) { /** * Add to each seat the manager and the active booking */ $seatQuery->with(['booked' => static function ($bookedQuery) use ($seatQuery, $_that) { return self::bookedSeatsForFloor($seatQuery, $bookedQuery, $_that->forDate); }, 'manager'])->orderBy('seat_cir'); }]); 方法中的$bookedQuery,但是我不知道如何将庞大的查询转换为一个Builder实例。

谢谢!

PS:由于复杂性,我最好不打算将大型查询重写为雄辩的语法

添加的详细信息:

因此,根据要求,这是我的原始查询,在其中我根据公司政策更改了一些数据库/表名

bookedSeatsForFloor

我的模型/关系如下:

$bookedQuery->select('something')

1 个答案:

答案 0 :(得分:0)

这个问题很困难。我一整周都坚持尝试不同的方法,但是找不到任何好的方法。

我最终使用@Jonas Staudenmeir的建议,方法是手动映射所有嵌套关系,然后在booked模型实例上从使用Seat获得的集合中设置相应的BookedSeat::hydrate()关系以原始查询的结果作为参数。

$availableSeats = new AvailableSeatsQuerySimple($format);

        // Map through all the Buildings
        $this->template['buildings'] = $this->template['buildings']
            ->map(static function (Building $building) use ($availableSeats) {

                // Map through all the Floors in a Building
                $floors = $building->floors->map(static function (Floor $floor) use ($availableSeats) {
                    /** @var BookedSeat|Collection $booked */
                    $booked = $availableSeats->execute($floor->id); // execute the raw query and get the results

                    if(count($booked) > 0) {

                        // Map through all the Seats in a Floor
                        $seats = $floor->seats->map(static function (Seat $seat) use ($booked) {

                            // Select the BookedSeat for the corresponding Seat
                            /** @var BookedSeat $bookedSeatForRelation */
                            $bookedSeatForRelation = $booked->filter(static function (BookedSeat $bookedSeat) use ($seat) {
                                return $bookedSeat->seat_identifier === $seat->id;
                            })->first();

                            // Attach the BookedSeat to the Seat only if the Status IS NOT 0
                            if($bookedSeatForRelation !== null && $bookedSeatForRelation->Status !== 0) {
                                return $seat->setRelation('booked', $bookedSeatForRelation);
                            }

                            return $seat->setRelation('booked', null);
                        });

                        return $floor->setRelation('seats', $seats);
                    }

                    return $floor;
                });

                return $building->setRelation('floors', $floors);
            });