Doctrine, how to left join an entity which wasn't generated? - php

I have rights:
CREATE TABLE `rights` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE INDEX `U_name` (`name`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
and profiles:
CREATE TABLE `profile` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE INDEX `U_name` (`name`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
I want to connect profiles to rights and also profiles to profiles:
CREATE TABLE `profile_profile` (
`profile_id1` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`profile_id2` INT(10) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`profile_id1`, `profile_id2`),
INDEX `I_profile_id2` (`profile_id2`),
CONSTRAINT `FK_profile_profile-profile-1` FOREIGN KEY (`profile_id1`) REFERENCES `profile` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `FK_profile_profile-profile-2` FOREIGN KEY (`profile_id2`) REFERENCES `profile` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TABLE `profile_right` (
`profile_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`right_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`profile_id`, `right_id`),
INDEX `I_right_id` (`right_id`),
CONSTRAINT `FK_profile_right-profile` FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `FK_profile_right-rights` FOREIGN KEY (`right_id`) REFERENCES `rights` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
a better overview:
so I generate entities:
php apps/doctrine.php dev orm:generate-entities libs/ --no-backup
--extend="\Doctrine\Entity\BaseEntity"
here come the problems. The Profile and Rights entities gets created, while Profile_rights and Profile_profile not. How to use them then?

In doctrine, join tables are not represented by an Entity.
You can find a #ORM\ManyToMany in your entities, with a #ORM\JoinTable and all informations about your associations.
This is the representation of your join table(s), use getters and setters like said by #Richard to access them.
Get more informations in the Associations mapping (to see all kind of associations) and Working with associations (to learn how work with them) chapters of the documentation.
Hope you have a good experience with doctrine.
EDIT
After look more at your UML, at least one of your associations doesn't need a many-to-many (As said by the first answer), but if they really have join tables in SQL, and you imported them by reverse engineering, they will surely be exactly as they are in SQL (a many-to-many with join table).

If you want to access the joining entity on a ManyToMany relationship you need to break it down to a OneToMany, ManyToOne.
E.g.
Profile - OneToMany < ProfileRight > ManyToOne - Profile.
Whether you should is another question. You only need to do this if you want to store extra data on the join table.
With what you have there it's trivial to get rights for a profile. For any profile you have loaded you simply call
$profile->getRights()
and doctrine will (assuming your generated entity mappings are correct) transparently fetch all the associated Rights entities for you based on the join table.
Similarly if you add a Right to a profile:
$right = new Right();
$profile->addRight($right);
Doctrine will transparently add the join table entry for you.

Related

Database Announcements Table - Add Excutable Code Within

I have a database containing over 1,000 item information and I am now developing a system that will have this check the API source via a regular Cron Job adding new entries as they come. Usually, but not always the case, when a new item is released, it will have limited information, eg; Image and name only, more information like description can sometimes be initially withheld.
With this system, I am creating a bulletin to let everyone know new items have been released, so like most announcements, they get submitted to a database, however instead of submitting static content to the database for the bulletin, is it possible to submit something which will be executed upon the person loading that page and that bulletin data is firstly obtained then the secondary code within run?
, For example, within the database could read something like the following
<p>Today new items were released!</p>
<?php $item_ids = "545, 546, 547, 548"; ?>
And then on the page, it will fetch the latest known information from the other database table for items "545, 546, 547, 548"
Therefore, there would be no need to go back and edit any past entries, this page would stay somewhat up-to-date dynamically.
Typically you would do something like have a date field on your items, so you can show which items were released on a given date. Or if you need to have the items associated with some sort of announcement record, create a lookup table that joins your items and announcements. Do not insert executable code in the DB and then pull it out and execute it.
CREATE TABLE `announcements` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`publish_date` DATETIME NOT NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `items` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` VARCHAR(128) NOT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `announcement_item_lkp` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`announcement_id` int(11) unsigned NOT NULL,
`item_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `announcement_item_lkp_uk1` (`announcement_id`,`item_id`),
KEY `announcement_item_lkp_fk_1` (`announcement_id`),
KEY `announcement_item_lkp_fk_2` (`item_id`),
CONSTRAINT `announcement_item_lkp_fk_1` FOREIGN KEY (`announcement_id`) REFERENCES `announcements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `announcement_item_lkp_fk_2` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
With the announcement_item_lkp table, you can associate as many items to your announcement as you like. And since you have cascading deletes, if an item gets deletes, its lookup records are deleted as well, so you don't have to worry about orphaned references in your announcements, like you would it you just stuff a string of IDs somewhere.
You're already using a relational database, let it do its job.

sql - Is it possible to add foreign key to table which references to id colum on the same table

I have user account system which needs sub-account system. So there is the "main account" and accounts that can acces the main account data. I was thinking of users table where is field "subaccount" true or false. And then according to "parent-account" field open data from account id that the "parent-account" references to.
How do you feel about this?
SELECT * FROM users WHERE id = :id
if("SUBACCOUNT" exists){
SELECT * FROM users WHERE id= :parentaccount
echo parentaccounddata
$_session['parentaccount'] = false; //restrict certain features
}
If every account has only one parent it is a quite straight forward implementation. You want to create a user table with an id and a parent column. If the account is a main account you can just set the parent column to NULL. If it is a sub account you can set it to the id.
Table Creation
CREATE TABLE IF NOT EXISTS `accounts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
-- other parameters like name, ip, ...
`parent` int(11) NULL,
PRIMARY KEY (`id`),
FOREIGN KEY(`parent`) REFERENCES `accounts`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
) AUTO_INCREMENT=1 ;
To check if it is a main account or not just check if the parent column is set to NULL or not.
It is possible to have a table reference its own primary key via foreign key constraint (at least it is in MySQL, but I see no reason why other SQL based databases wouldn't allow it).
However I'd like to propose alternative solution. Adding new table which will connect accounts:
CREATE TABLE `accountConnection` (
`accountId` int NOT NULL,
`subAccountId` int NOT NULL,
PRIMARY KEY (`accountId`, `subAccountId`),
CONSTRAINT `fkAccountId` FOREIGN KEY (`accountId`) REFERENCES `account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fkSubAccountId` FOREIGN KEY (`subAccountId`) REFERENCES `account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I used MySQL for the example. This way you allow many to many relationship between accounts and sub-accounts, which I'm not sure if you need from your question, but is very flexible solution, even if you don't need it right now, should you ever need to change your system to allow for it

Foreign Key constraints in MySQL

I am a newbie to PHP. I have been given the code snippet below as homework:
CREATE TABLE `admin_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`statusdate` DATETIME DEFAULT NULL,
`type` INT(11) DEFAULT NULL
PRIMARY KEY (`id`)
) ENGINE=MYISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin
Why will it not be possible to set up any foreign key constraints using this table?
I have done some research on Google and I cant find a reason why foreign key constraints are not possible. Please help
You can do that like this :
CREATE TABLE `admin_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`statusdate` DATETIME DEFAULT NULL,
`type` INT(11) DEFAULT NULL,
INDEX (type),
FOREIGN KEY (type)
REFERENCES type(id)
ON UPDATE CASCADE ON DELETE RESTRICT,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=latin
Notice the engine INNODB insetad of MYISAM which doesn't permit foreign key.
Or using MySQLAdmin in the "structure" tab, click on the "relationals view" link below the table description.
Even it has been mentioned on comment before -- just to make it more prominent: MyISAM is not supporting foreign keys. So you will need to change the engine of your table to e.g. INNODB if possible.

ON DELETE CASCADE not working in MySQL

I am using the following SQL to create a table named app_info:
CREATE TABLE IF NOT EXISTS `app_info` (
`_id` int(11) NOT NULL AUTO_INCREMENT,
`app_name` varchar(50) DEFAULT NULL,
`app_owner` varchar(50) DEFAULT NULL,
`last_update` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
I am using the following SQL to create a table named tab_info:
CREATE TABLE `myDB`.`tab_info` (
`_id` INT NOT NULL AUTO_INCREMENT ,
`app_id` INT NOT NULL ,
`tab_title` VARCHAR(15) NOT NULL ,
PRIMARY KEY (`_id`) ,
UNIQUE INDEX `app_id_UNIQUE` (`app_id` ASC) ,
INDEX `app_tab_key` (`app_id` ASC) ,
CONSTRAINT `app_tab_key`
FOREIGN KEY (`app_id` )
REFERENCES `myDB`.`app_info` (`_id` )
ON DELETE CASCADE
ON UPDATE CASCADE);
But when I delete data from primary key table, the orphaned rows in the foreign key table are not being deleted automatically. Does anyone know what the problem could be?
The MyISAM storage engine doesn't support foreign key constraints. The constraint is parsed but silently ignored.
To fix your problem use the InnoDB engine instead (for both tables).
CREATE TABLE ( ... ) ENGINE = InnoDB ... ;
Instead of dropping your tables and recreating them you can also change the storage engine:
ALTER TABLE myDB.app_info ENGINE = InnoDB;
ALTER TABLE myDB.tab_info ENGINE = InnoDB;
After changing the engine you will need to add the foreign key constraint again.

How to implement cascading deletion of hierarchical data in MySQL?

I'm working on a project that has categories/subcategories. The database table for this is unique, with the following structure:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(11) NOT NULL auto_increment,
`publish` tinyint(1) NOT NULL default '0',
`parent_id` int(11) NOT NULL default '0',
`description` text NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
So, in case the category is a "base" one, the parent_id is zero, and if the category has a parent, it herds the parent id. What I want to know is this: I need to delete everything above and related with a category when choosing that option, a cascade-like deletion, but I only have this table (no foreign keys). How do I do that? (Without a large amount of queries.)
You can write a trigger to do it.
DELIMITER //
CREATE TRIGGER CatDelete AFTER DELETE ON categories
FOR EACH ROW BEGIN
DELETE FROM categories WHERE parent_id = old.id;
END//
DELIMITER ;
You can ALTER your MyISAM tables to InnoDB, and then define foreign key constraints with the ON DELETE CASCADE option.
ALTER TABLE categories ENGINE=InnoDB;
ALTER TABLE categories ADD CONSTRAINT
FOREIGN KEY (parent_id) REFERENCES categories (id) ON DELETE CASCADE;
Re your comment, the first thing I'd check is if you have some orphan categories, that is with parent_id pointing to a non-existant row. That would prevent you from creating the constraint.
SELECT c1.*
FROM categories c1
LEFT OUTER JOIN categories c2
ON (c1.parent_id = c2.id)
WHERE c2.id IS NULL;
Just my $0.02 - this not so trivial solution should require MVC to handle the cascade deletion.

Categories