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

vincej's avatar
Level 15

Foreign Keys Incompatibility Problem

Hi All!

I have an two existing tables created manually: Quotations and Products.

I wish to create a pivot table, 'quotations_product' joining the two. Every time I run my migration I am getting an incompatibly error. I have spent 2 days trying to find the incompatibility, but it escapes me.

Question: is there some clever tool or trick for finding incompatibilities??

Big Thanks!!

0 likes
37 replies
LaryAI's avatar
Level 58

Hello!

When dealing with foreign key incompatibility issues, the most common problems are related to mismatched data types or collations between the primary keys in the parent tables and the corresponding foreign keys in the child table. Here are some steps you can take to troubleshoot and resolve the incompatibility:

  1. Check Data Types: Ensure that the data types of the primary key columns in the Quotations and Products tables match exactly with the data types of the corresponding foreign key columns in the quotations_product pivot table.

  2. Check Collations: If the primary keys are of a string type (like VARCHAR or CHAR), ensure that the collation settings are the same for both the primary keys and the foreign keys.

  3. Check Indexes: The columns you are referencing as foreign keys should be indexed in the parent tables. Usually, primary keys are indexed by default.

  4. Check Unsigned: If you are using integer-based keys, make sure that both the primary keys and foreign keys are either both signed or both unsigned.

  5. Check Default Values: Foreign key columns should not have a default value that conflicts with the primary key constraints.

