Запутался с hasOne Eloquent Отношения Laravel - PullRequest
0 голосов
/ 09 июля 2019

У меня новое приложение Laravel 5.8.Я начал играть с Eloquent ORM и его взаимосвязями.

Сразу же возникла проблема.

У меня есть следующие таблицы.(это всего лишь пример, по причинам тестирования, а не для реального приложения)

Login table:
--------------------------
| id | user    | data_id |
--------------------------
| 1  | admin   | 1       |
| 2  | admin   | 2       |
| 3  | admin   | 3       |
--------------------------

Data table:
--------------
| id | ip_id |
--------------
| 1  | 1     |
| 2  | 2     |
| 3  | 3     |
--------------

IP table:
----------------------
| id | ip            |
----------------------
| 1  | 192.168.1.1   |
| 2  | 192.168.1.2   |
| 3  | 192.168.1.3   |
----------------------

Я хотел получить IP, принадлежащий фактическому логину.

Итак, ядобавил отношение hasOne к Login table, имеющему внешний ключ для Data table:

public function data()
{
    return $this->hasOne('App\Models\Data');
}

Затем я добавил отношение hasOne к Data table, который имеет внешний ключ дляIP table:

public function ip()
{
    return $this->hasOne('App\Models\Ip');
}

Как только я закончил, я хотел получить IP-адрес для первой записи таблицы входа в систему:

Login::find(1)->data()->ip()->get();

Но я получаю эту ошибку:

Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::ip()

Чего мне здесь не хватает и как мне правильно получить IP этого логина?Нужно ли где-нибудь belongsTo?

Ответы [ 3 ]

3 голосов
/ 09 июля 2019

С вашей структурой базы данных:

Логин belongsTo Данные

Данные hasOne Вход

Данные belongsTo IP

IP hasOne Данные

После исправления ваших методов вы можете использовать ваши отношения следующим образом

$login = Login::with(['data.ip'])->find(1);

2 голосов
/ 09 июля 2019

1-я ошибка: неправильное определение взаимосвязи

Отношения Laravel двунаправлены. В отношениях one-to-one вы можете определить прямую связь (HasOne) и обратную связь (BelongsTo)

Прямая связь должна быть:

             HasOne                HasOne
[ Login ] <----------- [ Data ] <----------- [ IP ]

И обратное соотношение должно быть:

           BelongsTo             BelongsTo
[ Login ] -----------> [ Data ] -----------> [ IP ]

См. Eloquent: отношения - один-к-одному документы для деталей о том, как его определить.

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

2-я ошибка: вы вызываете метод отношений, а не сами отношения

Когда вы делаете:

Login::find(1)->data()->ip()->get();

Вы вызываете метод data, который определяет ваши отношения, а не связанную модель. Это полезно в некоторых случаях, но не в вашем случае.

Вместо этого вызывайте магическое свойство отношения:

Login::find(1)->data->ip;

Обратите внимание, что мы не используем () и нам не нужен get() здесь. Laravel позаботится о том, чтобы загрузить его для нас.

Использовать Eager Loading

Laravel Eloquent имеет Eager Loading для отношений, которые очень полезны в некоторых случаях, потому что они предварительно загружают ваши отношения и уменьшают количество выполняемых вами запросов.

В описанной вами ситуации (загрузка одной модели Login) это не приводит к повышению производительности, но также не замедляет работу.

Это полезно, когда вы загружаете много моделей, поэтому уменьшает количество запросов к базе данных с N+1 до 2.

Представьте, что вы загружаете 100 Login моделей, без какой-либо активной загрузки, вы сделаете 1 запрос, чтобы получить ваши Login модели, 100 запросов, чтобы получить Data модели, и более 100 запросов, чтобы получить * 1054. * модели.

При активной загрузке он выполнит только 3 запроса, что приведет к значительному увеличению производительности.

1 голос
/ 09 июля 2019

Вы можете попробовать вот так:

Логин

public function data()
{
    return $this->belongsTo('App\Models\Data', 'data_id');
}

Данные

public function ip()
{
    return $this->belongsTo('App\Models\Ip', 'ip_id');
}
$login = Login::with(['data.ip'])->find(1);

А внутри data у вас будет ip как $login->data->ip.

...