Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

lara9765's avatar

Non-Nullable Table Additions

Perhaps I'm missing something at a higher level, but I'm having troubles getting Laravel Migrations and Seeding to behave as I require. I have an existing schema, with existing data that needs to be preserved. I would like to add a new required foreign key relationship and running into troubles.

If I setup my migration to add the field and setup a required relationship, it will fail immediately because existing data does not exist. It seems I need to first do a migration to create the foreign table, then a seed operation to fill that with the default record before a second migration to set the FK field as non-nullable and defaulted to the now existing row.

This seems like it must be a common use case and I'm missing something basic - can someone clue me in on proper method to do such an operation because I cannot get the order of execution proper in my mind.

0 likes
5 replies
jekinney's avatar

Order is important. In your migration file add your column with a default value which should propagate to all rows with the default value. Then add the code for the foreign key.

I had issues when I started using laravels migrations with the sequence the different migrations ran. Specifically setting foreign keys to early.

Keep in mind too eloquent and relations don't rely on foreign keys, indexing works too with no performance impact unless you need sql cascade features of course

1 like
michaeldyrynda's avatar

If you are adding foreign keys, the referenced columns must match type definition exactly, and the field you're applying the index to must have valid data in it i.e. no values exist in the column you are creating a key on that does not exist in the foreign table.

If you have existing data, you'll need to do one of two things:

  1. Create and seed the foreign table with the existing index data first then create the migration to add the foreign keys
  2. Disable foreign key checks - DB::statement('SET FOREIGN_KEY_CHECKS=0'); before your migration, DB::statement('SET FOREIGN_KEY_CHECKS=1'); after it.

The first option is (usually) the preferable one; it ensures integrity of your foreign key constraints. The second option will allow you to run the (invalid) migrations, but may lead to issues down the track when updating rows that contain invalid key data.

Generally speaking, the foreign key will be an integer referencing a record in another table that has more information such as a text description or key. If you're using a string (which is a less efficient index than what an unsigned integer would be), I would consider the possibility of tackling option 1.

Before the migration runs:

  • Select all the unique values to be indexed
  • Insert them into the foreign table
  • Track the id of the inserted values
  • Update the relating table's data to match the new id values
  • Update the relating table's structure to be a nullable, unsigned integer
  • Apply the foreign key index to the relating table

You can have multiple Schema::table() calls inside your up and downmigration methods, so you can conceivable run all of the above within a single migration.

How the foreign data was created will determine the best way forward here.

lara9765's avatar

Thank you both for your answers, I apologize for taking so long to follow up - holidays and family! Anyway, I think I understand the general approach being outlined here, but I don't see how this works well with the artisan commands to execute the seed/migration.

Let's say I first make a migration to create the new foreign table, then a seed file to fill that table with the starting data needed to fulfill the default set in the second migration that adds the foreign key constraint to the primary table.

The issue I see is that if I now run artisan migrate, it will run the first to create the foreign table structure, then immediately run the second adding the constraint - but before I would have an option to run db:seed that would actually insert the data required to fulfill the constraint.

In this type of scenario is it normal to have seed data inserted during the execution of your migration script?

michaeldyrynda's avatar

Yeah, any dependent stuff like that I tend to put into the migration itself.

That said, you should still be able to create migrations that create foreign keys without any data in any tables. You'll only run into issues at that point if your foreign and reference fields don't match exactly. One common pitfall is having the foreign key not being unsigned() - where an auto-incrementing will always be unsigned.

If there's ever a change that you'll have a record in the reference table without establishing a relationship - say you create a post, but don't give it a status - then you should make the foreign key column nullable() as well.

Please or to participate in this conversation.