Hello everyone,
I'm trying to get a definitive clarification on the index creation behavior of ->constrained() when used in a table that also has a composite unique index. I've encountered a scenario where an expected index is not being created, and I'd like to understand if this is intended behavior.
Context:
- Laravel Version: 12.x
- PostgreSQL Version: 17.x
The Scenario
Let's say I have three migrations to create authors, categories, and a books table.
1. authors table:
Schema::create('authors', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
2. categories table:
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
3. books table:
This is where the unexpected behavior occurs. The table has two foreign keys and a composite unique index.
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->foreignId('author_id')->constrained();
$table->foreignId('category_id')->constrained();
$table->string('title');
$table->timestamps();
// A book title must be unique for a given author.
$table->unique(['author_id', 'title']);
});
The Expectation
Based on Laravel's conventions, I expected the books table to have three performance indexes:
- A primary key index on
id.
- A unique composite index on
(author_id, title).
- A simple B-Tree index on
category_id (created by the ->constrained() method).
The simple index on author_id would be correctly omitted as redundant because author_id is the leading column in the composite unique index.
The Actual Result
However, after running the migration and inspecting the database directly with the following query:
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'books';
The result only shows two indexes:
| indexname |
indexdef |
books_pkey |
CREATE UNIQUE INDEX books_pkey ON public.books USING btree (id) |
books_author_id_title_unique |
CREATE UNIQUE INDEX books_author_id_title_unique ON public.books USING btree (author_id, title) |
The expected index on category_id is missing.
The Question
Is this omission of the category_id index the intended behavior of Laravel's schema builder?
It seems that the presence of the unique(['author_id', 'title']) composite index is preventing the creation of other expected indexes from ->constrained(). This could lead to silent performance issues on queries filtering by category_id.
Any clarification on whether this is a bug or an undocumented optimization feature would be greatly appreciated!
Thanks!