laravel and artisan error cannot add foreign key on migrate - php

I know this has been answered many times as i saw many answers suggestions when i wrote the question, but none of them actually get me the correct answer.
When i try to add new field to the table and use artisan migrate in laravel i get an error
General error: 1215 Cannot add foreign key constraint (SQL: alter table `pages` add constraint pages_page_status_foreign foreign key (`page_status`) references `page_status` (`status_value`))
But i don't understand why do i get an error when before i have done the same thing and with no errors, only answer i could think of is that it's not possible to have two or more foreign keys on same table. Here is my migrate function
class CreateStatusTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('page_status', function($table) {
$table->increments('id');
$table->string('status_name');
$table->integer('status_value')->unsigned();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('page_status');
}
}
This is for status table, and now i have to add new field to pages table using this.
class AddStatusToPagesTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::table('pages', function($table) {
$table->integer('page_status')->unsigned();
$table->foreign('page_status')->references('status_value')->on('page_status');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('pages', function($table) {
$table->dropForeign('pages_page_status_foreign');
});
}
}
And this is where the error appears, for some reason it does not let me set foreign key on page_status field into pages table.
I am using xampp installation on my localhost and in phpmyadmin i can see that table is set to innodb, actually everything is set to innodb by default. and inside my database in pages table looking at page_status field i have first to set it index than i am able to use relations. why it's not set to index i don't know but i already have on that table id as an index and author as foreign key.
Before this i also added few fields when creating pages table and using this
class CreatePagesTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('pages', function($table){
$table->increments('id');
$table->integer('author')->unsigned();
$table->foreign('author')->references('id')->on('users');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('pages');
}
}
I did not had any problems when using migrate, it worked well and author is set as foreign key.
So why i can't add new foreign key now ?

This is probably due to the fact that a foreign key can't be added if the current records do not meet the condition. If page_status is optional make the field nullable:
$table->integer('page_status')->unsigned()->nullable();
If you don't want to make it nullable and are going to insert valid foreign key values later you can disable foreign key checks temporarily:
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
Schema::table('pages', function($table) {
$table->integer('page_status')->unsigned();
$table->foreign('page_status')->references('status_value')->on('page_status');
});
DB::statement('SET FOREIGN_KEY_CHECKS = 1');

I found where the problem is, after adding a new field to the table you have to set the index on that field, so there is a one step further on migration for setting the field as index.
I still do not understand why in my first migration when i added "author" field it made it automatically as an index but for this one i got it working by setting index additionally like this:
Schema::table('pages', function($table) {
$table->integer('status')->unsigned()->nullable();
$table->index('status');
$table->foreign('status')->references('status_value')->on('page_status');
});

Related

SQLSTATE[HY000]: General error:Can't create table

I am using Laravel 8.21.0 on a CentOS 8 server. I am using mariaDB. I have 3 tables: tests, students and grades.
I am trying to set foreign key of tests and students on the grades table.
However, when I run my migrations, I get errorno 150: Foreign key constraint is incorrectly formed.
Here are my migrations:
Grades table:
class CreateGradesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('grades', function (Blueprint $table) {
$table->id('id')->unique();
$table->unsignedInteger('student_id');
$table->string('test_id');
$table->foreign('test_id')->references('id')->on('tests');
$table->foreign('student_id')->references('id')->on('students');
$table->date('testDate');
$table->integer('testCount');
$table->integer('vocabScore');
$table->integer('readingScore');
$table->integer('listeningScore');
$table->integer('rawTotal');
$table->integer('adjustVocabScore');
$table->integer('adjustReadingScore');
$table->integer('adjustlisteningScore');
$table->integer('adjustTotal');
$table->string('passOrfail');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('grades');
}
}
Student Table migrations:
class CreateStudentsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('students', function (Blueprint $table) {
$table->integer('id')->unique();
$table->string('name');
$table->date('DOE');
$table->string('belongsTo');
$table->string('country');
$table->string('level');
$table->string('year');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('students');
}
}
Test table:
class CreateTestsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('tests', function (Blueprint $table) {
$table->string('id')->unique();
$table->string('testName');
$table->string('level');
$table->integer('vocabFullScore');
$table->integer('readingFullScore');
$table->integer('listeningFullScore');
$table->integer('totalFullScore');
$table->integer('vocabPassScore');
$table->integer('readingPassScore');
$table->integer('listeningPassScore');
$table->integer('totalPassScore');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('tests');
}
}
The bizzare thing is that the migrations table gets created successfully when I run it on localhost WAMP server but it throws me an error on CENTOS server. Does anyone have any idea on how to solve this?
Things that I have tried doing but did not work:
・Changed database to InnoDB by specifying 'engine' => 'InnoDB' on each model.
・Made sure that the order of migration is correct. First migrated student table, then tests and lastly grades.
・Ensure that the data type of the foreign key is correct on grades table.
Any ideas would be greatly appreciated. :'(
Edit: I set the foreign key type of student_id as integer.
In grades table migration:
$table->integer('student_id');
$table->foreign('student_id')->references('id')->on('students');
In students table migration:
$table->integer('id')->unique();
After doing this I am getting a new error:
Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for right syntax to use near ')' at line 1 (SQL: alter table 'grades' add constraint 'grades_test_id_foreign' foreign key ('test_id') references 'tests' ())
at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678
catch(Exception $e){
throw new QueryException(
$query, $this->prepareBindings($bindings),$e
);
}
+9 vendor frames
database/migrations/2021_01_13_064711_create_grades_table.php:54
Illuminate\Support\Facades\Facade::__callStatic()
+32 vendor frames
artisan:37
Illuminate\Foundation\Console\Kernel::handle()
Change
$table->unsignedInteger('student_id');
to
$table->integer('student_id');
in grades table to match the datatypes to form a constraint.

