Laravel returns 包含第一个模型副本的集合

Laravel returns a Collection with duplicates of the first model

我正在开发一个 Laravel 5.7 (API) 应用程序,其背后有一个 PostgreSQL 数据库。相关模型是:User(客户和员工)、CarRequest

员工UserCar创建了Request,属于客户[=113] =] User.

关系是:

  • Car (作为客户 : User = n:m
  • Car : Request = 1:n
  • User : Request (作为员工 = 1:n

(数据设计不是最理想的,说得客气一点,但无论如何,这是目前给定的现实。)

现在进入实际问题。我想显示 客户 User:

的所有 Request
Request::query()
    ->join('user_car', 'user_car.car_id', '=', 'request.car_id')
    ->join('user', 'user.id', '=', 'user_car.user_id')
    ->where('user.id', '=', $customer->id)
    ->select()
    ->get();

给定 $customer->id 客户n Request 个。并且上面调用的结果 Collection 的长度是正确的。但是所有这些 n 条目都是第一个条目的副本。意思是:我得到一个包含 nRequest#1 实例的列表。

为什么第一个调用 return 对同一 Model 对象的引用列表? 这是一个(已知)错误吗?


附加信息

关系:

class User extends \Illuminate\Foundation\Auth\User
{
    // ...
    public function cars()
    {
        return $this->belongsToMany('App\Car', 'user_car')->withTimestamps();
    }
    public function requests()
    {
        return $this->hasMany(Request::class, 'user_id');
    }
}

class Car extends Model
{
    // ...
    public function users()
    {
        return $this->belongsToMany('App\User', 'user_car')->withTimestamps();
    }
    public function requests()
    {
        return $this->hasMany(Request::class);
    }
}

class Request extends Model
{
    // ...
    public function car()
    {
        return $this->belongsTo(Car::class);
    }
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

查询正确

我记录了数据库请求,得到了生成的语句

SELECT *
FROM "request"
INNER JOIN "user_car" ON "user_car"."car_id" = "request"."car_id"
INNER JOIN "user" ON "user"."id" = "user_car"."user_id"
WHERE "user"."id" = 1;

...,手动执行。结果 table 包含预期的 n 不同的 个条目。

不只是参考文献

结果 Collection 的条目实例引用了不同的对象:

$test1 = $resultCollection->first();
$test2 = $resultCollection->last();
$test3 = spl_object_hash($test1);
$test4 = spl_object_hash($test2);

Xdebug 输出:

$test3 = "0000000077505ccd000000007964e0a8" <-- ccd0
$test4 = "0000000077505c33000000007964e0a8" <-- c330

解决方法

我找到了解决方法。这个电话

Request::whereIn('car_id', $customer->cars()->pluck('id')->toArray())->get();

... 检索 correct/expected 模型集。

首先,请注意您的对象哈希值实际上并不相同,您可能正在处理两个不同的实例。

您可能遇到的问题是列名不明确。当您 JOIN 将多个表放在一起时,任何 matching/duplicate 列名称都将包含最后一个匹配列的值。您的 SQL GUI/client 通常将它们分开。不幸的是 Laravel 没有前缀机制,只能使用关联数组。

假设您所有的表都有一个主键列 id,结果集中的每个 Request 对象都可能具有相同的 ID - 您在 [=13] 中传递的用户 ID =]条件。

您可以通过显式选择需要防止歧义的列来解决现有查询中的这个问题。使用 ->select(['request.*']) 将返回的信息限制为 Request 对象数据。