How to model users and administrators in a database? - php

I'm quite new to mysql. I want to make a database in mysql for a school. This database should store teachers' information and give some of these teachers the possibilities to create groups.
So I created a database that contains a table group and a table professeur. Group has many-to-many relationship with teachers and vice-versa which derives another table Group_professeur. Here is a simple structure of the tables:
Professeur:
CREATE TABLE IF NOT EXISTS `professeur` (
`id_professeur` int(11) NOT NULL AUTO_INCREMENT,
`nom_professeur` varchar(50) NOT NULL,
`prenom_professeur` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id_professeur`),
UNIQUE KEY `LOGIN` (`login`),
UNIQUE KEY `MDP` (`passwd`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=103 ;
Group
CREATE TABLE IF NOT EXISTS `groupe` (
`id_groupe` int(11) NOT NULL AUTO_INCREMENT,
`nom_groupe` varchar(255) NOT NULL,
`id_prof_responsable` int(11) NOT NULL,
PRIMARY KEY (`id_groupe`),
UNIQUE KEY `nom_groupe` (`nom_groupe`),
KEY `id_prof_responsable` (`id_prof_responsable`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
ALTER TABLE `groupe`
ADD CONSTRAINT `fk_professeur_to_groupe` FOREIGN KEY (`id_prof_responsable`) REFERENCES `professeur` (`id_professeur`) ON UPDATE CASCADE
Groupe_has_teachers:
CREATE TABLE IF NOT EXISTS `groupe_professeur` (
`id_groupe` int(11) NOT NULL,
`id_professeur` int(11) NOT NULL,
KEY `id_groupe` (`id_groupe`),
KEY `id_professeur` (`id_professeur`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `groupe_professeur`
ADD CONSTRAINT `groupe_professeur_ibfk_2` FOREIGN KEY (`id_professeur`) REFERENCES `professeur` (`id_professeur`) ON UPDATE CASCADE,
ADD CONSTRAINT `groupe_professeur_ibfk_1` FOREIGN KEY (`id_groupe`) REFERENCES `groupe` (`id_groupe`) ON UPDATE CASCADE;
Teachers can modify only the group(s) they created(i.e they can insert and delete members from their groups). Also, not all teachers have the right to create and modify groups.
After creating the tables, I was wondering who will give them the appropriate rights to do all these stuffs. I thought about creating an administrator. The admin can give the rights to certain teachers to create and modify their own groups and can also revoke these privileges.
I created a table which will store an administrator but who will give the admin the necessary rights to do these. Which brings me back to square one. And this table might have some relationships with other tables in the database thereby deriving some unnecessary tables.
Anyway I thought about changing the professors table to something general like staffs and adding the admin to the table. And then adding these staffs to the database. That means creating a new staff corresponds to adding the staff's information to the table staffs and then adding this staff as a user to the database. From there I can use SQL functions like GRANT and REVOKE to each user.
I'm not sure if this method is very efficient because these means if the school has 1000 professors then it has 1000 users in it's database.
Is there any efficient way to tackle this problem? Thanks.

Controlling application user access using the built-in MySQL notion of a user is unorthodox. For a basic application, I would recommend having another column in the group table for an owner_user_id that would refer to the professor table. Then in the application code, check for that id when the group is being altered.
Good luck!

You'll want to take a look at Role-based Access Control
Another explanation by Tony Marston

Related

SQL::PDO remove path to image from db if recipe is removed [duplicate]

I am trying to figure out relationships and deletion options.
I have two tables, User and UserStaff, with a 1:n relationship from User to UserStaff (a user can have multiple staff members).
When my User is deleted, I want to delete all of the UserStaff tables associated with that User. When my UserStaff is deleted, I don't want anything to happen to User. I understand that this is a cascading relationship, but I'm not sure which way.
i.e. Do I select the existing foreign key in my UserStaff table and make it cascading, or do I create a new foreign key in User and set that to cascading?
Yes, it's possible. You should make the FK in UserStaff table. In this way:
User Table
CREATE TABLE `User` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
UserStaff Table
CREATE TABLE `UserStaff` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`UserId` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`Id`),
KEY `UserId` (`UserId`),
CONSTRAINT `UserStaff_ibfk_1`
FOREIGN KEY (`UserId`)
REFERENCES `User` (`Id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
From Wikipedia:
CASCADE
Whenever rows in the master (referenced) table are deleted (resp. updated), the respective rows of the child (referencing) table with a matching foreign key column will get deleted (resp. updated) as well. This is called a cascade delete (resp. update[2]).
Here, User is the master table, and UserStaff is the child table. So, yes, you'll want to create the foreign key in UserStaff, with ON DELETE CASCADE
It's been a while since I've used this, but here goes (btw, I use Toad for MySql - a great IDE, and it's free too - http://www.toadworld.com/Freeware/ToadforMySQLFreeware/tabid/561/Default.aspx!)
You need to add a Constraint to the User table. If you have an id column (and the corresponding foreign userid key in UserStaff) then the SouceColumn should be id, the destination table UserStaff and the destination column userid. You can then set the OnDelete action to be 'Cascade'
The other options are pretty self-explanatory - Restrict limits values to the values in the source column, Set Null sets the foreign key matches to Null and No Action does, er, nothing.
This stuff is very easy to do via the Toad IDE. I used MySqlAdmin tools for ages but recently discovered Toad (and it has diff and compare tools too!).
The ON DELETE CASCADE is specified on the foreign key in the UserStaff table. For additional info on foreign keys the MySQL documentation has a number of examples. The User table does not have a foreign key pointing to UserStaff, so it will not be affected by changes to the UserStaff table.
The easiest way might be to make two quick tables and try it out. But since you didn't I can tell you that the outcome will be that it work the way that you want to.
When you have a table User and a table UserStaff were a field in UserStaff uses a foreign key to reference a field in User; then if you delete a record from UserStaff that will be removed wihtout having any affect on the User table. The other way around will delete all records related to that record.
Short version: A field in UserStaff should reference a field in User with CASCADE

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 keys mysql and phpmyadmin

I'm developing a website with php + mysql(phpMyAdmin).
I got 2 tables: USERS and FOLLOWERS like in the link i've pasted here below:
These tables are created with a sql script that I paste here bellow:
DROP TABLE IF EXISTS `FOLLOWERS`;
CREATE TABLE IF NOT EXISTS `FOLLOWERS` (
`Follower1_Id` int(10) unsigned NOT NULL,
`Follower2_Id` int(10) unsigned NOT NULL,
PRIMARY KEY (`Follower1_Id`, `Follower2_Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
(I just paste the table "FOLLOWERS" due to in that table is where is the problem).
Due to i don't know why phpMyAdmin doesn't allow to insert foreign keys, the problem comes when i try to insert a follower based on the USERS table. For any reason i can insert the user nÂș5 when i only have 3 users and these users have 1,2,3 as a User_Id PK.
Apparently I used the relational mode that phpMyAdmin offers me but there's no result.
What can i do?
The table you created is an MyISAM table and unfortunately they don't support Foreign Keys.
http://www.sitepoint.com/mysql-myisam-table-pros-con/
In PHPMyAdmin you can easily convert it from an MyISAM to an InnoDB table. This should enable the foreign Key features you're after.
When you say :
phpMyAdmin doesn't allow to insert foreign keys, the problem comes
when i try to insert a follower based on the USERS table.
What type of error do you get?
Could you give us more information?
MyISAM table doesn't support Foreignkey and it only supports Primary key. But you can convert it into InnoDB table and then you can assign Foreign key to it.

Zend Framework 2 with zfc-rbac database population

There are numerous 'getting started' tutorials out there on how to implement zfc-user and zfc-rbac into Zend Framework 2. The github pages for zfc-user and zfc-rbac (https://github.com/ZF-Commons) are clear and the implementation is indeed pretty easy (as stated on many of the tutorials). I also found the SQL schemes which are needed for both zfc-user and zfc-rbac (/vendor/zf-commons/zfc-[user/rbac]/data/).
The creation of a user into the database is easy, since zfc-user already sets this up for you (http://example.com/user). Everything fine so far. Now I want to populate the roles, but it's not clear to me on how to populate the rbac tables correctly. The lack on information about this surprises me, since the zfc-rbac component is a popular module for the Zend Framework.
I understand the principal of Role Based Access Control and the population of the tables for the permissions and the table linking the permissions and roles together are clear, it's the role table that's not clear to me. I understand that you can have a role which has a parent role, but it's not clear how to populate the table with a parent role since there is a foreign key constraint which states the 'parent_role_id' has to be a 'role_id'.
Below is the SQL for the role table (this is the SQL provided by zfc-rbac):
CREATE TABLE `rbac_role` (
`role_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`parent_role_id` int(11) unsigned NOT NULL,
`role_name` varchar(32) NULL,
PRIMARY KEY (`role_id`),
KEY `parent_role_id` (`parent_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
ALTER TABLE `rbac_role`
ADD CONSTRAINT `rbac_role_ibfk_1` FOREIGN KEY (`parent_role_id`) REFERENCES `rbac_role` (`role_id`);
With the foreign key in place adding a parent role seems impossible?
INSERT INTO `rbac_role` (parent_role_id, role_name) VALUES (NULL, 'admin');
Basically my question is (and I feel very stupid for asking this) but how does an insert for a parent role look like? And if the insert statement I presented is in fact correct, do I always need to remove the foreign key before inserting a parent role?
Change your create table to the following:
CREATE TABLE `rbac_role` (
`role_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`parent_role_id` int(11) unsigned NULL,
`role_name` varchar(32) NULL,
PRIMARY KEY (`role_id`),
KEY `parent_role_id` (`parent_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
Notice that parent_role_id is NULL instead of NOT NULL. If parent_role_id is NOT NULL then that means that it has to have a parent but since the foreign key reference is to the same table there is no way to insert a parent row!
fyi: This issue has been fixed. Version 0.2.0 of zfc-rbac will allow NULL value as parent_role_id

InnoDB foreign key difficulty?

Below I have two tables
users and users_profiles
Both are innoDB and collation: utf8_general_ci
They are as follows:
users
CREATE TABLE `users` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`status` char(10) NOT NULL,
`username` varchar(15) NOT NULL,
`email` varchar(50) NOT NULL,
`password` char(32) NOT NULL,
`reg_date` int(11) NOT NULL,
`ip` varchar(39) DEFAULT NULL,
PRIMARY KEY (`uid`),
UNIQUE KEY `username` (`username`,`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
users_profiles
CREATE TABLE `users_profiles` (
`uid` int(11) NOT NULL,
`first_name` varchar(40) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
`gender` char(6) DEFAULT NULL,
`website` varchar(100) DEFAULT NULL,
`msn` varchar(60) DEFAULT NULL,
`aim` varchar(60) DEFAULT NULL,
`yim` varchar(60) DEFAULT NULL,
`twitter` varchar(15) DEFAULT NULL,
UNIQUE KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
After creating the tables in phpmyadmin I created a foreign key on the users_profiles table, code below is what phpMyAdmin created.
As follows:
ALTER TABLE `users_profiles`
ADD CONSTRAINT `users_profiles_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`) ON DELETE CASCADE ON UPDATE CASCADE;
basically the users_profiles.uid is a foreign key and references the users.uid
In phpMyAdmin I go to insert and fill in some sample data leaving the uid obviously to auto increment. When i have inserted a record in users table I goes into the users_profiles table and notice the users.uid is not inserted automatically in the users_profiles,
Is this normal?
Reason is when someone for example registers on a form, they will be asked for username, email and password, and i do a query in php to insert that data into users table, i thought that because i have a foreign key that it would also automatically insert a row in the users_profiles table with the uid from users table so there is a link between the user and there profile. But when i insert a record into users table the users.uid is not inserted into the users_profiles table.
I tried another example to see what would happen and this one works as i would expect due to the cascade on update and delete.
If i insert a row in users table and then manually insert the users.uid into users_profiles.uid (they are now linked) and add for example my first_name and last_name then in phpmyadmin delete the user from users table it deletes the row in the users_profiles table. This works like it should obviously as i don't want a user to be deleted and have there profile remain.
This has confused me as when I do create a form and a user signs up, they essentially would not have a profile because on signup no profile is created for them with there users.uid in the user_profiles table (no link between them) although I have a foreign key.
Can some explain why it's not working as I expect, maybe it should be working like I want it to but something is wrong or I am missing the whole point otherwise.
UPDATE
In reference to reply from #Mark Wilkins
I understand what you mean now. But something I am not 100% sure on is this:
User signs up, a record is created in users table; they login and visit profile page where the can fill it in and submit the form.
On processing the form am I right in thinking I would need to do the following:
user filled in profile form and submitted (first time they submitted profile as they are a new user), after validating data etc I first check to see if the uid in the users table match a uid in the users_profile table, if there's a match then UPDATE record with new values (this would mean the user has previously filled in there profile as on signup they don't have one) but if no match is found on uid from both tables then I would perform an INSERT query because no profile yet exists for the user. I take it that obviously I would store the uid from users table in session with other data on successful login and the uid in session would be the uid that is inserted into the users_profiles table in column uid? That way a link is created between two tables and if I now decide to delete the user there profile will also be deleted to.
Foreign key constraints are not designed to create rows. Their purpose is to ensure data integrity by forcing that a value in a child table that references a parent table value actually exists in that parent table and prevents a parent row from being deleted that has references to it in a child table.
On insert, the calling code must write rows into the two tables (first users then profiles).
If I followed the description correctly, it is working as expected. A foreign key relationship basically says that a parent must exist for a given child (a user must exist for a given user_profile in your example). It does not require the opposite (that a user_profile record exist for a user). It will never result in an INSERT being performed on the child table. You have to insert the record into the user_profile table and the foreign key relationship will guarantee that it is maintained.
Edit for the additional OP info: In general, yes I believe that is the thing you want to do. I have not dealt enough with web development to know if that particular process is correct. In either case, though (whether or not a profile record has been created), you will need to know which user to modify. My opinion about this, however, would be to create the associated user_profile record directly after creating the user record (just leave the informational fields empty in it). That way you know it exists when the go to edit it and you don't have to perform a MERGE style operation.

Categories