Here's an example of how you might define a migration for the quotations_product pivot table in Laravel, assuming that id columns in Quotations and Products are unsigned integers:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateQuotationsProductTable extends Migration
{
    public function up()
    {
        Schema::create('quotations_product', function (Blueprint $table) {
            $table->unsignedBigInteger('quotation_id');
            $table->unsignedBigInteger('product_id');
            
            $table->foreign('quotation_id')->references('id')->on('quotations')->onDelete('cascade');
            $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
            
            // Optional: Define a primary key for the pivot table
            $table->primary(['quotation_id', 'product_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('quotations_product');
    }
}

If you're still encountering issues, you can use the following tools or methods to help identify the problem:

  • Laravel's Artisan Commands: Use php artisan migrate:status to check the status of your migrations and php artisan migrate to run migrations with verbose error output.
  • Database Logs: Check the database error logs for more detailed information about the foreign key constraint failure.
  • Database GUI Tools: Tools like phpMyAdmin, Sequel Pro, or TablePlus can help you visually inspect the table structures and identify discrepancies.

If you provide the exact error message and the structure of your Quotations and Products tables, I could give you a more precise solution.

Tray2's avatar

When you create the foreign key, you need to have the same data type in the foreign key field as the field you are referreing too.

This will explain it in more detail https://tray2.se/posts/sqlerrm#the-foreign-key-issue-aka-sqlstate-hy000

However, make sure that your table_id has the same data type as the id column id the table.

id unsigned big integer === user_id usigned big integer

vincej's avatar
Level 15

@Tray2 Thanks for the article, none of these points are discussed in the docs nor on Jeffrey's tutorial. I think part of my problem is that I manually created the two tables and I am now trying to insert a pivot table with migrations. I might just delete the tables and create everything with migrations. That way I will know that all the defaults are correct.

vincej's avatar
Level 15

@Tray2 Up until now, ( ie forever) I have created my tables manually without many to many relationships. question: when creating a foreign key must the foreign key and / or the targeted reference key be a primary?? I ask as my row id currently are primary and auto increment, but the target reference is not a primary.

vincej's avatar
Level 15

@Tray2 Thanks for that! My application has evolved over time. I never expected to have to have a many-to-many. Now I do. I will restructure my tables with the proper id's ! Also I have to get used to migrations. I've been doing things the old school way.

Snapey's avatar

Hi Vince

Note that your pivot table should be the singular names of the two tables. So in your case it should be quotation_product.

Also, if starting to use migrations, it might help to squash your migrations.

https://laravel.com/docs/10.x/migrations#squashing-migrations

This will create a file that contains the current state of your database. You can then start with a clean slate migrations wise and add migrations from there.

When creating a pivot table, you can use the following technique to ensure key compatibility

    {
        Schema::create('quotation_product', function (Blueprint $table) {
            $table->foreignIdFor(App\Models\Quotation::class);
            $table->foreignIdFor(App\Models\Product::class);            
        });
    }
1 like
vincej's avatar
Level 15

@Snapey thanks for that! I will have a look. One thing I have learned the hard way about migrations is that when ever you do a refresh of your migrations you loose all your data! Who invented this??

jlrdw's avatar

@vincej always backup, fresh is for development. Not good on production.

2 likes
vincej's avatar
Level 15

@jlrdw Ok question: I am in development. Yes, I know I can use a factory / faker to populate my tables. But does faker do product specific data like for HVAC, Plumbing, roofing etc ? I didn't think it did, so I'm a bit stuck with migrating my products table, no?

jlrdw's avatar

I also use real data, but of course it's backed up for when I go to production.

chiefguru's avatar

Hi @vincej another minor thing, back in ancient times, if you didn't specify the pivot table and keys, Eloquent expected to see the table name in alphabetic order, so product_quotation instead of quotation_product

Haven't made any recently so I don't know if it is still the case.

vincej's avatar
Level 15

HI All, Back again! I still have never ending incompatibility errors between my pivot table and my target tables.

My target tables have existing columns in them. Does it matter ??? Is this this problem???

Last night I created two fresh test tables and an accompanying pivot / join table. The test tables were empty excepting the primary column. Everything worked fine. I have cross checked the structure of my test tables with my two live tables and their structures are identical, excepting the reality that my live tables do have a series of additional columns.

vincej's avatar
Level 15

Hi Martin, Thanks for helping out!

Here is the error for the products table. I get the same error for the quotations table:

[HY000][3780] Referencing column 'product_id' and referenced column 'product_id' in foreign key constraint 'products_quotations_products_product_id_fk' are incompatible.

here is the pivot table. HOWEVER, the table does nto show any foreign keys as obviously they are rejected.

mysql> desc products_quotations;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| reference_id | varchar(30) | NO   |     | NULL    |       |
| product_id   | varchar(30) | NO   |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

Here is the products table. Primary key is product_id:


mysql> desc products;
+--------------+---------------+------+-----+-------------------+-------------------+
| Field        | Type          | Null | Key | Default           | Extra             |
+--------------+---------------+------+-----+-------------------+-------------------+
| product_id   | varchar(30)   | NO   | PRI | NULL              |                   |
| product_name | varchar(50)   | NO   |     | NULL              |                   |
| status       | varchar(20)   | NO   |     | active            |                   |
| description  | varchar(50)   | YES  |     | NULL              |                   |
| price        | decimal(10,2) | NO   |     | NULL              |                   |
| cost_price   | decimal(10,2) | YES  |     | NULL              |                   |
| category_id  | int unsigned  | NO   |     | NULL              |                   |
| color        | varchar(50)   | YES  |     | NULL              |                   |
| size         | varchar(20)   | YES  |     | NULL              |                   |
| coverage     | int           | YES  |     | NULL              |                   |
| tax_rate     | int           | YES  |     | NULL              |                   |
| weight       | varchar(5)    | YES  |     | NULL              |                   |
| barcode      | int           | YES  |     | NULL              |                   |
| qrcode       | int           | YES  |     | NULL              |                   |
| box_quantity | int           | YES  |     | NULL              |                   |
| supplier_id  | int           | NO   |     | NULL              |                   |
| alternate_id | int           | YES  |     | NULL              |                   |
| location_id  | int           | YES  |     | NULL              |                   |
| created_at   | timestamp     | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
| updated_at   | timestamp     | YES  |     | NULL              |                   |
+--------------+---------------+------+-----+-------------------+-------------------+
20 rows in set (0.00 sec)

Here is the Quotations table. It has a primary key as reference_id:

mysql> mysql> desc quotations;
+-----------------+---------------+------+-----+-------------------+-------------------+
| Field           | Type          | Null | Key | Default           | Extra             |
+-----------------+---------------+------+-----+-------------------+-------------------+
| reference_id    | varchar(30)   | NO   | PRI | NULL              |                   |
| material_qty    | int           | YES  |     | NULL              |                   |
| quote_name      | varchar(100)  | YES  |     | NULL              |                   |
| customer_id     | varchar(10)   | YES  |     | NULL              |                   |
| lead_product    | varchar(40)   | YES  |     | NULL              |                   |
| materials_total | varchar(12)   | YES  |     | NULL              |                   |
| services_total  | varchar(12)   | YES  |     | NULL              |                   |
| start_date      | varchar(20)   | YES  |     | NULL              |                   |
| line_cost       | varchar(10)   | YES  |     | NULL              |                   |
| subtotal        | varchar(10)   | YES  |     | NULL              |                   |
| discount        | tinyint       | YES  |     | NULL              |                   |
| discount_value  | varchar(10)   | YES  |     | NULL              |                   |
| netofdiscount   | varchar(10)   | YES  |     | NULL              |                   |
| gst             | varchar(10)   | YES  |     | NULL              |                   |
| total           | varchar(11)   | YES  |     | NULL              |                   |
| job_firstname   | varchar(50)   | YES  |     | NULL              |                   |
| job_lastname    | varchar(50)   | YES  |     | NULL              |                   |
| job_email       | varchar(50)   | YES  |     | NULL              |                   |
| job_telephone   | varchar(15)   | YES  |     | NULL              |                   |
| job_prop_number | varchar(20)   | YES  |     | NULL              |                   |
| job_address     | varchar(100)  | YES  |     | NULL              |                   |
| job_city        | varchar(50)   | YES  |     | NULL              |                   |
| job_province    | varchar(50)   | YES  |     | NULL              |                   |
| job_postcode    | varchar(10)   | YES  |     | NULL              |                   |
| job_country     | varchar(50)   | YES  |     | NULL              |                   |
| status          | varchar(50)   | NO   |     | open              |                   |
| comment         | varchar(1000) | YES  |     | NULL              |                   |
| created_at      | timestamp     | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
| updated_at      | timestamp     | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
+-----------------+---------------+------+-----+-------------------+-------------------+
29 rows 

vincej's avatar
Level 15

@martinbean because in the case of both reference_id for the quotation table and product_id for the products table I need these id's to be alpha numeric. should I change them to something else?

vincej's avatar
Level 15

@martinbean In my test tables I created last night I also used varchar without ill effect.

vincej's avatar
Level 15

@martinbean Here is the sql phpstorm generates:

alter table products_quotations
    add constraint products_quotations_products_product_id_fk
        foreign key (product_id) references products (product_id)
            on update cascade on delete cascade;

Once I get it working I will convert it to a migration

Snapey's avatar

@vincej No you need a product_id to be varchar (if it is visible and referenced externally), but there should be a seperate big integer primary key that is ensured to be unique and is automatically handled by autoincrement.

1 like
vincej's avatar
Level 15

@Snapey I don't follow. My app creates an alpha numeric reference to a quotation. So at the same time I repeat and save that reference into the join table for each of the products that were quoted on. The pivot / join table has a link back to the products table. Each reference_id is unique, but not autoincremented. Is that not how things work?

Snapey's avatar

exactly the same. I'm saying your argument that product_id must be alpha numeric does not hold true and is not what most do, hence @martinbean reaction

vincej's avatar
Level 15

@Snapey Agreed. Product_id's and quotation_id's are not required to be alpha numeric. My issue is are primary and foreign keys required to be numeric only? I did not think so. I am at a total loss as to why things are not working when my small test tables I built last night work fine. I have copied the structure the same. I have also deleted all the contents out of the tables, so I just have columns.

Snapey's avatar

@vincej you are only adding the new pivot table with 0 rows and the products table with existing data? Its just that I have seen errors like this when trying to create constraints and one table has values that are not in the other

vincej's avatar
Level 15

@Snapey No, I have emptied both tables of all their data. I have just retained the column headings. I did an experiment where I backed up, then deleted all the columns and attempted to create the foreign keys. It still failed. FYI I am building these tables with PHPStorm DB tool. I do not want to complicate the issue with migrations which I am not very familiar with.

I also noticed that the collation for my tables is 'utf8mb3_unicode_ci', which was Laravels old default. It is now 'utf8mb4_unicode_ci', That should not cause a problem as I am not Japanese; but still 'utf8mb3_unicode_ci', is now deprecated since MySQL went past version 8.0. Heaven knows how I might upgrade all my table to the new collation.

Tray2's avatar
Tray2
Best Answer
Level 73

@vincej That is most likely the problem, the mb3 and mb4 are different, you should always use the same collation in the database, or you will get strange issues.

utf8mb3 uses a maximum of three bytes per character. utf8mb4 uses a maximum of four bytes per character.

So they are definately incompatible

https://dev.mysql.com/doc/refman/8.3/en/charset-unicode-conversion.html

I still don't understand why you build your tables in PHPStorm, and not use migrations. If you think they are complex to write I suggest giving Laravel Idea a spin, it's a really awesome plugin for PHPStorm. It can create model,controller,migration,factory, policies, form requests, and rest API controllers in just a few seconds, a really sweet no code tool, just use the create model wizard.

1 like
vincej's avatar
Level 15

@Tray2 thanks for that Tray! I agree it could be the cause of the incompatibility. So I checked all the tables and they are all mb3 except for the three new ones. So I changed those 3 new ones to mb3 and tried again. Sadly it failed. I will change my config / DB to mb3. I have not done that yet.

Re Migrations - I don't know all the default syntax so it was a pain, adding more complexity and more chances to screw up. PHPSStorm DB tool is very easy to use, takes all the SQL syntax away and easy to use. BUT ... of course I use both a MacBook AND a Linux server. Moving DB changes from one to the other would be much better with migrations. If Laravel Idea works well I will try it. I have read that it is great, but very expensive. Do you use it? Many Thanks!

Tray2's avatar

@vincej Yes I use it, it gives some nice perks to the editor and speeds up the development a lot.

vincej's avatar
Level 15

@Tray2 I just had a look - it's not expensive. I don't know what people are talking about. I'll give it a go, there is a 30 day trial.

2 likes
Tray2's avatar

@vincej For some it is, but if you can afford it, it is a good investment.

vincej's avatar
Level 15

@Tray2 I have hunted around google, and I can not find find whether ONE licence allows me to install it on both my MacBook AND my Linux server like Storm allows. Do you know? Thanks!

Tray2's avatar

@vincej Sorry no, but I can't find anything that disallows it either.

vincej's avatar
Level 15

@tray2 HALLELUJAH!!!! SUCCESS!! Best reply go to you sir!

I just changed the Laravel config / DB collation and character set to mb3 and I now have successful foreign keys!!!!!

I want to thank everyone who pitched in at different moments, special mention to @snapey and @martinbean!!!

Now I have one new problem - how to update ALL my tables to mb4 ..... I have already checked and tried a couple of suggestions none of which worked. So ... I might have to manually change them all.

Please or to participate in this conversation.