Alter ENUM column and add value to that column in Laravel

I have a MySQL database with a table called user_level_attempt. That table has a ENUM type column with ['PROGRESSED', 'STOPPED', 'COMPLETED'] values. I need to write a migration to add another value (let's say 'PASSED') to that column. After adding, it'll look like this, ['PROGRESSED', 'STOPPED', 'COMPLETED', 'PASSED]. How can I do that in Laravel?
I tried the following solution but it doesn't seem like a good practice/solution.
/**
* Schema table name to migrate
* #var string
*/
public $set_schema_table = 'bt_user_level_attempt';
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::table($this->set_schema_table, function ($table) {
$table->dropColumn('status');
});
Schema::table($this->set_schema_table, function ($table) {
$table->enum('status', ['PROGRESS', 'STOPPED', 'COMPLETED', 'PASSED'])->default('PROGRESS')->after('effective_time_spend');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table($this->set_schema_table, function ($table) {
$table->dropColumn('status');
});
Schema::table($this->set_schema_table, function ($table) {
$table->enum('status', ['PROGRESS', 'STOPPED', 'COMPLETED'])->default('PROGRESS')->after('effective_time_spend');
});
}
Thank you.
After all, I figure out to find a solution. Thanks to all fellows for enlightening me. :)
/**
* Schema table name to migrate
* #var string
*/
public $set_schema_table = 'bt_user_level_attempt';
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
DB::statement("ALTER TABLE ".$this->set_schema_table." MODIFY COLUMN status ENUM('PROGRESS', 'STOPPED', 'COMPLETED', 'PASSED') NOT NULL DEFAULT 'PROGRESS'");
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
DB::statement("ALTER TABLE ".$this->set_schema_table." MODIFY COLUMN status ENUM('PROGRESS', 'STOPPED', 'COMPLETED') NOT NULL DEFAULT 'PROGRESS'");
}
You should try with DB::statement method:
Use the DB::statement method:
DB::statement("ALTER TABLE ".$this->set_schema_table." CHANGE COLUMN status ENUM('PROGRESS', 'STOPPED', 'COMPLETED','PASSED') NOT NULL DEFAULT 'PROGRESS'");
Please refer to documentation:
Only the following column types can be "changed": bigInteger, binary, boolean, date, dateTime, dateTimeTz, decimal, integer, json, longText, mediumText, smallInteger, string, text, time, unsignedBigInteger, unsignedInteger and unsignedSmallInteger.
So the ENUM cannot be modified using simple migration syntax. But you can migrate your column using a custom statement:
DB::statement("ALTER TABLE ".$this->set_schema_table." MODIFY COLUMN status ENUM('PROGRESS', 'STOPPED', 'COMPLETED','PASSED') NOT NULL DEFAULT 'PROGRESS'");
Add this code in your migration file before the schema.
public function __construct()
{
\Illuminate\Support\Facades\DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
}
try something like :
edit
$table->enum('converted', array('yes','no'))->default('no');

Remove primary key and auto-increment in database using migration in laravel

I have table with primary key and auto-increment field, I want make new migration to drop primary key index and also remove the auto increment field. How can i achieve this.
I created new migration as
public function up()
{
Schema::table('tbl_message_read_state', function (Blueprint $table) {
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('tbl_message_read_state', function (Blueprint $table) {
$table->dropPrimary('message_id');
$table->unsignedInteger('message_id');
});
}
It gave me error in command as [Illuminate\Database\QueryException]
SQLSTATE[42S21]: Column already exists: 1060 Duplicate column name 'message _id' (SQL: alter table tbl_'message_read_state' add 'message_id' int unsigned not null)
Whats Wrong ?????
Blueprint class offers dropPrimary methods that allow you to remove primary key.
public function down()
{
Schema::table('table', function (Blueprint $table) {
$table->dropPrimary();
$table->unsignedInteger('id'); // for removing auto increment
});
}
This did it for me:
Schema::table('table_name', function (Blueprint $table) {
// Make AI field `id` unsigned otherwise SQL
// will throw error when you try to remove it
$table->integer('id')->unsigned()->change();
$table->dropColumn('id');
// If there was a foreign on message_id, make sure to remove it
$table->dropForeign('table_name_message_id_foreign');
$table->dropPrimary('message_id');
$table->dropColumn('message_id');
}
The dropPrimary method only removed the primary key, not the column. You would have to do:
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('tbl_message_read_state', function (Blueprint $table) {
$table->dropPrimary('message_id');
$table->dropColumn('message_id');
$table->unsignedInteger('message_id');
});
}
Or instead of dropping and re-creating the column, you can use change in Laravel 5.
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('tbl_message_read_state', function (Blueprint $table) {
$table->dropPrimary('message_id');
$table->integer('message_id')->unsigned()->change();
});
}
You can try this
$table->dropPrimary('id_primary');
Drop the primary key:
$table->dropPrimary( 'id' );
Reference
Use a simple drop column
$table->dropColumn('id');
https://laravel.com/docs/4.2/schema#dropping-indexes
tabel_name
column
index $table->dropPrimary('users_id_primary');

laravel : seeder with foriegn keys

in my database i have already tables namely: notifications table, statuses table, they have many to many relation, that is why i have a pivot table called notification_status. i created them with migrations and seed them with a seeder, all works fine. now i realize that i need one extra table which has many to one relation with notifications table(natification->hasMany->alertfrequency). when i tried to migrate it, it did allow me to do so.
here is my notification table
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateNotificationsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('notifications', function (Blueprint $table) {
$table->increments('id');
$table->string('website_url');
$table->string('email');
$table->string('slack_channel');
$table->string('check_frequency');
$table->string('alert_frequency');
$table->string('speed_frequency');
$table->boolean('active');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('notifications');
}
}
and the alert frequency table, the new table i want to add,
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAlertFrequenciesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('alertFrequencies', function (Blueprint $table) {
$table->increments('id');
$table->integer('notification_id');
$table->foreign('notification_id')
->references('id')->on('notifications')
->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('alertFrequencies');
}
}
when i tried to add i get the following constrain
[Illuminate\Database\QueryException]
SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alter table `alertFrequencies` add constraint `alertfrequencies_notification_id_foreign` foreign key (`notification_
id`) references `notifications` (`id`) on delete cascade)
any one with a idea or suggestion. i appreciate all idea and suggestions.
From #AlexeyMezenin's answer, update based on Documentation:
Replace
$table->integer('notification_id')->unsigned();
with
$table->unsignedInteger('notification_id');
First thing you should do is to add unsigned(), because id is unsigned too:
$table->integer('notification_id')->unsigned();
If it'll not help, divide key creation and adding a foreign key constraint:
Schema::create('alertFrequencies', function (Blueprint $table) {
$table->increments('id');
$table->integer('notification_id')->unsigned();
$table->timestamps();
});
Schema::table('alertFrequencies', function (Blueprint $table) {
$table->foreign('notification_id')->references('id')->on('notifications')->onDelete('cascade');
});
You need to migrate notifications table prior to alertFrequencies table as notification_id of alertFrequencies is referencing id of notifications table.
And you need to change $table->integer('notification_id'); to $table->integer('notification_id')->unsigned(); as you cannot accept minus ids as foreign key
And also make sure you have set your table engine set to InnoDB in your `config/database.php'.
$table->engine = 'InnoDB';

Handling foreign checks in Laravel 5 migrations

I have a calls table with user_id column and a foreign key constrain. So, user_id is related to users table.
I made a migration which deletes the column and foreign key constrain. The up method works fine. However, when it comes to down it breaks down because I add a user_id column and then restore the constrain, but the values in the column have nothing to do with real data so I got 150 MySQL error.
I've found a solution which didn't work for me. The solution is to temporarily disable foreign key constrains. Here is my migration file:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class RemoveUseridFromCalls extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::table('calls', function(Blueprint $table)
{
$table->dropForeign('calls_user_id_foreign');
$table->dropColumn('user_id');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
\DB::statement('SET FOREIGN_KEY_CHECKS = 0');
Schema::table('calls', function(Blueprint $table)
{
$table->integer('user_id')->unsigned();
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('set null');
});
\DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
}
But I still get the same error.
So, the questions are:
What would you do if you need to remove user_id from columns table as it's not needed anymore, however you have to write the down method as well as up?
Why disabling foreign key checks doesn't work?
I use InnoDB as table type for all my tables.
You can simply use onDelete('cascade').
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('calls', function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->timestamps();
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade'); // use 'cascade'
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('calls');
}
}
onDelete('cascade') tells Laravel to delete a calls row when it's corresponsding foreign key user_id is deleted.
I had the same problem and fixed it by separating migration into several files. First one creates the table, the others add columns for the keys and the keys themselves.
After that everything starts working like a charm.
To check foreign key in table while dealing with migration can be as follow:
$keyExists = DB::select(DB::raw('SHOW KEYS FROM TABLE_NAME WHERE Key_name=\'YOUR_FOREIGN_KEY_NAME\''));
if(!$keyExists){
$table->foreign('app_id')
->references('id')
->on('apps')
->onDelete('cascade');
}

Categories