Why I can't do a migration with constraint foreign key - laravel

for a homework i have to do a todolist in laravel 7 i do my db but when i try to create a foreign key to the table task
and a table categories
the program fail with the formula :"SQLSTATE[HY000]: General error: 1824 Failed to open the referenced table 'categories' (SQL: alter table tasks add constraint tasks_category_id_foreign foreign key (categ
ory_id) references categories (id) on delete cascade)"
i've done it a lot of time but i don't understand why i doesn't work if someone could explains
the right way to do it

The migration is trying to create the tasks table before categories, the categories table still doesn't exist at the time it tries to create the FK.
Swap the order of your migrations changing its filename. Each migration file at database/migrations has a date-time before the name. Alter it so the tasks migration has a date greater than the categories one.

will Gus Costa is right and you should do as he says but i want to add that your
foreign key should be unsigned
and here you will find some good answers
"General error: 1005 Can't create table" Using Laravel Schema Build and Foreign Keys
// CREATING TABLE
Schema::create('table_name', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->integer('field_id')->unsigned; //for field that contains foreign key constraint
});
// FOREIGN KEY CONSTRAINT
Schema::table('stock', function ($table) {
$table->foreign('field_id')->references('id')->on('some_table')->onDelete('cascade')->onUpdate('cascade');
});

Related

How to add unique constraint to an existing foreignId column in laravel?

