How to sync three laravel models - php

I have three models: Users, Roles, Organizations.
And a pivot table
organization_role_user
user_id role_id organization_id
1 2 1
I can attach a user to an organization with a role like so:
$u = User::first();
$r= Role::first();
$u->roles()->first()->attach($r, ['organization_id'=> Organization::first()->id]);
and it works fine, the issue now its when I want to update a user: I want to sync pivot table.
I have tried
$u->roles()->sync([1=>['role_id'=>$r->id], 2=>['organization_id'=>$o->id]]);
But it gives me a queryexception saying that organization_id does not have a default value. Any idea how should I sync pivot table while I try to update user details, and note I do not want to keep any existing records on pivot for the user I am trying to update.

Let's assume that your user_id is 1
If you call for example:
$u->roles()->sync([1, 2]);
Laravel will attaching roles_id 1 and 2 to the user_id 1:
user_id role_id
------------------
1 1
1 2
If you want to pass additional intermediate table values you should do it like this
$u->roles()->sync([1 => ['organization_id' => 2], 2 => ['organization_id' => 2]]);
user_id role_id organization_id
-------------------------------------
1 1 2
1 2 2
You have to provide a organization_id in your sync function that's why Laravel return organization_id does not have a default value.

Related

Laravel the right way for Many to Many table structure

I'm having a many to many relation. Lets say that I have Many users that can be in Many groups and the other way around.
I wan't to make a table that contains the following columns
group_user
id / name / user_id / group_id
Since I have only 3 groups Junior, Middle and Senior I don't want to make another separated table for them. My idea here is to make the following approach:
Creating 3 records in the same table group_user with the following data
id / name / user_id / group_id
1 / Junior / null / null
2 / Middle / null / null
3 / Senior / null / null
So now when I want to insert a group_id I will use this 3 ID's that I just created with NULL user_id and group_id.
My records will look something like this:
id / name / user_id / group_id
4 / NULL / 125 / 1 -> The id of the Junior group that is in the same table.
5 / NULL / 125 / 3 -> The id of the Senior group that is in the same table.
Is this a valid way to do it? How wrong it is?
I would recommend following the correct procedures as follows:
Table 1 users table:
id | name
Table 2 groups table:
id | name
Table 3 group_user pivot table:
id | user_id | group_id
Note: Table 3 should never hold a nullable value
The relation would be as follows, right now we have two models, a User model and a Group model.
both will have a belongsToMany relation towards each other.
Here is the method you will use in the User model:
public function groups()
{
$this->belongsToMany(User::class);
}
And here is the method you will use in the Group model:
public function users()
{
$this->belongsToMany(Group::class);
}
This is the recommended way to continue.

MySQL Friend Table Two-Way Unique Index

My user table is something like:
Table: users
UserID Username
1 'Tom'
2 'x1x'
3 'Google'
And my current DB design for my friends table is:
Table: user_relationships
relationship_id to_user_id by_user_id status date
1 1 2 1 CURDATE()
2 1 3 2 CURDATE()
3 3 2 3 CURDATE()
The status column is:
1 - Friends
2 - Pending Request
3 - Ignored
And the purpose of the date column is to find how long user A and user B have been friends. The thing is, when Tom(id: 1) becomes friends with x1x (id: 2), Tom should not be able to send a friend request to x1x (id: 2). Likewise, Tom(id: 1) should not be able to send a friend to Google(id: 3). (instead an option of accepting should be given). I put a unique constraint on to_user_id and by_user_id, but the unique constraint is not both ways as in:
Table: user_relationships
relationship_id to_user_id by_user_id status date
1 1 2 1 CURDATE()
2 1 3 2 CURDATE()
3 3 2 3 CURDATE()
4 2 1 2 CURDATE()
Relationship ID 4 should NOT be allowed, and the query should be something like
INSERT INTO ... ON DUPLICATE KEY UPDATE relationship_id = relationship_id
But again, the unique constraint is not two way. How can I accomplish this? I really don't want to have to do
SELECT .... WHERE to_user_id = x AND by_user_id = y OR to_user_id = y AND by_user_id = x
To check for existence and then INSERT based on that. Is there a systematic flaw in my DB design for this that may be causing this problem?
If you are only interested in "there is a relationship between two users", you could sort the user IDs such that to_user_id is the smaller ID and and by_user_id is the greater ID or the other way around.
If you are interested in "who added whom", you can add two new columns user_1 and user_2 and do what I described above using the two new columns.
If the two IDs are sorted, a single unique key does what you want.
You can use the MySQL function GREATEST() and LEAST() to simplify your insert queries.
If this does not work for you, you can try to use triggers, to check if the input should be made or not, but I think this is a very ugly way and maybe you get problems with reading and writing from the same table.

