Laravel Eloquent 在与其他表连接后从关系中选择字段

Posted

技术标签:

【中文标题】Laravel Eloquent 在与其他表连接后从关系中选择字段【英文标题】:Laravel Eloquent selecting field from relationship after joining with other tables 【发布时间】:2021-12-21 03:19:23 【问题描述】:

我有一个查询,我已将多个表连接在一起,并且由于它们具有相同名称的字段,因此我使用 select 查询仅挑选出我需要的字段。但是,我还需要包含从 with 查询获得的结果,但我不知道如何在 select 查询中指定它。

        $display_tickets = ManualTicket::select('u.name as name', 'i.name as initiator', 'manual_tickets.status as status', 'manual_tickets.description as description', 'manual_tickets.location as location', 'manual_tickets.created_at as created_at', 'manual_tickets.initiator_id as initiator_id', 'manual_tickets.id as manual_ticket_id','manual_tickets.manual_ticket_log as manual_ticket_log_id')
            ->leftJoin('users as u', 'u.id', '=', 'manual_tickets.user_id')
            ->leftJoin('users as i', 'i.id', '=', 'manual_tickets.initiator_id')
            ->where(function ($checkClients) use($target_client_id)
                $checkClients->where('u.client_id', '=', $target_client_id)
                    ->orWhere('i.client_id', '=', $target_client_id);
            )
            ->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
            ->with('manual_ticket_log')
            ->orderBy("created_at", "DESC")->get();

我试图通过使用下面的代码来包含手动工单日志,但它说手动工单表中没有这样的字段。

'manual_tickets.manual_ticket_log as manual_ticket_log_id')

简而言之,如何将 ->with('manual_ticket_log') 中的 with 关系的结果包含到 select 语句中

--EDIT-- (尝试用查询替换联接)

但是,我的 SQL 查询似乎有任何错误,我认为这源于导致显示此错误的代码的 whereHas 部分。

strtolower() 期望参数 1 是字符串,给定对象

 $display_tickets = ManualTicket::select('*')
                ->with('user')
                ->with('initiator')
                ->with('manual_ticket_log')
                ->where(function ($checkClients) use($target_client_id)
                    $checkClients->whereHas('user', function ($checkClient) use($target_client_id)
                        $checkClient->where('client_id', '=', $target_client_id);
                    )
                    ->orWhere($checkClients->whereHas('initiator', function ($checkClient2) use($target_client_id)
                        $checkClient2->where('client_id', '=', $target_client_id);
                    ));
                )
                ->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
                ->orderBy("created_at", "DESC")->get();

【问题讨论】:

你不能也不应该。雄辩的关系在单独的查询中加载,有充分的理由不使用连接,而是对您的用户关系也这样做 @apokryfos 我尝试用 with 替换连接查询,但我的操作似乎有错误。你能告诉我我做错了什么吗 尝试用 ->orWhereHas 替换 orWhere($checkClients->whereHas @apokryfos 谢谢,我相信我的问题已经解决了。您可能想发布您的 cmets 作为答案,以便我将其标记为正确 【参考方案1】:

您应该加入该表以获取字段形式:

   $display_tickets = ManualTicket::select('u.name as name', 'i.name as initiator', 'manual_tickets.status as status', 'manual_tickets.description as description', 'manual_tickets.location as location', 'manual_tickets.created_at as created_at', 'manual_tickets.initiator_id as initiator_id', 'manual_tickets.id as manual_ticket_id','manual_tickets.manual_ticket_log as manual_ticket_log_id',
            'manual_tickets.manual_ticket_log as manual_ticket_log_id')
            ->leftJoin('users as u', 'u.id', '=', 'manual_tickets.user_id')
            ->leftJoin('users as i', 'i.id', '=', 'manual_tickets.initiator_id')
            ->leftJoin('manual_ticket_logs',function ($join)
            $join->on('manual_ticket_logs.manual_ticket_id', '=', 'manual_tickets.id')
                ->on('manual_ticket_logs.id', '=', \DB::raw("(select max(id) from manual_ticket_logs WHERE manual_ticket_logs.manual_ticket_id = manual_tickets.id)"));
            )
            ->where(function ($checkClients) use($target_client_id)
                $checkClients->where('u.client_id', '=', $target_client_id)
                    ->orWhere('i.client_id', '=', $target_client_id);
            )
            ->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
            ->with('manual_ticket_log')
            ->orderBy("created_at", "DESC")->get();

请确保最新加入子句中的字段名称。

【讨论】:

有没有办法让它保持原样? 问题是:在 Laravel 中使用单独的查询获取信息,据我所知,没有其他直接的方法。 在这种情况下,如果我要使用加入,我只希望手动工单与手动工单日志中的最新条目一起加入(因为一张特定的手动工单可能有很多日志)如何能做到吗? 我已经更新了加入最新行的代码,更多关于加入最新行的信息:***.com/a/45570994/10573560【参考方案2】:

你可以试试这个

    $display_tickets = ManualTicket::select('u.name as name', 'i.name as initiator', 'manual_tickets.status as status', 'manual_tickets.description as description', 'manual_tickets.location as location', 'manual_tickets.created_at as created_at', 'manual_tickets.initiator_id as initiator_id', 'manual_tickets.id as manual_ticket_id','manual_tickets.manual_ticket_log as manual_ticket_log_id')
        ->leftJoin('users as u', 'u.id', '=', 'manual_tickets.user_id')
        ->leftJoin('users as i', 'i.id', '=', 'manual_tickets.initiator_id')
        ->where(function ($checkClients) use($target_client_id)
            $checkClients->where('u.client_id', '=', $target_client_id)
                ->orWhere('i.client_id', '=', $target_client_id);
        )
        ->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
        ->with(['manual_ticket_log' => function($query) 
                $query->select('field1', 'field2');
          ])
        ->orderBy("created_at", "DESC")->get();

或者这样做

->with('manual_ticket_log:field1, field2')

【讨论】:

我试过但同样的错误,手动票证中找不到手动票证日志列【参考方案3】:

您可以利用 Laravel 关系使代码更具可读性并避免昂贵的leftJoin

 $display_tickets = ManualTicket::select('*')
     ->with([ 'user', 'initiator', 'manual_ticket_log' ])
     ->where(function ($checkClients) use($target_client_id)
         $checkClients->whereHas('user', function ($checkClient) use($target_client_id)
              $checkClient->where('client_id', '=', $target_client_id);
        )->orWhereHas('initiator', function ($checkClient2) use($target_client_id)
              $checkClient2->where('client_id', '=', $target_client_id);
       ));
     )->whereBetween('created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
     ->orderBy("created_at", "DESC")
     ->get();

【讨论】:

以上是关于Laravel Eloquent 在与其他表连接后从关系中选择字段的主要内容,如果未能解决你的问题,请参考以下文章

如何在laravel eloquent中为左连接表起别名

使用 eloquent laravel 获取连接表数据

在 Laravel Eloquent 中连接表值

Laravel Eloquent 表之间通过其他表的关系

Laravel Eloquent:如何从连接表中仅获取某些列

使用 Laravel 的查询构建器或 Eloquent 将一个表与一个临时表进行内部连接