Имеет один через 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;
}
Ответ 3
Это относится к вашему первоначальному запросу.
hasOneThrough будет доступен в Laravel 5.8
https://github.com/laravel/framework/pull/26057/files
Ответ 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();
Потому что он использует энергичную загрузку, и вы обращаетесь к базе данных на каждой итерации. Решения такого типа не оптимизированы.