laravel set up a foreign key - php

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.

Related

Laravel Migration create foreign key using foreignIdFor with specific data type?

So I've been trying to write a migration that creates a data table question_display_formats using tiny increments as you see below.
And then, adding new Foreign Key column to existing questions table, trying to use the foreignIdFor method as a shortcut that'd look nice
public function up()
{
Schema::create('question_display_formats', function (Blueprint $table) {
$table->tinyIncrements('id');
$table->string('format');
});
Schema::table('questions', function (Blueprint $table) {
$table->foreignIdFor(QuestionDisplayFormat::class)
->nullable(true)
->after('question_type_id')
->constrained();
});
}
Turns out, this errors out with
General error: 1215 Cannot add foreign key constraint
Which turns out because the foreignIdFor users a different data type (confirmed by manually matching them and running the erroring out SQL alter table statement).
I googled, read and tried to adjust by doing:
$table->mediumIncrements('question_display_format_id'); before the foreignIdFor line, which leads to error
SQLSTATE[42S21]: Column already exists: 1060 Duplicate column name
'question_display_format_id' (SQL: alter table questions add
question_display_format_id mediumint un signed not null
auto_increment primary key, add question_display_format_id bigint
unsigned null after question_type_id)
Is there a way to use foreignIdFor with the matching column size? or am I supposed to fall back on the classic way of first creating the column explicitly, then doing like $table->foreign('question_display_format_id')->references('id')->on('question_display_formats'); which I don't like because its very verbose and doesn't look good?
On the other hand, this is a one time used script.. lol would've been faster to just do it the old way! but I am curious to see how to do it right :)

Foreign key don't force referential integrity in laravel/PHPMYADMIN

I have users and phone table and I made one-to-one relationship in laravel, that's working perfectly but if I try to add data (foreign key user_id) manually to phone table without reference of any user(id), it also work.
In mysql(phpmyadmin), there is no foreign key relation built after the migration.
So I want to ask, what are the advantages of foreign key if it does't put any constraints in db tables or if is there any way to add these constraints using laravel, kindly let me know.
Code snippets
app/Phone.php
public function user(){
return $this->belongsTo('App\User');
}
app/User.php
public function phone(){
return $this->hasOne('App\Phone');
}
routes/web.php
Route::get('/', function () {
$user = factory(\App\User::class)->create();
$phone=new \App\Phone;
$phone->phone = '123456789';
$phone->user_id = '1';
$user->phone()->save($phone);
});
Phone (migration)
public function up()
{
Schema::create('phones', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('phone');
$table->unsignedBigInteger('user_id')->index();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
}
Users table does not have any user with id 10 but this also works and add data to phone (user_id)
$phone->phone = '123456789';
$phone->user_id = '10';
$phone->save();
Without the foreign key, there is no point to use a relational database. It helps you to maintain integrity and consistency. It will always check the records in the parent table while inserting into child table, on another hand, it reduces the execution time because of indexes.
In your case, you are able to insert the data into the child table because you did not make the reference key constraints. If you are not using that then you are breaking the role of relational database engines.
It's not a part of any framework or programming language, it's all about right database configuration and design. Laravel is not responsible for it.
There is a disadvantage too but it's only on the machine. It increases the cost of the server CPU.

Automatically fill foreign key row

I'm trying to create a login/register system for a project using laravel, and since it's my first time, I've been running into a lot of troubles, so I'd ask to please forgive me for any extremely dumb mistakes I've made.
I've already succesfully added a new custom field to the default registration screen, but in my modified users table, I also have two foreign keys referencing other tables ('gameInfo_id' refers to the 'gameInfo' table, and 'role_id' refers to the 'roles' table)
This is the error I'm getting:
Does this mean I have to find a way for the foreign key to be filled in automatically? If so, how would I go about doing this?
I've done some googling and found that this usually seems to be issue, but I've never found a clear solution.
Thank you!
Here's my migrations in the users table:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('firstName');
$table->string('lastName');
$table->string('email')->unique;
$table->string('password');
$table->integer('gameInfo_id')->unsigned();
$table->integer('role_id')->unsigned();
$table->timestamps();
});
Schema::table('users', function($table) {
$table->foreign('gameInfo_id')->references('id')->on('gameInfo');
$table->foreign('role_id')->references('id')->on('roles');
});
gameInfo is a table of scores that the user would achieve in a game we're also making. What I'm trying to make happen is, when a new user registers an account, it creates a new row in gameInfo, to which the gameInfo_id foreign key would refer. The columns of this newly created row could be set to 0 by default if it helps
I would do it the other way around.
I would reference the user on the gameInfo table with a user_id column.
that way, the user hasMany gameInfo, and a gameInfo belongs to a user. Since the Game info isn't created before the user is created, and has played a game.
Does that make sense?
If the users table already contains datasets, where some of them don't have existing gameInfo_id or role_id, you can't add an foreign key constraint to the table.
The table must be in an valid state, before you add an foreign key constraint.
That means: Adding an new foreign key constraint to an existing field is not the best idea. Doing it anyway, you have to fix the integrity before.
If your still on an development environment, the best way to fix this would be to start all over again with migrate:refresh.
If the system runs in production with users, you have to fix all users with invalid or unset gameInfo_id and role_id. If you have just a few users, you could do this by hand. If you have many users, you could do some magic inside the migration script (iterate over all users and add missing references, before you add fk constraints).
Since the ->nullable() didn't seem te be recognized by laravel, I instead decided to give each column in gameInfo a default value of 0, which works just fine for me as well.
On the other hand, reversing the FK relationship between users and gameInfo also worked, so my problem's been resolved!
Thank you all very much for your answers, every contribution is appreciated greatly!

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');

Doctrine/Symfony generated migrations out of order

I'm not sure if this is a bug, but it sure seems like one to me.
When I generate migrations using generate-migrations-diff involving tables that have foreign keys in Symfony, the migrations that result seem to be out of order.
For instance, assuming I have a foreign key in table A to table B, and I delete table B and the referencing field in Table A, the first migration drops Table B and the column in Table A whereas the second migration then drops the foreign key constraint in Table A. The first migration does not work as the field in Table A with the foreign key constraint cannot be dropped, nor can Table B be dropped because of the foreign key constraint.
What should happen is that the foreign key constraint should be dropped first, and then the table and fields should be dropped.
I'm using mysql 5.1.37 as my DBMS.
Here's some of the generated code:
class Version94 extends Doctrine_Migration_Base
{
public function up()
{
$this->dropTable('B');
$this->removeColumn('A', 'b_id');
}
.
.
.
class Version95 extends Doctrine_Migration_Base
{
public function up()
{
$this->dropForeignKey('A', 'a_b_id_b_id');
}
It's almost certainly a bug. In my experience, doctrine:generate-migrations-diff is very unreliable and will generate incorrect migrations for more complex changes.
You can report the issue here.

Categories