Как настроить отношения (relationships) в Laravel Eloquent без использования внешних ключей (foreign keys) в базе данных?

«Как настроить отношения (relationships) в Laravel Eloquent без использования внешних ключей (foreign keys) в базе данных?» — вопрос из категории Базы данных, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Eloquent ORM позволяет определять отношения между моделями даже при отсутствии декларативных внешних ключей (FOREIGN KEY) на уровне базы данных. Для этого нужно явно указать имена полей, связывающих таблицы.

Сценарий: У нас есть таблицы users и posts. В таблице posts есть поле author_id, которое логически ссылается на users.id, но внешний ключ не создан.

1. Определение отношения hasMany (User -> Posts): В модели User мы указываем, что локальным ключом является id, а внешним ключом в связанной таблице Post — поле author_id.

// app/Models/User.php
class User extends Model
{
    public function posts()
    {
        // hasMany(RelatedModel, foreignKey, localKey)
        return $this->hasMany(Post::class, 'author_id', 'id');
    }
}
// Использование
$user = User::find(1);
foreach ($user->posts as $post) { // Eloquent выполнит запрос: SELECT * FROM posts WHERE author_id = 1
    echo $post->title;
}

2. Определение обратного отношения belongsTo (Post -> Author): В модели Post мы указываем, что внешним ключом в этой таблице является author_id, а он ссылается на локальный ключ id в модели User.

// app/Models/Post.php
class Post extends Model
{
    public function author()
    {
        // belongsTo(RelatedModel, foreignKey, ownerKey)
        return $this->belongsTo(User::class, 'author_id', 'id');
    }
}
// Использование
$post = Post::find(10);
echo $post->author->name; // Запрос: SELECT * FROM users WHERE id = {value_of_author_id}

3. Создание миграции БЕЗ внешнего ключа:

// database/migrations/xxxx_create_posts_table.php
Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('author_id'); // Просто целочисленное поле
    // $table->foreign('author_id')->references('id')->on('users'); // ЭТУ СТРОКУ ПРОПУСКАЕМ
    $table->string('title');
    $table->timestamps();
});

Важные замечания:

  • Целостность данных: Без FOREIGN KEY база данных не будет автоматически проверять существование связанной записи (users.id) при вставке или обновлении posts.author_id. Ответственность за целостность ложится на приложение.
  • Каскадное удаление: Опции onDelete('cascade') на уровне базы данных работать не будут. Для этого нужно использовать события модели или мягкое удаление (soft deletes).
  • Производительность: Отсутствие индекса на поле author_id сильно замедлит выборки по отношению. Всегда добавляйте индекс вручную: $table->index('author_id');.