Laravel UUID as primary key and ID as foreign key - mysql

I've defined a uuid as primary key, and would like to use id (auto increment) as foreign key to define relationships.
Both Posts and Comments have in their migration:
$table->uuid('uuid')->primary();
$table->bigInteger('id');
Their models have:
protected $primaryKey = 'uuid';
protected $keyType = 'string';
public $incrementing = false;
And the comments migration has
$table->bigInteger('post_id')->unsigned();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
But if I run the migration I get General error: 1215 Cannot add foreign key constraint (SQL: alter table `comments` add constraint `comments_post_id_foreign` foreign key (`post_id`) references `posts` (`id`) on delete cascade) of which I'm not sure why it's happening? Before adding the UUID logic it was working fine so I'm sure I've made a mistake there, but I'm not sure what as the error is generic.

Give this a try.
$table->foreignId('post_id')->constrained('posts')->onDelete('cascade');

Adding a foreign key requires the referenced key and the foreign one to have the same type and sign. According to your migrations, both posts.id and comments.id are signed bigints but you are trying to add a foreign key to a unsigned bigint.
In your migrations for posts and comments, change
$table->bigInteger('id');
to
$table->bigInteger('id')->unsigned();
and migrate them again.

You can try this.
$table->foreignUuid('post_id')->constrained()->onDelete('cascade');
You can have a look at the method which is inside the vendor/laravel/framework/src/Illuminate/Database/Schema/Blueprint.php
/**
* Create a new UUID column on the table with a foreign key constraint.
*
* #param string $column
* #return \Illuminate\Database\Schema\ForeignIdColumnDefinition
*/
public function foreignUuid($column)
{
return $this->columns[] = new ForeignIdColumnDefinition($this, [
'type' => 'uuid',
'name' => $column,
]);
}

Related

Foreign Key Constraint in Laravel Migration

In Laravel 4, how can I add a foreign key constraint in a migration?
In the migration for mytable (which references foreigntable):
// add Column
$table
->string( 'foreigntable_id', 6 );
// add FK
$table
->foreign( 'foreigntable_id' )
->references( 'id' )
->on( 'foreigntable' );
Error:
[Exception]
SQLSTATE[HY000]: General error: 1005 Can't create table 'mydb.#sql-1a24_2
1a' (errno: 150) (SQL: alter table `mytable` add constraint
mytable_foreigntable_id_foreign foreign key (`foreigntable_id`) references
`foreigntable` (`id`)) (Bindings: array (
))
I assume the problem is that foreigntable does not exist when MySQL tries to add the foreign key constraint to mytable (because the migration that creates foreigntable will only be run after the migration for mytable was finished).
How can I get around this problem?
Actually, I just found the answer myself.
Move the following from the migration for mytable into a new migration:
// add FK
$table
->foreign( 'foreigntable_id' )
->references( 'id' )
->on( 'foreigntable' );
Since the new migration will be run after the migration for mytable, as well as after the migration for foreigntable, both tables will be present at the time that the foreign key constraints is added. Hence it works.

Integrity constraint violation: 1452 Cannot add or update a child row:

