add 'on update' & 'on delete' foreign key constraints with laravel migrations - php

I am making my table like this...
Schema::create('matched_merchants', function (Blueprint $table)
{
$table->increments('id')->unsigned();
$table->integer('merchant_id')->unsigned();
$table->integer('offer_id')->unsigned();
$table->foreign('merchant_id')->references('id')->on('merchants')->onUpdate('cascade')->onDelete('restrict');
$table->foreign('offer_id')->references('id')->on('offers')->onUpdate('cascade')->onDelete('restrict');
});
Im adding 2 foreign keys both have onUpdate and onDelete constraints but only the update constraint gets added.
If i delete the onUpdate, it will correctly add the onDelete constraint.
I cannot add them separately because i get the error of duplicate key.
I could add them manually with a raw sql statement but if theres a right way to do it id rather do that.

For some reason, if you set the columns as nullable(), Laravel sets the foreign keys correctly.
That said, I can see that causing other problems, so I recommend doing the raw SQL statement.

Related

Column 'tax_id' cannot be NOT NULL in Laravel

I am working in a project where a table has a foreign key called tax_id, the problem is that in some point another migration was created to change the tax_id to nullable.
Schema::table('products', function (Blueprint $table) {
$table->unsignedInteger('tax_id')->nullable()->change();
});
I realize this after I wrote other migrations and tried to run them, it throws Column 'tax_id' cannot be NOT NULL, it seems that in the "project" that migration was run, so I can't just delete the file, how can I run my migrations without the error? I have tried to remove the foreign key, but nothing worked.
I'll assume you're working with MySQL InnoDB because you did not specify the database type/engine.
You could try different approaches:
1) If yuo're in hurry, disable foreign keys, do the update, enable the foreign key. You should then correct the problem of course.
2) Add set tax_id to '' where tax_id is null and then do the migration
3) Document yourself about the database engine and implement a correct solution
Giacomo

How to drop foreign key using Laravel migrations?

I've tried to drop a an InnoDB table that holds a foreign key using Laravel Migrations but I found out that I need to drop the foreign first but what I've read on the doc and on articles doesn't work.
Here's the portion of the code creating the problem :
Schema::table('admin_admin_action', function(Blueprint $table) {
$table->dropForeign(['admin_action_id']);
$table->dropColumn('admin_action_id');
$table->dropForeign(['admin_id']);
$table->dropColumn('admin_id');
});
And here's the error code
Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1091 Can't DROP 'admin_admin_action_admin_action_id_foreign'; check that column/key exists (SQL: alter table `admin_admin_action` drop foreign key `admin_admin_action_admin_action_id_foreign`)
This table is the result of a many to many association and here are the foreign keys
Schema::table('admin_admin_action', function($table) {
$table->unsignedInteger('admin_id');
$table->unsignedInteger('admin_action_id');
$table->foreign('admin_id')->references('id')->on('admin');
$table->foreign('admin_action_id')->references('id')->on('admin_actions');
});
I've also tried specifying the full foreign key name but it says that the key/column doesn't exist though it does exist in the database.
I'm doubting this is a migration's issue because the migration runs smoothly but that's not the case for the rollback.
Edit: The foreign keys are respectively named
admin_admin_action_admin_id_foreign
admin_admin_action_admin_action_id_foreign
Help!
I found the solution !
You're right ! It's a mis-conception issue I've created a "admin_admin_action" and an "admin_action_admin" tables. So I've been trying to the table that doesn't have the foreign keys but has the same position in the database. They're duplicated ! Thanks

Laravel 5.1 QueryException when trying to delete a project