Yii Use two different tables for login with different RBAC

I want to use two different tables for login. One for users and one for staff login. Currently there is only one table 'users' and I am using yii's built in RBAC and everything is working fine. Now I want to add the second table 'staff' for login without RBAC. So during login it will first check for the username in 'users' table and if it is not there then it will check in the 'staff' table and then check the password. I was able to do the logic for login successfully. But now the problem is RBAC. The "AuthAssignment" table has userid as foreign key of 'users' table, so if the staff id has a corresponding id in 'users' table then the system will provide access according to the role of the 'users' table. How can I avoid it if login is from staff table. Is it possible to give different role for 'staff' table or is it possible to not use role for 'staff' table.
Please help. Thanks in advance.
It's better to have one table for logins (because of FK and relationships).
[user]
ID | username | password | type
Than have staff and users info table with different fields:
[staff]
ID | user_id | workstation_id | favorit_tool | ...
[user_info]
ID | user_id | name | surname | address | ...
Than in Yii you can do relations:
[userModel]
public function relations() {
return [
'stuff' => [self::BELONGS_TO, 'stuff', 'user_id', 'condition' => 'type = '.static::TYPE_STUFF],
'info' => [self::BELONGS_TO, 'user_info', 'user_id',
];
}

Yii model relation using a field other than the primary key

I need to create a relation based on a field that is not the primary key. Many of the examples of how to do this are based on One to many and many to many relationships. I have tried the suggestions from the following without success
Relation in YII with not "ID" as primary key
Yii CActiveRecord: find related data, but not using the primary key
Yii Relations with non-Primary keys
Yii Model Relation Join (HAS_ONE)
I have the following table structure:
+------+---------+-----------+
| id | name | status_id |
+------+---------+-----------+
| 1 | service1| 1 |
+------+---------+-----------+
| 2 | service2| 2 |
+------+---------+-----------+
This is my table active_service. I also have the following table
+----------+----------+---------------------+-----------+
|id |related_id|related_text | text |
+----------+----------+---------------------+-----------+
|65 |1 |ActiveServices_status| Open |
+----------+----------+---------------------+-----------+
|72 |2 |ActiveServices_status| Active |
+----------+----------+---------------------+-----------+
|102 |3 |ActiveServices_status| Closed |
+----------+----------+---------------------+-----------+
This is my related_fields table
This table holds all the fields used for dropdown etc. The related_text tells us what it is for and the related_id is the id of the status and this is the field i need to link to. So the status_id in the active_service table relates to the related_id field of the related_fields table where the condition is met, ie the related_text is set to ActiveServices_status. How would I go about creating this relation. This is the best example of what I have done so far (in the ActiveServices model).
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'rl_status'=>array(self::BELONGS_TO,'RelatedFields','status_id','condition'=>'related_text = "ActiveServices_status"','on'=>'status_id = related_id'),
);
}
Any help would be appreciated.
So finally figured this thing out after trying about 100 different lines of code. So heres the solution that worked for me.
'rl_status' => array(self::BELONGS_TO, 'RelatedFields', '', 'foreignKey' => array('status_id'=>'related_id'),'condition'=>'related_text = "ActiveServices_status"',
For the record, in Yii 1.1.9 this was addressed; see issue #2706. It is not terribly obvious from the documentation, but this exact thing can be accomplished by putting an array in place of where the foreign key name would ordinarily go, with local column as key and foreign column as value.
So, for instance, if you had two local columns 'fk1' and 'fk2' referencing a composite unique key with columns 'col1' and 'col2' in the table for model "Foo", your entry in the relationship array would look like this:
'foo' => array(self::BELONGS_TO, 'Foo', array('fk1'=>'col1','fk2'=>'col2'))
Quoted from the documentation on CActiveRecord.relations():
In case you need to specify custom PK->FK association you can define it as array('fk'=>'pk').
'rl_status' => array(self::BELONGS_TO, 'RelatedFields', '', 'foreignKey' => array('status_id'=>'related_id'),'condition'=>'related_text = "ActiveServices_status"'

Database Relationships, I can't understand, MYSQL, PHP

I'm pretty new on using multiple tables in mysql, so i really don't understand some things..
Here is my php code from my role.model.php.
public function getRolePerms($role_id) {
$str = "SELECT t2.perm_desc FROM role_perm as t1
JOIN permissions as t2 ON t1.perm_id = t2.perm_id
WHERE t1.role_id = :role_id";
$sth = $this->db->prepare($str);
$sth->execute(array(':role_id' => $role_id));
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
print_r($data);
}
I called it in my role.php controller and fed a dummy $role_id of 1; getRolePerms(1);
I print_r($data) so i will know whether it will fetch a data but the output is just
"array()"
My question is, how does this works?
If i have these tables:
roles
permissions
role_perms
with role_id and perm_id columns, referencing the primary key of roles and permissions.
Kinda confused. Can someone really explain to me, in a simple language?
Did you check the role_perms table whether there is a row with a role_id of 1? Because there should be for the query to work.
how does this work?
First, try understanding the tables and their relationships. I would imagine your tables are somewhat like the following tables.
The ROLE table contains roles in your application (e.g. Admin, Guest).
Table 1: ROLE
-----------------------------------------------
role_id role_description
-----------------------------------------------
1 Admin
2 Guest
The PERMISSIONS table contains permissions in your application.
Table 2: PERMISSIONS
-----------------------------------------------
permission_id permission_description
-----------------------------------------------
1 Can create files
2 Can edit files
3 Can view files
The ROLE_PERMISSIONS table contains the permissions of the different roles in your application. Take the data below for instance, it means role 1 (Admin) has permissions 1, 2, and 3 (can create, edit, view files respectively).
Table 3: ROLE_PERMISSIONS
-----------------------------------------------
role_id permission_id
-----------------------------------------------
1 1
1 2
1 3
2 3
Now, try understanding the query.
SELECT t2.permission_description
FROM role_permissions as t1 JOIN permissions as t2
ON t1.permission_id = t2.permission_id
WHERE t1.role_id = 1
The first line instructs what data should be retrieved. It says to select the values from table t2 in the column permission_description. Therefore, possible results of the query are:
Can create files
Can edit files
Can view files
The second & third lines instructs from where data should be retrieved. It says to retrieve data from the joint table of role_permissions and permissions ON the condition:
t1.permission_id = t2.permission_id
Note: AS indicates that an alias will be used. Meaning, t1 is just the same as role_permissions.
From the PERMISSIONS and ROLE_PERMISSIONS tables above, the joint table would look like below.
Table 4: Joint ROLE_PERMISSIONS and PERMISSIONS
------------------------------------------------------------------------
role_id permission_id permission_id permission_description
------------------------------------------------------------------------
1 1 1 Can create files
1 2 2 Can edit files
1 3 3 Can view files
2 3 3 Can view files
This is the table where data will be retrieved.
So far, the instruction is to retrieve the permission_description from the joint table (Table 4).
The fourth line adds a constraint to the first instruction to only select permission_descriptions WHERE role_id = 1. Therefore, the complete instruction is
retrieve permission_descriptions from the joint table (Table 4)
where the role_id is 1.
Which in plain english means:
Retrieve the permission description of the role 'Admin'.
Thus the result of the query is:
Table 5: PERMISSION_DESCRIPTIONS of Admin
-----------------------------------------------
permission_description
-----------------------------------------------
Can create files
Can edit files
Can view files
Hope this helps you!
SELECT t2.perm_desc FROM role_perm as t1
JOIN permissions as t2 ON t1.perm_id = t2.perm_id
WHERE t1.role_id = :role_id;
this command tells MySQL to join role_perm and permissions by they're shared columns (perm_id), this is correct. but then we're missing the connectivity between the role ids in these 2 tables. the join results in one table containing the columns in the permissions table where the perm_id exists in the role_perm table as well, regardless of whether role_perm.role_id is pointing at the right role_id. the where clause filters only t1. this might result in getting permissions that other roles have as well. to solve this, change the SQL command to
SELECT t2.perm_desc FROM role_perm as t1
JOIN permissions as t2 ON t1.perm_id = t2.perm_id AND t1.role_id=t2.role_id
WHERE t1.role_id = :role_id;

Categories