I am trying to insert values into my comments table and I am getting a error. Its saying that I can not add or update child row and I have no idea what that means.
my schema looks something like this
-- ----------------------------
-- Table structure for `comments`
-- ----------------------------
DROP TABLE IF EXISTS `comments`;
CREATE TABLE `comments` (
`id` varchar(36) NOT NULL,
`project_id` varchar(36) NOT NULL,
`user_id` varchar(36) NOT NULL,
`task_id` varchar(36) NOT NULL,
`data_type_id` varchar(36) NOT NULL,
`data_path` varchar(255) DEFAULT NULL,
`message` longtext,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_comments_users` (`user_id`),
KEY `fk_comments_projects1` (`project_id`),
KEY `fk_comments_data_types1` (`data_type_id`),
CONSTRAINT `fk_comments_data_types1` FOREIGN KEY (`data_type_id`) REFERENCES `data_types` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_comments_projects1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_comments_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf32;
-- ----------------------------
-- Records of comments
-- ----------------------------
-- ----------------------------
-- Table structure for `projects`
-- ----------------------------
DROP TABLE IF EXISTS `projects`;
CREATE TABLE `projects` (
`id` varchar(36) NOT NULL,
`user_id` varchar(36) NOT NULL,
`title` varchar(45) DEFAULT NULL,
`description` longtext,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_projects_users1` (`user_id`),
CONSTRAINT `fk_projects_users1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf32;
-- ----------------------------
-- Records of projects
-- ----------------------------
INSERT INTO `projects` VALUES ('50dcbc72-3410-4596-8b71-0e80ae7aaee3', '50dcbc5c-d684-40bf-9715-0becae7aaee3', 'Brand New Project', 'This is a brand new project', '2012-12-27 15:24:02', '2012-12-27 15:24:02');
and the mysql statement I am trying to do looks something like this
INSERT INTO `anthonyl_fbpj`.`comments` (`project_id`, `user_id`, `task_id`, `data_type_id`, `message`, `modified`, `created`, `id`)
VALUES ('50dc845a-83e4-4db3-8705-5432ae7aaee3', '50dcbc5c-d684-40bf-9715-0becae7aaee3', '1', '50d32e5c-abdc-491a-a0ef-25d84e9f49a8', 'this is a test', '2012-12-27 19:20:46', '2012-12-27 19:20:46', '50dcf3ee-8bf4-4685-aa45-4eb4ae7aaee3')
the error I get looks like this
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(anthonyl_fbpj.comments, CONSTRAINT fk_comments_projects1
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE NO
ACTION ON UPDATE NO ACTION)
It just simply means that the value for column project_id on table comments you are inserting doesn't exist on table projects. Bear in mind that the values of column project_id on table comments is dependent on the values of ID on table Projects.
The value 50dc845a-83e4-4db3-8705-5432ae7aaee3 you are inserting for column project_id does not exist on table projects.
Make sure you have project_id in the fillable property of your Comment model.
I had the same issue, And this was the reason.
If you are adding new foreign key to an existing table and the columns are not null and not assigned default value, you will get this error,
Either you need to make it nullable or assign default value, or delete all the existing records to solve it.
Also make sure that the foreign key you add is the same type of the original column, if the column you're reference is not the same type it will fail too.
I hope my decision will help. I had a similar error in Laravel. I added a foreign key to the wrong table.
Wrong code:
Schema::create('comments', function (Blueprint $table) {
$table->unsignedBigInteger('post_id')->index()->nullable();
...
$table->foreign('post_id')->references('id')->on('comments')->onDelete('cascade');
});
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
...
});
Please note to the function on('comments') above. Correct code
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
that means that the value for column project_id on table comments you are inserting not only doesn't exist on table projects BUT also project_id probably doesn't have default value. E.g. in my case I set it as NULL.
As for Laravel you can consider this expressions as a chunk of code of a migration php file, for example:
class ForeinToPhotosFromUsers extends Migration
{ /** * Run the migrations. * * #return void */
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('photo_id')->nullable();// ! ! ! THIS STRING ! ! !
$table->foreign('photo_id')->references('id')->on('photos');
});
}
}
Obviously you had to create the Model class(in my case it was Photo) next to all these.
First delete the constraint "fk_comments_projects1" and also its index. After that recreate it.
You also get this error if you do not create and populate your tables in the right order. For example, according to your schema, Comments table needs user_id, project_id, task_id and data_type_id. This means that Users table, Projects table, Task table and Data_Type table must already have exited and have values in them before you can reference their ids or any other column.
In Laravel this would mean calling your database seeders in the right order:
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* #return void
*/
public function run()
{
$this->call(UserSeeder::class);
$this->call(ProjectSeeder::class);
$this->call(TaskSeeder::class);
$this->call(DataTypeSeeder::class);
$this->call(CommentSeeder::class);
}
}
This was how I solved a similar issue.
Maybe you have some rows in the table that you want to create de FK.
Run the migration with foreign_key_checks OFF
Insert only those records that have corresponding id field in contents table.
Issue could be because of no project record found in database with project_id you are trying to add...
In case someone is using Laravel and is getting this problem. I was getting this as well and the issue was in the order in which I was inserting the ids (i.e., the foreign keys) in the pivot table.
To be concrete, find below an example for a many to many relationship:
wordtokens <-> wordtoken_wordchunk <-> wordchunks
// wordtoken_wordchunk table
Schema::create('wordtoken_wordchunk', function(Blueprint $table) {
$table->integer('wordtoken_id')->unsigned();
$table->integer('wordchunk_id')->unsigned();
$table->foreign('wordtoken_id')->references('id')->on('word_tokens')->onDelete('cascade');
$table->foreign('wordchunk_id')->references('id')->on('wordchunks')->onDelete('cascade');
$table->primary(['wordtoken_id', 'wordchunk_id']);
});
// wordchunks table
Schema::create('wordchunks', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('text');
});
// wordtokens table
Schema::create('word_tokens', function (Blueprint $table) {
$table->increments('id');
$table->string('text');
});
Now my models look like follows:
class WordToken extends Model
{
public function wordchunks() {
return $this->belongsToMany('App\Wordchunk');
}
}
class Wordchunk extends Model
{
public function wordTokens() {
return $this->belongsToMany('App\WordToken', 'wordtoken_wordchunk', 'wordchunk_id', 'wordtoken_id');
}
}
I fixed the problem by exchanging the order of 'wordchunk_id' and 'wordtoken_id' in the Wordchunk model.
For code completion, this is how I persist the models:
private function persistChunks($chunks) {
foreach ($chunks as $chunk) {
$model = new Wordchunk();
$model->text = implode(' ', array_map(function($token) {return $token->text;}, $chunk));
$tokenIds = array_map(function($token) {return $token->id;}, $chunk);
$model->save();
$model->wordTokens()->attach($tokenIds);
}
}
I had this issue when I was accidentally using the WRONG "uuid" in my child record. When that happens the constraint looks from the child to the parent record to ensure that the link is correct. I was generating it manually, when I had already rigged my Model to do it automatically. So my fix was:
$parent = Parent:create($recData); // asssigning autogenerated uuid into $parent
Then when I called my child class to insert children, I passed this var value:
$parent->uuid
Hope that helps.
I had the same error, the problem was that I was trying to add role_id foreign to the users table, but role_id did not have a default value, so the DB did not allow me to insert the column because I already had some users and it didn't know how to add the column to them.
Since I was in development I just used migrate:fresh, but if I was on production, I would probably set a default value to the role_id and not make it not constrained until I had the corresponding role on the DB.
Could be you are inserting a foreign key value that does not match with that of the primary key.
In my case it didn't work because I was leaving a space in one of the names of in the model.
I had a table fulfilled with some entries that has dead foreigners keys.
once i cleaned the table, i was able to add a new constraint.
DELETE FROM `table_with_bad_entries` WHERE `expected_foreign_key` not in (select `id` from `the_referenced_table`);
I just exported the table deleted and then imported it again and it worked for me. This was because i deleted the parent table(users) and then recreated it and child table(likes) has the foreign key to parent table(users).

A foreign key constraint fails with Zend_Db_Table

I have to tables and their SQL is as below:
CREATE TABLE item (
itemID INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
name VARCHAR(100) NOT NULL,
price FLOAT NOT NULL,
stock INTEGER NOT NULL
) ENGINE = InnoDB;
CREATE TABLE salesrecord (
recordID INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
itemID INT UNSIGNED NOT NULL,
recordDate DATE NOT NULL,
FOREIGN KEY (itemID) REFERENCES item (itemID)
) ENGINE = InnoDB;
And this is my Zend_Db_Table concrete classes:
<?php
class Store_Model_Items extends Zend_Db_Table_Abstract
{
protected $_name = 'item';
protected $_rowClass = 'Store_Model_Item';
protected $_dependTables = array('Store_Model_SalesRecords');
}
<?php
class Store_Model_SalesRecords extends Zend_Db_Table_Abstract
{
protected $_name = 'salesrecord';
protected $_referenceMap = array(
'Item' => array(
'columns' => array('itemID'),
'refTableClass' => 'Store_Model_Items',
'refColumns' => array('itemID'),
'onDelete' => self::CASCADE
)
);
}
When I try to delete a row in item table,
I get this message:
SQLSTATE[HY000]: General error: 1451 Cannot delete or update a parent row: a foreign key constraint fails (newstore/salesrecord, CONSTRAINT salesrecord_ibfk_1 FOREIGN KEY (itemID) REFERENCES item (itemID))
If you want delete the record from parent table, firstly you should delete related records in a child table. Also you can add ON DELETE CASCADE checking, it will help automatically delete related records in the child table. Try to recreate your FK in this way -
ALTER TABLE salesrecord
DROP FOREIGN KEY salesrecord_ibfk_1; -- change FK name here!
ALTER TABLE salesrecord
ADD CONSTRAINT salesrecord_ibfk_1 FOREIGN KEY (itemID)
REFERENCES item(itemID) ON DELETE CASCADE;
Change 'salesrecord_ibfk_1' with actual FK name.

CakePHP delete not cascading. Causes foreign key error

I have a table called custom_carts and I have a table called custom_cart_items.
custom_cart_items has a foreign key called custom_cart_id that is set to custom_cart.id
According to the Cake Manual, when you call Model->delete() the first param is the id of the entry you want to delete and the second param is whether or not to cascade delete the dependent entries.
So when I call $this->CustomCart->delete(7,true) I get this error:
SQL Error: 1451: Cannot delete or
update a parent row: a foreign key
constraint fails
(`krake`.`custom_cart_items`,
CONSTRAINT `custom_cart_items_ibfk_1`
FOREIGN KEY (`custom_cart_id`)
REFERENCES `custom_carts` (`id`))
Here is the query:
DELETE `CustomCart`
FROM `custom_carts` AS `CustomCart`
LEFT JOIN `users` AS `User`
ON (`CustomCart`.`user_id` = `User`.`id`)
WHERE `CustomCart`.`id` = 25
Shouldn't it cascade and dlete the other entry too?
So why am I getting an error?
You must set the dependent parameter to true in the model with the hasmany relationship in order to enable cascading deletion.
Your model would look something like this:
class CustomCart extends AppModel {
var $name = 'CustomCart';
var $hasMany = array(
'CustomCartItem' => array(
'dependent'=> true
)
);
}
Please see http://book.cakephp.org/view/82/hasMany
Just adding fields to a table (regardless of the field name) does not tell CakePHP that they're associated, nor does it link them for cascading deletion.
You need to set the associations in your model: SEE DETAILS HERE.
On the other hand, if you want to delete a record from a table A, and there is another table B which as a FOREIGN KEY constraint on table A, you must set the option on that foreign key of the table B in the Database like this
"SET NULL ON DELETE".
I usually do this in MySqlWorkbench, but you can also use legacy SQL queries to create a table with such an option.

Do I need to manually create indexes for a DBIx::Class belongs_to relationship

I'm using the DBIx::Class modules for an ORM approach to an application I have.
I'm having some problems with my relationships.
I have the following
package MySchema::Result::ClusterIP;
use strict;
use warnings;
use base qw/DBIx::Class::Core/;
our $VERSION = '1.0';
__PACKAGE__->load_components(qw/InflateColumn::Object::Enum Core/);
__PACKAGE__->table('cluster_ip');
__PACKAGE__->add_columns( # Columns here );
__PACKAGE__->set_primary_key('objkey');
__PACKAGE__->belongs_to( 'configuration' => 'MySchema::Result::Configuration', 'config_key');
__PACKAGE__->belongs_to( 'cluster' => 'MySchema::Result::Cluster',
{ 'foreign.config_key' => 'self.config_key',
'foreign.id' => 'self.cluster_id'
}
);
As well as
package MySchema::Result::Cluster;
use strict;
use warnings;
use base qw/DBIx::Class::Core/;
our $VERSION = '1.0';
__PACKAGE__->load_components(qw/InflateColumn::Object::Enum Core/);
__PACKAGE__->table('cluster');
__PACKAGE__->add_columns( # Columns here );
__PACKAGE__->set_primary_key('objkey');
__PACKAGE__->belongs_to( 'configuration' => 'MySchema::Result::Configuration', 'config_key');
__PACKAGE__->has_many('cluster_ip' => 'MySchema::Result::ClusterIP',
{ 'foreign.config_key' => 'self.config_key',
'foreign.cluster_id' => 'self.id'
});
There are a couple of other modules, but I don't believe that they are relevant.
When I attempt to deploy this schema, I get the following error:
DBIx::Class::Schema::deploy(): DBI Exception: DBD::mysql::db do failed: Can't create table 'test.cluster_ip' (errno: 150) [
for Statement "CREATE TABLE `cluster_ip` (
`objkey` smallint(5) unsigned NOT NULL auto_increment,
`config_key` smallint(5) unsigned NOT NULL,
`cluster_id` char(16) NOT NULL,
INDEX `cluster_ip_idx_config_key_cluster_id` (`config_key`, `cluster_id`),
INDEX `cluster_ip_idx_config_key` (`config_key`),
PRIMARY KEY (`objkey`),
CONSTRAINT `cluster_ip_fk_config_key_cluster_id` FOREIGN KEY (`config_key`, `cluster_id`) REFERENCES `cluster` (`config_key`, `id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `cluster_ip_fk_config_key` FOREIGN KEY (`config_key`) REFERENCES `configuration` (`config_key`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB"] at test_deploy.pl line 18
(running "CREATE TABLE `cluster_ip` (
`objkey` smallint(5) unsigned NOT NULL auto_increment,
`config_key` smallint(5) unsigned NOT NULL,
`cluster_id` char(16) NOT NULL,
INDEX `cluster_ip_idx_config_key_cluster_id` (`config_key`, `cluster_id`),
INDEX `cluster_ip_idx_config_key` (`config_key`),
PRIMARY KEY (`objkey`),
CONSTRAINT `cluster_ip_fk_config_key_cluster_id` FOREIGN KEY (`config_key`, `cluster_id`) REFERENC
ES `cluster` (`config_key`, `id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `cluster_ip_fk_config_key` FOREIGN KEY (`config_key`) REFERENCES `configuration` (`conf
ig_key`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB") at test_deploy.pl line 18
From what I can tell, MySQL is complaining about the FOREIGN KEY constraint, in particular, the REFERENCE to (config_key, id) in the cluster table. From my reading of the MySQL documentation, this seems like a reasonable complaint, especially in regards to the third bullet point on this doc page.
Here's my question. Am I missing something in the DBIx::Class module? I realize that I could explicitly create the necessary index to match up with this foreign key constraint, but that seems to be repetitive work. Is there something I should be doing to make this occur implicitly?
Not sure what is happening here as SQL::Translator::Producer::MySQL should insert SET foreign_key_checks=0 at the start of the deployed DDL so no foreign key errors should occur. I suspect something is broken even after the whole DDL is deployed. You can find out the exact nature of the foreign key error by connecting to the DB and running this statement:
SHOW INNODB STATUS;
I just fixed this same problem by adding the following into my result classes that were causing problems:
sub sqlt_deploy_hook {
my ($self, $sqlt_table) = #_;
$sqlt_table->add_index(name => 'idx_my_column', fields => ['my_column']);
}