Связи моделей IO7
Внимание
Данная статья документации написана нейросетью. Возможны некоторые расхождения и неточность информации!
IO7 использует стандартные методы связей Eloquent, доступные через IO\SqlClass (который наследует Illuminate\Database\Eloquent\Model).
Определение связей
hasMany()
Один ко многим. Определяется в родительской модели.
public function hasMany(
string $related,
string $foreignKey = null,
string $localKey = null
): \Illuminate\Database\Eloquent\Relations\HasMany
Параметры:
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$related | string | — | Да | Класс связанной модели |
$foreignKey | string|null | null | Нет | Внешний ключ связанной таблицы |
$localKey | string|null | null | Нет | Локальный ключ текущей таблицы |
namespace App\Models;
use IO\SqlClass;
class Department extends SqlClass
{
protected $primaryKey = 'pkid';
public static function tablename(): string
{
return 'departments';
}
public function employees()
{
return $this->hasMany(Employee::class, 'department_id', 'pkid');
}
}
$department = Department::find(1);
$employees = $department->employees; // коллекция
$count = $department->employees()->count(); // количество
belongsTo()
Многие к одному. Определяется в дочерней модели.
public function belongsTo(
string $related,
string $foreignKey = null,
string $ownerKey = null,
string $relation = null
): \Illuminate\Database\Eloquent\Relations\BelongsTo
Параметры:
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$related | string | — | Да | Класс родительской модели |
$foreignKey | string|null | null | Нет | Внешний ключ текущей таблицы |
$ownerKey | string|null | null | Нет | Ключ родительской таблицы |
$relation | string|null | null | Нет | Имя связи |
class Employee extends SqlClass
{
protected $primaryKey = 'pkid';
public static function tablename(): string
{
return 'employees';
}
public function department()
{
return $this->belongsTo(Department::class, 'department_id', 'pkid');
}
}
$employee = Employee::find(1);
$department = $employee->department; // объект Department
$departmentName = $employee->department->name; // имя отдела
hasOne()
Один к одному.
public function hasOne(
string $related,
string $foreignKey = null,
string $localKey = null
): \Illuminate\Database\Eloquent\Relations\HasOne
class User extends SqlClass
{
public static function tablename(): string
{
return 'users';
}
public function profile()
{
return $this->hasOne(Profile::class, 'user_id', 'pkid');
}
}
$user = User::find(1);
$avatar = $user->profile->avatar_url;
hasManyThrough()
Один ко многим через промежуточную модель.
public function hasManyThrough(
string $related,
string $through,
string $firstKey = null,
string $secondKey = null,
string $localKey = null,
string $secondLocalKey = null
): \Illuminate\Database\Eloquent\Relations\HasManyThrough
Параметры:
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$related | string | — | Да | Класс целевой модели |
$through | string | — | Да | Класс промежуточной модели |
$firstKey | string|null | null | Нет | Внешний ключ промежуточной модели |
$secondKey | string|null | null | Нет | Внешний ключ целевой модели |
$localKey | string|null | null | Нет | Локальный ключ текущей модели |
$secondLocalKey | string|null | null | Нет | Локальный ключ промежуточной модели |
class Country extends SqlClass
{
public static function tablename(): string
{
return 'countries';
}
public function posts()
{
return $this->hasManyThrough(
Post::class,
User::class,
'country_id', // ForeignKey в users
'user_id', // ForeignKey в posts
'pkid', // LocalKey в countries
'pkid' // LocalKey в users
);
}
}
belongsToMany()
Многие ко многим (через промежуточную таблицу).
public function belongsToMany(
string $related,
string $table = null,
string $foreignPivotKey = null,
string $relatedPivotKey = null,
string $relation = null
): \Illuminate\Database\Eloquent\Relations\BelongsToMany
class User extends SqlClass
{
public function roles()
{
return $this->belongsToMany(
Role::class,
'user_roles', // промежуточная таблица
'user_id', // ключ текущей модели
'role_id' // ключ связанной модели
);
}
}
$user = User::find(1);
$roles = $user->roles; // коллекция Role
$hasAdmin = $user->roles->contains('name', 'admin');
Жадная загрузка (Eager Loading)
Для предотвращения N+1 проблемы используйте with():
// Загрузка связей вместе с основной моделью
$departments = Department::with('employees')->get();
// Вложенные связи
$users = User::with(['posts', 'profile', 'roles'])->get();
// Жадная загрузка в CrudApi через onSearchParams()
class EmployeeApi extends \IO\Api\CrudApi
{
public function onSearchParams(array $params): array
{
$params['join'][] = 'LEFT JOIN departments ON employees.department_id = departments.pkid';
return $params;
}
}
Фильтрация по связям
// whereHas — модели, у которых есть связанные записи
$users = User::whereHas('posts', function ($query) {
$query->where('status', 'published');
})->get();
// whereDoesntHave — модели без связанных записей
$users = User::whereDoesntHave('posts')->get();
// has — модели минимум с N связями
$users = User::has('posts', '>=', 3)->get();
Пример: полная модель со связями
namespace App\Models;
use IO\SqlClass;
class Order extends SqlClass
{
protected $primaryKey = 'pkid';
public $timestamps = false;
public static function tablename(): string
{
return 'orders';
}
public function customer()
{
return $this->belongsTo(Customer::class, 'customer_id', 'pkid');
}
public function items()
{
return $this->hasMany(OrderItem::class, 'order_id', 'pkid');
}
public function status()
{
return $this->belongsTo(OrderStatus::class, 'status_id', 'pkid');
}
public static function rules(): array
{
return [
'static' => ['*' => 1],
'object' => ['*' => 1],
];
}
}
// Использование
$order = Order::with(['customer', 'items', 'status'])->find(42);
echo $order->customer->name;
echo $order->status->label;
foreach ($order->items as $item) {
echo $item->product_name . ': ' . $item->quantity;
}