Имеет один через Laravel Eloquent

У меня есть три таблицы garages, cars, securities.

Ценные бумаги обеспечивают безопасность одного автомобиля, у вас может быть несколько ценных бумаг, но одна безопасность может обеспечить безопасность только одного автомобиля. В гараже есть машина и ценные бумаги.

Я хочу перечислить все ценные бумаги и узнать название гаража, которым он является. Проблема в том, что securities не имеет столбца с идентификатором гаража, только идентификатор автомобиля, который он держит в безопасности, но у автомобиля есть идентификатор гаража.

У Laravel Eloquent есть метод, называемый hasManyThrough, но у securities есть только один garage - cars.

Вот таблицы:

таблица гаражей:

-----------------------------------
|garage_id|garage_name|garage_size|
-----------------------------------
|        1|   Garage 1|        200|
-----------------------------------
|        2|   Garage 2|        400|
-----------------------------------

таблица машин:

---------------------------
|car_id|car_name|garage_id|
---------------------------
|     1|   Car 1|        1|
---------------------------
|     2|   Car 2|        1|
---------------------------
|     3|   Car 3|        2|
---------------------------

таблица ценных бумаг:

----------------------------------
|security_id|security_name|car_id|
----------------------------------
|          1|  Security 1|      1|
----------------------------------
|          2|  Security 2|      1|
----------------------------------
|          3|  Security 3|      2|
----------------------------------
|          4|  Security 4|      3|
----------------------------------

Выходные данные должны быть:

Security 1 is on Garage 1
Security 2 is on Garage 1
Security 3 is on Garage 1
Security 4 is on Garage 2

И у меня есть модели:

Код состоит в том, чтобы перечислить гаражи, но я хочу сделать подобное, но перечислить ценные бумаги (только для вас, чтобы иметь представление о том, какова структура).

$garages = Garages::with(['cars', 'securities'])->get();

$garages->transform(function($garages) {
    return array(
        'garage_name'      => $garage->garage_name,
        'count_cars'       => $garage->cars->count(),
        'count_securities' => $garage->securities->count(),
   );
});

class Garages extends Eloquent
{
    public function cars()
    {
        return $this->hasMany('cars');
    }

    public function securities()
    {
        return $this->hasMany('securities');
    }
}

class Cars extends Eloquent
{
}

class Securities extends Eloquent
{
}

И еще раз подчеркну: Я хочу, чтобы название гаража было связано с автомобилем, который охраняет.

Просто чтобы было еще проще понять, если я сделаю это:

$securities = Securities::with(['cars'])->get();

class Securities extends Eloquent
{
    public function cars()
    {
        return $this->hasOne('cars');
    }
}

Я получу только garage_id из таблицы cars как отношения. Что я действительно хочу, так это название гаража.

[relations:protected] => Array
    (
        [cars] => Cars Object
            (
                ...
                [attributes:protected] => Array
                    (
                        ...
                        [car_name] => Car 1
                        [garage_id] => 1
                        ...

Ответ 1

Похоже, вы не правильно связываете объекты. Давайте разберемся с этим:

Если Garage имеет много Car, то Car принадлежит Garage, давайте продолжим с этой идеей.

  • Garage имеет много Car
  • Car имеет много Security
  • Security Принадлежит Car
  • Garage имеет много Security до Car

В Eloquent вы можете просто обойти эти отношения, это должно работать, учитывая схему, которую вы опубликовали выше.

class Garage extends Eloquent
{
    public function cars()
    {
        return $this->hasMany('cars');
    }

    public function securities()
    {
        return $this->hasManyThrough('Security', 'Car');
    }
}

class Car extends Eloquent
{
    public function securities()
    {
        return $this->hasMany('Security');
    }

    // ADDED IN SECOND EDIT

    public function garage()
    {
        return $this->belongsTo('Garage');
    }       
}

class Security extends Eloquent
{
    public function car()
    {
        return $this->belongsTo('Car');
    }
}

И так и должно быть.

ОБНОВЛЕНИЕ: Вы можете получить доступ ко всем этим отношениям из любой модели, если есть путь, который вы можете нарисовать из одной модели в другую, используя комбинацию этих отношений. Проверьте это, например:

$security = Security::with('car.garage')->first();

извлечет первую запись Security и загрузит в нее отношение Car, затем сделает еще один шаг и загрузит все отношения Garage для каждого объекта Car, загруженного по модели Security.

Вы можете получить к ним доступ, используя следующий синтаксис:

$security->car->garage->id

// Other examples
$garage = Garage::with('cars.securities')->first();

foreach($garage->cars as $car)
{
    foreach($cars->securities as $security)
    {
        echo "Car {$car->id} has {$security->id} assigned to it";
    }
}

Кроме того, обратите внимание, что объекты отношений являются экземпляром Collection, поэтому у вас есть все причудливые методы, такие как ->each(), ->count(), ->map().

Ответ 2

Недавно я столкнулся с подобной проблемой и смог сделать что-то вроде этого:

class Cars extends Eloquent
{
    public function garage()
    {
        return $this->hasOne('Garages');
    }
}

class Securities extends Eloquent
{
    public function car()
    {
        return $this->hasOne('Cars');
    }

    public function garage()
    {
        return $this->car->garage();
    }
}

Это дает вам ценные бумаги с одним гаражным отношением, и вы можете сделать что-то вроде:

$securities = Securities::with('garage')->get();

foreach($securities as $security)
{
    echo $security->security_name . ' is on ' . $security->garage->garage_name;
}

Ответ 4

Вы можете использовать hasOneThrough, встроенный в Laravel => 5.8.

public function garage()
{
    return $this->hasOneThrough(
        Garage::class,
        Car::class,
        'car_id',
        'id',
        null,
        'garage_id'
    );
}

Не используйте такие решения, как

return $this->car->garage();

Потому что он использует энергичную загрузку, и вы обращаетесь к базе данных на каждой итерации. Решения такого типа не оптимизированы.