I have an existing table in database with data and I want to add unique constraints to the customer_id column in it.
I tried doing $table->foreignId('customer_id)->unique()->change(). But it doesn't seem to work. The same works for any non foreign fields like string and int.
Error:
SQLSTATE[42S21]: Column already exists: 1060 Duplicate column name 'customer_id' (SQL: alter table `partner_preferences` add `customer_id` bigint unsigned not
null)
foreignId() essentially creates a new column which in your case already exists.
The foreignId method is an alias of the unsignedBigInteger method:
laravel - difference between foreignId() and unsignedBigInteger()
Try this please:
Unique constraint
public function up()
{
// Change the 'table name' according to your needs.
Schema::table("employees", function (Blueprint $table) {
$table->unique('customer_id');
});
}
WARNING: Make sure the column(s) that you're applying this constraint to is actually unique. (Must have unique data.)
Otherwise, an error (SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1' for key 'XXXX_customer_id_unique') will be thrown.

How can I set index for json column in mysql 8 with laravel migration

I'm creating a project with laravel 6. One of my table column type is json.
The data format in the table column is like this:{age:30, gender:male, nation:china,...}. I am wondering if there is a way for me to set index for this column with laravel migration. my database version is mysql 8.0.21.
thank you!
I found this article very helpful for figuring this out. So for your example structure above, you might have a migration that looks like the following:
public function up(){
Schema::create('my_table', function(Blueprint $table){
$table->bigIncrements('id');
$table->json('my_json_col')->nullable();
$table->timestamps();
// add stored columns with an index
// index in this is optional, but recommended if you will be filtering/sorting on these columns
$table->unsignedInteger('age')->storedAs('JSON_UNQUOTE(my_json_col->>"$.age")')->index();
$table->string('gender')->storedAs('JSON_UNQUOTE(my_json_col->>"$.gender")')->index();
$table->string('nation')->storedAs('JSON_UNQUOTE(my_json_col->>"$.nation")')->index();
});
}
And this is equivalent to the following mysql statement:
create table my_table
(
id bigint unsigned auto_increment primary key,
my_json_col json null,
created_at timestamp null,
updated_at timestamp null,
age int unsigned as (json_unquote(json_unquote(json_extract(`my_json_col`, _utf8mb4'$.age')))) stored,
gender varchar(255) as (json_unquote(json_unquote(json_extract(`my_json_col`, _utf8mb4'$.gender')))) stored,
nation varchar(255) as (json_unquote(json_unquote(json_extract(`my_json_col`, _utf8mb4'$.nation')))) stored
)
collate = utf8mb4_unicode_ci;
create index my_table_age_index
on my_table (age);
create index my_table_gender_index
on my_table (gender);
create index my_table_nation_index
on my_table (nation);
And a simple view of the table after creation:
This example created actual stored columns, which for this scenario i think is what you would want. But you can also make virtual columns, which are created at query time instead of persistent columns, and you would just use the virtualAs function instead of the storedAs function in the migration.
These functions are documented in the Column Modifiers section of the Laravel migration docs, but it doesn't go into detail on JSON columns, this requires a bit more mysql knowledge.
I also found this article helpful for the mysql side of things for the JSON columns (SemiSQL).

Migrate from increments to bigIncrements from existing table

Im been searching around in laravel issues/forums on how to migrate changes from increments() to bigIncrements() using existing tables.
Error : SQLSTATE[HY000]: General error: 1833 Cannot change column 'id': used in a foreign key constraint 'account_users_acc_id_foreign' of table 'mydatabasename.account_users' (SQL: ALTER TABLE accounts CHANGE id id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL)
i know its because of the foreign table, then i tried to disabled constraint using Schema::disableForeignKeyConstraints()
Error : SQLSTATE[HY000]: General error: 1025 Error on rename of './mydatabasename/#sql-ea_201' to './mydatabasename/accounts' (errno: 150 - Foreign key constraint is incorrectly formed) (SQL: ALTER TABLE accounts CHANGE id id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL)
in my up() function:
i also tried to switch the order of the loops
Error: SQLSTATE[HY000]: General error: 1832 Cannot change column 'acc_id': used in a foreign key constraint 'account_users_acc_id_foreign' (SQL: ALTER TABLE account_users CHANGE acc_id acc_id BIGINT UNSIGNED DEFAULT NULL, CHANGE app_user_id app_user_id BIGINT UNSIGNED NOT NULL, CHANGE approved_by approved_by BIGINT UNSIGNED DEFAULT NULL, CHANGE rejected_by rejected_by BIGINT UNSIGNED DEFAULT NULL)
Is there any way to solve this?
References:
DBAL doctrine : https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/schema-manager.html
Could you:
Store each table's foreign keys in an array of [$table_name => $array_of_foreign_keys]
Drop all foreign keys
Change each table's id column to use bigIncrements
Change each foreign key column to be type unsigned big integer
Loop through the array from step 1, recreating all foreign keys (by looping through each foreign key by table name)
I tried to implement like #brice suggested. for now its working..
below is migration code. i dont know it is the best practice or not
this is my gist : https://gist.github.com/afiqiqmal/6518a2048246cd76c03bdee04ff87a82

Laravel auto-increment on non-primary field

There is my migration. I want id field being auto-incremented but not primary. Is it possible? This migration throws an exception Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key
Schema::create('tests', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('project_id');
$table->unsignedInteger('model_id');
$table->timestamps();
$table->dropPrimary('id');
$table->foreign('project_id')
->references('id')
->on('projects')
->onDelete('cascade');
$table->primary(['project_id', 'model_id']);
});
This is not Laravel error. you can't have two auto increment column in mysql table. (of course if you are using mysql) but I think none of any relational database gives you ability to have two auto increment columns.
But there is another way to do that.
look at this question
This is not a Laravel error but a MySQL error. As the error message says: it must be defined as a key. You are dropping the primaryKey constraint on the id column. You should try setting it as an index.
try the following $table->unsignedInteger('id')->index()->autoIncrement();. This will make it an AI integer and also an index but not a PK.

foreign key constraint: cannot drop table because other objects depend on it

I'm running migrations on Laravel 5.1 and am switching DBs from Mysql to Postgres.
Typically I could set foreign key checks to 0 prior to running down migrations as such:
- DB::statement('SET FOREIGN_KEY_CHECKS = 0');
- Do stuff
- DB::statement('SET FOREIGN_KEY_CHECKS = 1');
Postgres does not offer this.
In running down migrations, I get error:
Dependent objects still exist: 7 ERROR: cannot drop table table2 because other objects depend on it
DETAIL: constraint table1_table2_table1_id_foreign on table table1_table2 depends on table table2
HINT: Use DROP ... CASCADE to drop the dependent objects too. (SQL: drop table "table2")
Question: This complaint is curious to me as I set ->onDelete('cascade'); on the foreign key creations. Why is this happening?
Snippets:
Create Table1 Table:
...
public function down()
{
Schema::drop('table1_table2');
Schema::drop('table1');
}
Create Table2 Table (Called after table 1 migration):
...
public function down()
{
Schema::drop('table2');
}
Create Foreign Keys Table (last migration to be called)
public function up()
{
Schema::table('table1_table2', function(Blueprint $table)
{
$table->foreign('table1_id')->references('id')->on('table1')->onDelete('cascade');
$table->foreign('table2_id')->references('id')->on('table2')->onDelete('cascade');
});
...
}
public function down()
{
...
Schema::table('table1_table2', function(Blueprint $table)
{
$table->dropForeign('table1_id');
$table->dropForeign('table2_id');
});
...
}
This complaint is curious to me as I set ->onDelete('cascade'); on the foreign key creations. Why is this happening?
The key term here is "on delete" - when you delete a row from one table, that option will determine whether rows with foreign keys referencing that row will be deleted as well.
However, your change script is not deleting rows, it is dropping the table. This is therefore a different event, not effected by the ON DELETE option on the foreign key.
The CASCADE mentioned in the hint is a keyword on the DROP TABLE statement, discussed in the manual under "Dependency Tracking":
Key quotes:
When you create complex database structures involving many tables with foreign key constraints, views, triggers, functions, etc. you implicitly create a net of dependencies between the objects. For instance, a table with a foreign key constraint depends on the table it references.
and:
if you do not want to bother deleting all the dependent objects individually, you can run DROP TABLE products CASCADE; and all the dependent objects will be removed, as will any objects that depend on them, recursively. In this case, it doesn't remove the orders table, it only removes the foreign key constraint.
and:
Almost all DROP commands in PostgreSQL support specifying CASCADE.
For my case; you can get this error with this scenario;
If your table seems rollbacked in migration table (ex: maybe forget drop function first time) but table still exists on database you can get this error. migrate:fresh command will fail with given error message for this scenario.
You can drop table manually or add a row to migration table with that migration name and everything will be working normally.

Resources