This is the error I am thrown when I try to delete my categories entry with products under that parent category:
Illuminate \ Database \ QueryException
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`store`.`products`, CONSTRAINT `products_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)) (SQL: delete from `categories` where `id` = 1)
After I did some research I do know that you cannot delete a parent with existing children
I am not sure how to join the products with my category id when I delete the category id. This way I can delete all products with the associated category id.
Here is my function for deleting:
public function postDestroy() {
$category = Category::find(Input::get('id'));
if ($category) {
$category->delete();
return Redirect::to('admin/categories/index')
->with('message', 'Category Deleted');
}
return Redirect::to('admin/categories/index')
->with('message', 'Something went wrong, please try again');
}
If you want to delete any products that have the same category, I would change your category class that extends eloquent to something like this:
class Category extends Eloquent
{
// ... all your existing code...
public function delete()
{
// Delete all of the products that have the same ids...
Products::where("category_id", $this->id)->delete();
// Finally, delete this category...
return parent::delete();
}
}
Now calling $category->delete() will delete all the products that have the same category_id, as well as the category itself.
Related
Why is ON DELETE SET NULL failing when deleting a row via the application code, but it behaves correctly when manually executing an SQL statement?
I have a todo table and a category table. The todo table has a category_id foreign key that references id in the category table, and it was created with the "ON DELETE SET NULL" action.
create table `category` (
`id` integer not null primary key autoincrement,
`name` varchar(255) not null
);
create table `todo` (
`id` integer not null primary key autoincrement,
`title` varchar(255) not null,
`complete` boolean not null default '0',
`category_id` integer,
foreign key(`category_id`) references `category`(`id`) on delete SET NULL on update CASCADE
);
I also have an endpoint in my application that allows users to delete a category.
categoryRouter.delete('/:id', async (req, res) => {
const { id } = req.params
await req.context.models.Category.delete(id)
return res.status(204).json()
})
This route successfully deletes categories, but the problem is that related todo items are not getting their category_id property set to null, so they end up with a category id that no longer exists. Strangely though, if I open up my database GUI and manually execute the query to delete a category... DELETE FROM category WHERE id=1... the "ON DELETE SET NULL" hook is successfully firing. Any todo item that had category_id=1 is now set to null.
Full application source can be found here.
Figured it out, thanks to MikeT.
So apparently SQLite by default has foreign key support turned off. WTF!
To enable FKs, I had to change my code from this...
const knex = Knex(knexConfig.development)
Model.knex(knex)
to this...
const knex = Knex(knexConfig.development)
knex.client.pool.on('createSuccess', (eventId, resource) => {
resource.run('PRAGMA foreign_keys = ON', () => {})
})
Model.knex(knex)
Alternatively, I could have done this inside of the knexfile.js...
module.exports = {
development: {
client: 'sqlite3',
connection: {
filename: './db.sqlite3'
},
pool: {
afterCreate: (conn, cb) => {
conn.run('PRAGMA foreign_keys = ON', cb)
}
}
},
staging: {},
production: {}
}
FYI and other people who stumbled across a similar problem, you need PRAGMA foreign_keys = ON not only for the child table but also for the parent table.
When I set PRAGMA foreign_keys = ON only for a program which handles the child table, ON UPDATE CASCADE was enabled but ON DELETE SET NULL was still disabled. At last I found out that I forgot PRAGMA foreign_keys = ON for another program which handles the parent table.
Good day, I'm new to laravel I was doing a migration rollback and it was successfully done
Rolled back: 2018_02_22_172102_adding_fk_constrains_products_to_product_types_and_service_sub_types_table
But when I try to re-migrate I encountered this error below. BTW I don't want to drop the column because it already existed and I don't want to lose the existing data in that column. I only want to add constraint between those tables
[Illuminate\Database\QueryException]
SQLSTATE[23000]: Integrity constraint violation: 1022 Can't write; duplicate key in table '#sql-2fc8_17c' (SQL: alter table products add constraint products_product_type_id_foreign
foreign key (product_type_id) references product_types (id) on update cascade)
[PDOException]
SQLSTATE[23000]: Integrity constraint violation: 1022 Can't write; duplicate key in table '#sql-2fc8_17c'
Table Non_unique Key_name Seq_in_index Column_name
-------- ---------- ---------------------------------- ------------ -------------------
products 0 PRIMARY 1 id
products 1 products_product_type_id_index 1 product_type_id
products 1 products_service_sub_type_id_index 1 service_sub_type_id
this my migration code
public function up()
{
Schema::table('products',function (Blueprint $table){
$table->integer('product_type_id')->unsigned()->index()->change();
$table->foreign('product_type_id')->references('id')->on('product_types')->onUpdate('cascade');
$table->integer('service_sub_type_id')->nullable()->unsigned()->index()->change();
$table->foreign('service_sub_type_id')->references('id')->on('service_sub_types')->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('products', function(Blueprint $table){
$table->dropForeign(['product_type_id']);
$table->dropForeign(['service_sub_type_id']);
});
}
I just remove the following code in my migration
$table->integer('product_type_id')->unsigned()->index()->change();
$table->integer('service_sub_type_id')->nullable()->unsigned()->index()->change();
public function up()
{
Schema::table('products',function (Blueprint $table){
//$table->integer('product_type_id')->unsigned()->index()->change();
$table->foreign('product_type_id')->references('id')->on('product_types')->onUpdate('cascade');
//$table->integer('service_sub_type_id')->nullable()->unsigned()->index()->change();
$table->foreign('service_sub_type_id')->references('id')->on('service_sub_types')->onUpdate('cascade');
});
}
when I re migrate it was successfully migrated..
For those still looking for a solution, add this code to the down() method.
public function down()
{
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
Schema::table('products', function(Blueprint $table){
$table->dropForeign(['product_type_id']);
$table->dropForeign(['service_sub_type_id']);
});
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
I am trying to pull a list of characters that belong to a certain user. When I make the request I get an SQL Error. Reading through the error it is trying to us fields that don't exist.
Error:
SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'characters' (SQL: select `characters`.*, `characters`.`id` as `pivot_id`,
`characters`.`character_id` as `pivot_character_id`
from `characters` inner join `characters` on `characters`.`id` = `characters`.`character_id` where `characters`.`id` = 1)
"character_id" does not exist in my database. The problem is I can't find where Eloquent is making that field. I looked through the source code and there was a lot of "If this is not provided use $variable.'_id'. I could not find that code anywhere for this though.
Models are below.
class Character extends Eloquent {
protected $guarded = array('id');
protected $table = 'characters';
public function User ()
{
return $this->belongsTo('User', 'id');
}
}
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
protected $table = 'users';
protected $hidden = ['password', 'remember_token'];
protected $guarded = ['password'];
public function Character ()
{
return $this->belongsToMany('Character', 'characters', 'id');
}
}
There is a foreign key between user_id in the characters table, and id in the users table.
belongsToMany is for many-to-many relations. Laravel throws you an error because it expects third table - pivot table - containing both character_id and user_id.
If you dont want many-to-many but one-to-many then you should use hasMany and belongsTo.
EDIT: for the tl;dr crowd, my question is: How do I access the mappings from inside the ForeignKeyConvention in order to determine the table name that a given type is mapped to?
The long version:
I am using Fluent NHibernate to configure NHibernate, and I have a custom foreign key convention that is failing when I alias tables and columns.
My tables use a convention where the primary key is always called "PK", and the foreign key is "FK" followed by the name of the foreign key table, e.g., "FKParent". For example:
CREATE TABLE OrderHeader (
PK INT IDENTITY(1,1) NOT NULL,
...
)
CREATE TABLE OrderDetail (
PK INT IDENTITY(1,1) NOT NULL,
FKOrderHeader INT NOT NULL,
...
)
To make this work, I've built a custom ForeignKeyConvention that looks like this:
public class AmberForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName( Member member, Type type )
{
if ( member == null )
return "FK" + type.Name; // many-to-many, one-to-many, join
return "FK" + member.Name; // many-to-one
}
}
This works so long as my entities are named the same as the table. But it breaks when they aren't. For example, if I want to map the OrderDetail table to a class called Detail, I can do so like this:
public class DetailMap : ClassMap<Detail>
{
public DetailMap()
{
Table( "OrderDetail" );
Id( o => o.PK );
References( o => o.Order, "FKOrderHeader" );
...
}
}
The mapping works for loading a single entity, but when I try to run any kind of complicated query with a join, it fails, because the AmberForeignKeyConvention class is making incorrect assumptions about how the columns are mapped. I.e., it assumes that the foreign key should be "FK" + type.Name, which in this case is Order, so it calls the foreign key "FKOrder" instead of "FKOrderHeader".
So as I said above: My question is, how do I access the mappings from inside the ForeignKeyConvention in order to determine a given type's mapped table name (and for that matter, their mapped column names, too)? The answer to this question seems to hint at the right direction, but I don't understand how the classes involved work together. When I look through the documentation, it's frightfully sparse for the classes I've looked up (such as the IdMapping class).
the idea is to load the mappings
public class AmberForeignKeyConvention : ForeignKeyConvention
{
private static IDictionary<Type, string> tablenames;
static AmberForeignKeyConvention()
{
tablenames = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => typeof(IMappingProvider).IsAssignableFrom(t))
.ToDictionary(
t => t.BaseType.GetGenericArguments()[0],
t => ((IMappingProvider)Activator.CreateInstance(t)).GetClassMapping().TableName);
}
protected override string GetKeyName( Member member, Type type )
{
return "FK" + tablenames[type]; // many-to-one
}
}
I am new to nhibernate and trying to create a query on a database with manytomany links between items and categories.
I have a database with 3 tables : items, categories and a lookup table categoryitem like this:
categorys - primary key categoryId
items - primary key itemId
categoryItem - categoryId column and itemId column
I want a query returning items for a particular category and have tried this and think i am along the right lines:
public IList<Item> GetItemsForCategory(Category category)
{
//detached criteria
DetachedCriteria itemIdsCriteria = DetachedCriteria.For(typeof(Category))
.SetProjection(Projections.Distinct(Projections.Property("Item.Id")))
.Add(Restrictions.Eq("Category.Id", category.Id));
criteria.Add(Subqueries.PropertyIn("Id", itemIdsCriteria));
return criteria.List<Item>() as List<Item>;
}
I only have business objects for category and item.
how do i create a repository method to find items for a particular category?
I assume that your classes look like this:
class Item
{
// id and stuff
IList<Category> Categories { get; private set; }
}
class Category
{
// id and stuff
}
query (HQL)
session.CreateQuery(#"select i
from Item i
inner join i.Categories c
where
c = :category")
.SetEntity("category", category)
Criteria
session
.CreateCriteria(typeof(Item))
.CreateCriteria("Categories", "c")
.Add(Restrictions.Eq("c.Id", category.Id))