I am building something that allows users to upload their graphical work and other users can comments on the projects and like other projects.
Now when a user wants to delete their own project it works but as soon as the project has comments or likes I get this error:
Integrity constraint violation: 1451
Cannot delete or update a parent row: a foreign key constraint fails
(`scotchbox`.`comments`, CONSTRAINT `comments_on_projects_foreign` FOREIGN KEY (`on_projects`)
REFERENCES `projects` (`id`)) (SQL: delete from `projects`
where `id` = 31 and `user_id` = 32)
I assume this can be resolved by deleting the comments from the comments table and the likes from the likes table aswell? but I honestly have no idea how to fix this issue with the foreign keys.
I think I need a way to delete the comments and likes of the project before deleting the project itself. Is this possible from the destroy function of my ProjectsController?
This is my destroy function for deleting the projects:
public function destroy($id)
{
$input = Request::all();
Project::whereId($id)->whereUserId(Auth::user()->id)->delete();
return redirect('projects/');
}
When you have a foreign key, you can choose the behavior of deletion.
You have: Users, Projects
User has many Projects
so that means in the Projects schema you will have something similar to this
Schema::create('projects', function(Blueprint $table){
// table fields
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
});
In this case, you have established a relation without saying "how to behave" if a deletion occurs on the parent (User), that means if you attempt to delete the user, the query exception will be thrown to protect you.
So, if this is the case, you will have to find all the children (Projects) of that parent (User), and delete them before you are able to delete the Parent.
the 2nd solution would be adding onDelete() behavior:
Schema::create('projects', function(Blueprint $table){
// table fields
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
This means, if you delete the Parent (User) it will automatically find all of the children (Projects) and delete them one by one for you.
Finally, please note that i am using Parent Children analogy in order to clarify the situation, where this analogy only fits in the One to Many relations.
Please avoid using the analogy when you are surrounded by Databasists to protect yourself from further consequences.
The user you try to delete commented on a project. Delete his comments before deleting the user. Or there is a comment on the project you try to delete.
Fixed it by adding onDelete('cascade') to my migrations like this:
Comments migration:
$table->foreign('on_projects')->references('id')->on('projects')->onDelete('cascade');
Likes migration:
$table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');

Laravel Eloquent truncate - Foreign key constraint

I am having some issues with deleting data using Laravel 5. I seem to be stuck on a 'foreign key constraint', while I don't see why.
In my current database model I have a datapoints table, which has a foreign key to the sensors table (datapoints.sensors_id -> sensor.id).
The code I am trying:
Route::get('/truncateData', function() {
DB::table('datapoints')->truncate();
DB::table('sensors')->truncate();
return 'Done...';
});
The result:
SQLSTATE[42000]: Syntax error or access violation: 1701 Cannot
truncate a table referenced in a foreign key constraint
(alerting.datapoints, CONSTRAINT datapoints_sensor_id_foreign
FOREIGN KEY (sensor_id) REFERENCES alerting.sensors (id))
(SQL: truncate sensors)
I would understand this constraint if the order would be inverse (first deleting sensors), but when datapoints is empty, there should be no problem deleting sensors? I have also tried:
DB::table('datapoints')->delete();
DB::table('sensors')->delete();
return 'Done...';
Lastly I also tried adding explicitly 'DB::commit()' between the delete statements, but all return the same result.
Is this normal behaviour? Am I missing something?
No, this is the way your database works. You can't truncate table that is referenced by some other table. You may do something like
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
DB::table('datapoints')->truncate();
DB::table('sensors')->truncate();
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
to disable foreign key checks, truncate tables and enable it again.
If you prefer to use Eloquent objects, Maksym's answer the "Eloquent" way
use Illuminate\Support\Facades\Schema;
use App\Models\Datapoint;
use App\Models\Sensor;
Schema::disableForeignKeyConstraints();
Datapoint::truncate();
Sensor::truncate();
Schema::enableForeignKeyConstraints();
In Laravel 7 and 8, for compatibility across 4 databases (MySql, Postgres, SQLite and SqlServer) and no Eloquent, you can use:
Schema::disableForeignKeyConstraints();
DB::table('datapoints')->truncate();
DB::table('sensors')->truncate();
Schema::enableForeignKeyConstraints();

laravel set up a foreign key

I am very new in laravel.I am doing a project where user first of all sign up and his information will store in a User table.then he or she sign in to the website and the user will provide another form for registration as a Blood donner. This imformation will store another table namely blooddonners table.so i want to make a foreign key in blooddonner table.The foreingn key will be the id from the user table.Thats why i have created a migration for blooddonner table like that,
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateBlooddonnerTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('blooddonners', function(Blueprint $table)
{
$table->increments('id');
$table->unsignedInteger('donner_id');
$table->foreign('donner_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->string('donner_name');
$table->string('email')->unique();
$table->string('blood_group');
$table->string('phone_number')->unique();
$table->string('location');
$table->date('date_of_birth');
$table->date('last_date_of_donation');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('blooddonners');
}
}
but after submitting the the form,i am facing the following error,
Illuminate \ Database \ QueryException
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`needa`.`blooddonners`, CONSTRAINT `blooddonners_donner_id_foreign` FOREIGN KEY (`donner_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE) (SQL: insert into `blooddonners` (`date_of_birth`, `blood_group`, `location`, `email`, `phone_number`, `last_date_of_donation`, `updated_at`, `created_at`) values (2014-08-06, A-, SHERPUR, raihn_cse#yahoo.com, 01796580404, 2014-08- 20, 2014-08-10 19:42:38, 2014-08-10 19:42:38))
though i am very new in Laravel,so i have question that,the way i have set foreign in my migration file ,is it the rigth way to set foreign key ?, cause the error is related to that foreign key.
There's nothing wrong with the way you're setting up foreign keys. Proof of that is that MySQL is actually stopping you from inserting an entry which violates the foreign key you've told it to enforce. The problem is that you're trying to add a blood donor entry without specifying which user it is related to. You have two options, then:
In the query you are using to add the new blood donor, specify the related user's id;
Even though donor_id is the foreign key, it can be nullable if that's what you want, though you need to tell Laravel / MySQL that.
Here's how to define a nullable column:
$table->unsignedInteger('donor_id')->nullable();
I've taken the liberty of correcting your spelling: blood donner > blood donor. Proper spelling helps code readability, too ;)
Edit
If you want a suggestion on how you can improve your database design in order to make good use of foreign keys, here's how I'd approach it:
First thing: I'd only have a secondary blood_donors table if not every user was a blood donor. If 100% of your users are blood donors, don't over-complicate things: use only one table and add more columns. Otherwise, read on.
My users table would probably have all columns that relate to general personal information:
name
email
phone number (unique on users)
location
date of birth
After that I can just "link" the users table and the blood_donors table via foreign keys so I don't have to repeat myself. All I need to know is which user corresponds to which blood donor, and all I need to accomplish that is an id. That way, when I do a query on the database, whether it's on the blood_donors or users table originally, I can easily join both tables together and have all the data at once, without having it repeated in two different places.
So, going back to your migration, this would now suffice:
Schema::create('blooddonners', function(Blueprint $table)
{
$table->increments('id');
$table->unsignedInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->string('blood_group');
$table->date('last_date_of_donation');
$table->timestamps();
});
i.e. you don't need a "donor name" in your blood_donors table, because you already have it recorded on your users table, and they are the same person after all. You should have columns which are pertinent only in the context of donating blood.
You can then access all this data with a query like this (assuming you already have your models set up):
User::join('blood_donors', 'users.id', '=', 'blood_donors.user_id')->get();
And you'd have an object with both tables' columns at once. Just be mindful that columns with the exact same name will override each other in your final object, so if you want to reliably access both ids within that object, you'd better specify aliases on the select portion of the query. You can even change the column names if you have to. In Laravel you can do it like this:
User::select('users.*', 'users.name as donor_name', 'blood_donors.id as donor_id')
->join('blood_donors', 'users.id', '=', 'blood_donors.user_id')
->get();
Take a look at the Eloquent documentation on relationships to see how you can take your database designs further and still maintain clean, readable code.

Categories