Does cakephp implicitly implement on delete cascade for BTM association - cakephp

I am a beginner with cakephp and I am trying to understand it through the bookmarker app tutorial.
http://book.cakephp.org/3.0/en/quickstart.html
So here is the SQL scheme :
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
created DATETIME,
modified DATETIME
);
CREATE TABLE bookmarks (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(50),
description TEXT,
url TEXT,
created DATETIME,
modified DATETIME,
FOREIGN KEY user_key (user_id) REFERENCES users(id)
);
CREATE TABLE tags (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
created DATETIME,
modified DATETIME,
UNIQUE KEY (title)
);
CREATE TABLE bookmarks_tags (
bookmark_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (bookmark_id, tag_id),
FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
);
So here is what I did after "bake all" ->
I went and added a user in my app and added a bookmark and tag for the user.
Now I am trying to delete the user which throws a SQL Error saying it violates foreign key constraint as bookmarks refer to a user. Makes sense.
The problem is here - I can go and delete a bookmark without any error, however in my opinion it should throw an error because bookmarks_tags refers to a bookmark and that too violates referential integrity.
So can someone please tell me how this is working internally. It is my first time with CakePHP and MVC in general and I am trying to understand the internal workings of it.

Yes by default CakePHP removes related record from the join table first if the primary record is deleted. It's done so because in most case you don't care about association if primary record itself is deleted.
You can prevent automatic removal of join table records by setting dependent option to false in association config as stated here.

Related

Update/Delete violate foreign key on either side

I have two tables, below are the strutures
CREATE TABLE IF NOT EXISTS nl_address (
id int NOT NULL GENERATED BY DEFAULT AS IDENTITY,
address_text varchar(100),
pincode varchar(6),
city_id int NOT NULL,
state_id int NOT NULL,
country_id int NOT null,
is_active boolean default true,
PRIMARY KEY (id),
CONSTRAINT fk_city_id FOREIGN KEY(city_id) REFERENCES nl_city(id),
CONSTRAINT fk_state_id FOREIGN KEY(state_id) REFERENCES nl_state(id),
CONSTRAINT fk_country_id FOREIGN KEY(country_id) REFERENCES nl_country(id)
);
CREATE TABLE IF NOT EXISTS nl_customer (
cust_id int NOT NULL,
prefix varchar(10) default 'CUST-',
suffix varchar(2),
org_name varchar(100) NOT NULL,
domain_name varchar(100) NOT NULL,
pan_number varchar(10) NOT null,
pri_contact varchar(10) NOT NULL,
pri_number varchar(10) NOT NULL,
pri_email varchar(30) NOT NULL,
sec_contact varchar(10),
sec_number varchar(10),
sec_email varchar(30),
is_active boolean default true,
addr_id int not null,
created_date date,
created_by varchar(10),
updated_date date,
updated_by varchar(10),
PRIMARY KEY (cust_id),
CONSTRAINT fk_address_id FOREIGN KEY(addr_id) REFERENCES nl_address(id)
);
The problem is, neither I am able to update or delete
If i am trying to update record in nl_address, I got an violation error that the field is used inside `nl_customer.
If i tried to update from nl_customer, then I got an violation error that the field is used inside nl_address
It was originated, when JPA trying to persist the data, I have inserted a dummy data with id 1, when JPA trying to insert another record then it throws
.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "nl_address_pkey"
Detail: Key (id)=(1) already exists.
It seems there is something wrong with the table structure, any help appreciated
Actually this is common that you cannot update or delete that belong to primary/foreign key if you generate duplicates, as all values should be unique (i.e. if you have already id=1 and update id=2 to id=1, you will get the error you mentioned) and because a foreign key construct is a specific relationship it should be clarified what will happen with this relationship.
In case of 'nl_address' you used 'GENERATED BY DEFAULT AS IDENTITY' which have the same purpose as SERIAL (i.e. auto increment), but it is more compliant with SQL standard. (I assume you are also aware of difference between GENERATED BY DEFAULT and GENERATED ALWAYS)
However, you can specify the sequence in order to ensure the proper auto increment functionality.
ALTER TABLE nl_address
ALTER COLUMN "id"
DROP IDENTITY IF EXISTS;
ALTER TABLE nl_address
ALTER COLUMN "id"
ADD GENERATED BY DEFAULT AS IDENTITY (START WITH 1 INCREMENT 1);
If you use UPDATE or DELETE on FOREIGN KEY construct ensure what should happen with relationship:
[CONSTRAINT fk_name]
FOREIGN KEY(fk_columns)
REFERENCES parent_table(parent_key_columns)
[ON DELETE delete_action]
[ON UPDATE update_action]
/* as delete_action or update_action you can use e.g. SET NULL, RESTRICT or CASCADE;
so ensure what happen with records in related table*/

Entity Framework appears to skip mapping a table with a multi-column primary key set through a constraint

I've created an ADO.NET model via the database first approach.
One of my tables which is listed when creating the model doesn't actually get added to it.
The table has a multi-column primary key, composed of two foreign keys.
CREATE TABLE ForumAccess
(
UserID INT FOREIGN KEY REFERENCES Users(UserID) NOT NULL,
ForumID INT FOREIGN KEY REFERENCES Forums(ForumID) NOT NULL,
CONSTRAINT ForumAccessID PRIMARY KEY (UserID, ForumID),
);
It does show up when I have to select which tables to add, but then it seems to be skipped. No class is generated for it, and it's not shown in the .edmx file.
Part of my application depends on the existence of this table. I have another table which has a multi-column primary key, and another DateTime type column. That table does get added.
That table is:
CREATE TABLE Moderators
(
UserID INT FOREIGN KEY REFERENCES Users(UserID) NOT NULL,
ForumID INT FOREIGN KEY REFERENCES Forums(ForumID) NOT NULL,
TimeOfAddition DateTime NOT NULL, -- When the mod was added as a mod.
CONSTRAINT ModeratorID PRIMARY KEY (UserID, ForumID),
);
Why does the Moderators table get added, but the ForumAccess table doesn't?
There is no error, or any warning that I can see.
What am I missing?

Why the table is not inserted in Model.edmx file diagram

I'm using ASP.NET MVC with SQL Server and EF6, when I created the ADO.NET data model, all my tables in the database were inserted into the .edmx diagram except this one table (shown below). When I opened the .edmx properties, I found this (MODEL.SupervisorAOI Assassination) but not (MODEL.SupervisorAOI Entity Type).
Is there any error in my table?
CREATE TABLE [dbo].[SupervisorAOI]
(
[supervisorid] VARCHAR (50) NOT NULL,
[AOIId] INT NOT NULL,
[SUPAOI] INT NOT NULL IDENTITY,
CONSTRAINT [FK_SupervisorAOI_AreaOfInterest]
FOREIGN KEY ([AOIId]) REFERENCES [dbo].[AreaOFInterest] ([AOIId]),
CONSTRAINT [FK_SupervisorAOI_Supervisors]
FOREIGN KEY ([supervisorid]) REFERENCES [dbo].[Supervisors] ([supervisorId])
);
The problem I could see is that your primary key needs not to have 'not null' as primary keys are not nullable by default. Make it look like the following.
[SUPAOID] INT IDENTITY
It is also advisable to name your primary key as ID or append the primary key with Id. Your primary key could be SUPAOID or SupervisorAOIID.
This table does not have a Primary Key. You may have SUPAOI column identity but this is not enough..
Working around EntityFramework need to have a Primary Key (Entity Key) on tables.The best practices each table should be PK. Change table structure and add a Primary Column then update Model..
[SUPAOI] INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Note: if you do not use PK, you must provide table uniqueness , but it's not good idea..

Proper Database Design with Foreign Keys

Because I've never designed a database, I wanted to make sure that the design I'm using, while simple, follows general idiomatic design patterns.
Essentially, a friend is making a discord bot that allows you to submit photos and have others rate them. Putting the obvious trolling opportunities aside, here are the data fields that are needed:
Discord ID (unique ID for every person using discord)
List of URLs (each URL is tied to a certain discord member)
List of Votes (each vote has a score, the voter ID, and the URL)
What I don't particularly like about this design is that it maintains two scores: a running total that will be divided by the total number of votes of that user, and each vote in particular.
My questions are:
Is this design proper?
Using this design, how can I ensure that each person can only vote for each url once?
https://dbdesigner.net output:
CREATE TABLE "Members" (
"id" serial NOT NULL,
"discord_id" bigint NOT NULL,
"total_score" bigint NOT NULL,
"total_votes" bigint NOT NULL,
CONSTRAINT Members_pk PRIMARY KEY ("id")
) WITH (
OIDS=FALSE
);
CREATE TABLE "Images" (
"id" serial NOT NULL,
"url" TEXT(64) NOT NULL,
"member_id" bigint NOT NULL,
CONSTRAINT Images_pk PRIMARY KEY ("id")
) WITH (
OIDS=FALSE
);
CREATE TABLE "Votes" (
"id" serial NOT NULL,
"voter_id" serial NOT NULL,
"target_id" serial NOT NULL,
"score" serial NOT NULL,
"image_id" serial NOT NULL,
CONSTRAINT Votes_pk PRIMARY KEY ("id")
) WITH (
OIDS=FALSE
);
ALTER TABLE "Images" ADD CONSTRAINT "Images_fk0" FOREIGN KEY ("member_id") REFERENCES "Members"("discord_id");
ALTER TABLE "Votes" ADD CONSTRAINT "Votes_fk0" FOREIGN KEY ("voter_id") REFERENCES "Members"("discord_id");
ALTER TABLE "Votes" ADD CONSTRAINT "Votes_fk1" FOREIGN KEY ("target_id") REFERENCES "Members"("discord_id");
ALTER TABLE "Votes" ADD CONSTRAINT "Votes_fk2" FOREIGN KEY ("image_id") REFERENCES "Images"("id");
Since I cannot see your foreign key references and I don't see your code (i.e., SQL statements), I cannot know for sure if your synthetic keys are a good idea. But at first glance, it appears as though your real key for VOTES is (VOTER_ID, IMAGE_URL).
If we assume that you are not going to change the relations, their keys, and their non-key attributes, then all you need to do to satisfy #2 is to put a unique constraint on VOTES (VOTER_ID, IMAGE_URL).
Responding to the first part of the question, "Is this design proper", the short answer is "no".
If discord_ids are unique, you do not need another ID column in members. The discord_id is the primary key of the members table.
If Image URLs are unique, that could be the primary key of the Images table. That's really up to you; some people don't like using long text strings as keys. I'll assume you're one of them.
The Votes table shouldn't have an ID column at all. It's a many-to-many join table. Your key there is (voter_id, image_id). This also has the effect of preventing members from voting more than once.
The target_id column in votes is completely redundant, as that information already exists in the images table.
Neither voter_id nor image_id in Votes should be Serial. Instead, they should be INT. Score, which is presumably a numeric score, should be NUMERIC or INT (I'll use INT since total_score is bigint).
using mixed-case identifiers is generally a bad idea in SQL, as identifier (table) names are case-sensitive in strange ways.
Limiting URLs to 64 characters seems shortsighted; do you have an application constraint here you need to match?
You should add CASCADE to all of your foriegn keys, so that you can easily delete members or images.
As such, below is your revised schema:
CREATE TABLE "members" (
"discord_id" bigint NOT NULL,
"total_score" bigint NOT NULL,
"total_votes" bigint NOT NULL,
CONSTRAINT members_pk PRIMARY KEY ("discord_id")
);
CREATE TABLE "images" (
"id" serial NOT NULL,
"url" VARCHAR(64) NOT NULL,
"discord_id" BIGINT NOT NULL,
CONSTRAINT images_pk PRIMARY KEY ("id"),
CONSTRAINT images_url UNIQUE ("url")
);
CREATE TABLE "votes" (
"voter_id" INT NOT NULL,
"image_id" INT NOT NULL,
"score" INT NOT NULL,
CONSTRAINT votes_pk PRIMARY KEY (voter_id, image_id)
);
ALTER TABLE "images" ADD CONSTRAINT "images_fk0"
FOREIGN KEY ("discord_id") REFERENCES "members"("discord_id")
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "votes" ADD CONSTRAINT "votes_fk0"
FOREIGN KEY ("voter_id") REFERENCES "members"("discord_id")
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "votes" ADD CONSTRAINT "votes_fk2"
FOREIGN KEY ("image_id") REFERENCES "images"("id")
ON DELETE CASCADE ON UPDATE CASCADE;

Can't Create Foreign Key: May Cause Cycles

I've got a very simple relationship between two tables that are used to manage custom UI branding:
ui_portal_branding
CREATE TABLE ui_portal_branding
(
id VARBINARY(16) NOT NULL,
branding_type VARCHAR(128) NOT NULL,
portal_name NVARCHAR(128) NOT NULL,
theme_id VARBINARY(16) NOT NULL,
portal_logo VARBINARY(16) NULL,
portal_favicon VARBINARY(16) NULL,
background_color VARCHAR(50) NULL,
organization_id VARBINARY(16) NULL,
CONSTRAINT pk_ui_port_bran_id PRIMARY KEY (id)
)
ui_portal_resource
CREATE TABLE ui_portal_resource
(
id VARBINARY(16) NOT NULL,
mime_type NVARCHAR(128) NOT NULL,
binary_data VARBINARY(MAX) NOT NULL,
CONSTRAINT pk_ui_port_reso_id PRIMARY KEY (id)
)
Branding is the main table, Resources is a BLOB store for binary data. Both portal_logo and portal_favicon in the branding table are optional binary data from the resource table.
I'd like to define this as a foreign key constraint with the following general logic: neither logo or favicon are required to be defined. If they are defined, they point to a record in the resource table by ui_portal_resource.id. If the data is deleted from the resource table, I want to set the corresponding column in the branding table to null. I don't want to disallow the resource deletion, I don't want to cascade the delete to the branding table.
So I define the following:
ALTER TABLE ui_portal_branding
ADD CONSTRAINT fk_ui_port_bran2ui_port_reso
FOREIGN KEY (portal_logo) REFERENCES ui_portal_resource (id)
ON DELETE SET NULL
So far so good. Now I define:
ALTER TABLE ui_portal_branding
ADD CONSTRAINT fk_ui_port_bran2ui_port_reso2
FOREIGN KEY (portal_favicon) REFERENCES ui_portal_resource (id)
ON DELETE SET NULL
and all of a sudden we have a problem:
Introducing FOREIGN KEY constraint 'fk_ui_port_bran2ui_port_reso2' on table 'ui_portal_branding' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
This to me seems wrong. I'm not introducing a cycle. It's two tables with the foreign keys defined in a single direction. I guess it can technically be multiple cascade paths–if the same resource is the favicon and the logo it has to set 2 things null. But really? This is the deal breaker for the SQL Server engine? Oracle and Postgres both find this situation to be acceptable.
Is there a sensible workaround for this issue? I'm not interested in a solution involving triggers. Is there a better way to model the data? I was hoping that the resources table could service more than just the branding table, which led to the current FK placement. But maybe that is just not possible?

Resources