How to setup Eloquent Relationship for Laravel Zizaco/entrust to get Role Permissions? - eloquent

In Zizaco/entrust's Laravel Entrust, how do you setup the Entrust class relationships with Eloquent so you can get the list of permissions a role has like so:
// get admin role
$adminRole = Role::find(1);
// get the permission of the admin role
$adminRolePermissions = $adminRole->permissions();
I've tried adding the following Eloquent relationships to the Role class:
class Role class {
public function permissions()
{
return $this->hasManyThrough('App\Permission', 'App\PermissionRole', 'role_id', 'permission_id');
}
}
With this, when I get an instance of a role and try to get its permissions like so:
$adminRolePermissions = $adminRole->permission();
this error occurs:
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'permission_role.id' in 'on clause' (SQL: select permissions.*,
permission_role.role_id from permissions inner join
permission_roles on permission_role.id =
permissions.permission_id where permission_role.role_id = 1)
Of course, I have already created the following models: PermissionRole for the permission_role table.

permission_role.id is invalid. Your table permission_role should have columns, named "permission_id" and "role_id" by default because of "Eloquent ORM Relationships".
May be you have override your Model's Primary Key name.
Please check that Config.entrust file has the same attributes as your Models have.

Related

Entity Framework Many to Many and existing data post from angular

Entity Framework from Database First created the Table model classes having many to many relationships in C# WebApi.
Table ACCOUNTS and table METADATA have a many-to-many relationship between them.
I want to add a new entry on ACCOUNTS table and link this entry with some existing entries from METADATA table. How can I do this using AngularJS to post data?
I am sending this data on $http:
var account: {
Title: 'Title',
User: 'User',
METADATA: [
{
Name: 'value1'
},
{
Name: 'value2'
}]
}
The account variable above is based on ACCOUNTS class which is being read by the C# web api using POST and [FromBody] like this:
[HttpPost]
public async Task<IHttpActionResult> Add([FromBody]ACCOUNTS account)
{
db.ACCOUNTS.Add(account);
await db.SaveChangesAsync();
int accountId = account.AccountId;
return Ok(accountId);
}
I am getting an error of primary key violation of existence of value1 and value2 on table METADATA.
This is correct because the values exist in the table.
But actually I want these values to be linked to the "intermediate table" which links ACCOUNTS and METADATA as many-to-many relationship, and not to be added.
Any solution to this scenario?
Before inserting the passed disconnected account object, you need to map the child METADATA objects to existing database entities. For instance, using something like this:
var medataNames = account.METADATA.Select(e => e.Name);
account.METADATA = db.METADATA.Where(e => metadataNames.Contains(e.Name)).ToList();
db.ACCOUNTS.Add(account);
// ...

Why can't EF handle two properties with same foreign key, but separate references/instances?

Apparently, EF6 doesn't like objects that have multiple foreign key properties that use the same key value, but do not share the same reference. For example:
var user1 = new AppUser { Id = 1 };
var user2 = new AppUser { Id = 1 };
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
When I attempt to insert this record, EF throws this exception:
Saving or accepting changes failed because more than one entity of type
'AppUser' have the same primary key value. [blah blah blah]
I've discovered that doing this resolves the issue:
var user1 = new AppUser { Id = 1 };
var user2 = user1; //same reference
I could write some helper code to normalize the references, but I'd rather EF just know they're the same object based on the ID alone.
As for why EF does this, one explanation could be that its trying to avoid doing multipe CRUD operations on the same object since separate instances of the same entity could contain different data. I'd like to be able to tell EF not to worry about that.
Update
So it's as I suspected per my last paragraph above. In absense of a means to tell EF not to do CRUD on either instance, I will just do this for now:
if (address.ModifiedBy.Id == address.CreatedBy.Id)
{
address.ModifiedBy = address.CreatedBy;
}
Works well enough so long as I am not trying to do CRUD on either.
Update2
I've previously resorted to doing this to prevent EF from validating otherwise-required null properties when all I need is the child entity's ID. However, it doesn't keep EF from going into a tizzy over separate instances with the same ID. If it's not going to do CRUD on either AppUser object, why does it care if the instances are different?
foreach (var o in new object[] { address.ModifiedBy, address.CreatedBy })
{
db.Entry(o).State = EntityState.Unchanged;
}
If you get AppUser from context, then you will not need to do anything, because Entity Framework will track entities:
var user1 = context.AppUsers.Find(1);
var user2 = context.AppUsers.Find(1);
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
Now, they both will point to same objects and will not cause to conflict.
You can add two extra properties to have the Id for the main objects which is the AppUser, then you can use only one AppUser object and reference it for both the created and modified by properties.
CreatedById = user1.Id,
ModifiedById = user1.Id
Otherwise, your code will end up by saving two instances of AppUser with the same primary key.
Another approach is to set both the foreign key properties to only one AppUserobject
The explanation is that EF's change tracker is an identity map. I.e. a record in the database is mapped to one, and only one, CLR object.
This can be demonstrated easily by trying to attach two objects with the same key:
context.AppUsers.Attach(new AppUser { Id = 1 });
context.AppUsers.Attach(new AppUser { Id = 1 });
The second line will throw an exception:
Attaching an entity of type 'AppUser' failed because another entity of the same type already has the same primary key value.
This also happens if you assign
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
Somewhere in the process, user1 and user2 must be attached to the context, giving rise to the exception you get.
Apparently, you have a function that receives two Id values that can be different or identical. Admittedly, it would be very convenient if you could simply create two AppUser instances from these Ids, not having to worry about identical keys. Unfortunately, your solution ...
if (address.ModifiedBy.Id == address.CreatedBy.Id)
... is necessary. Solid enough, though.

Many to Many relation in JPA entities

I have 5 tables:
User:
username (PK)
Role:
role_id(PK)
role_name
Permissions:
perm_id(PK)
perm_name
User_role_rels:
ur_id(PK)
username(FK) -> user.username
role_id(FK) -> role.role_id
Role_perm_rels:
rp_id(PK)
role_id(FK) -> role.role_id
perm_id(FK) -> Permissions.perm_id
When I create JPA entities for these five tables I get List of UserRoleRels in User entity, but I would need list of permissions for this user. So, there should be a
List permList in User entity.
I am new to JPA and not sure how I can achieve this using annotations?
Since there is no direct relationship between User and Permission, it's correct that User doesn't have a permList collection property.
Assuming that your IDE has created correctly the entities according to your tables, you don't have ManyToMany annotations in your classes, but only OneToMany and ManyToOne. This makes a little cumbersome to fetch the permissions collection:
String userName = "";
User myUser = entityManager.find(User.class, userName);
for (UserRoleRels urr : myUser.getUserRoleRelsList()) {
Role r = urr.getRole();
for(RolePermRels rpr : r.getRolePermRelsList()) {
Permission p = rpr.getPermission();
}
}
If you don't have an extra property in the ManyToMany relation tables (in your case User_role_rels and Role_perm_rels), you can drop the PK field for both. This will let your IDE: (1) create simpler Entity classes, (2) avoid the creation of the Entity classes for the relation tables, (3) use the ManyToMany annotations. With that change, you'll be able to access the permissions more easily and in a more readable way:
String userName = "";
User myUser = entityManager.find(User.class, userName);
for (Role r : myUser.getRoleList()) {
List<Permission> pl = r.getPermissionList();
}
Useful links: EclipseLink UserGuide, JPA Wikibooks.

JPA, duplicate entry on persist in join table when using #ManyToMany relationship

I want to create custom JAAS authentication where my users and principals relationships are defined in JPA as shown:
class AuthUser
public class AuthUser implements Serializable {
// own properties
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int uid;
#Column(name="NAME",unique=true)
private String name;
// Join classes
#ManyToMany(fetch=FetchType.EAGER,cascade={CascadeType.MERGE})
#JoinColumn(name="PRINCIPALS_PRINCIPAL")
private Set<AuthPrincipal> principals;
}
class AuthPrincipal
public class AuthPrincipal implements Serializable {
// Defining roles
public enum Principal {
AUTHUSER, STUDENT, ADMINISTRATOR, TEACHER
}
#Id
#Enumerated(EnumType.STRING)
#Column(name="PRINCIPAL")
private Principal principal;
#ManyToMany(mappedBy = "principals")
#JoinColumn(name="USERS_USER")
private Set<AuthUser> users;
}
Maps to the following Table definition
Table authprincipal
===================
PRINCIPAL varchar(255) PK
Table authuser
==============
UID int(11) PK
EMAIL varchar(255)
NAME varchar(255)
PASSWORD varchar(255)
Table authuser_authprincipal
============================
users_UID int(11) PK
principals_PRINCIPAL varchar(255) PK
Now, I created a JSF file from which I call an action method that calls this one:
public void createUser(AuthUser newUser) throws UserNameExistsException, UserEmailExistsException {
AuthPrincipal role = authRoleFacade.find(AuthPrincipal.Principal.AUTHUSER);
if( role == null ){
role = new AuthPrincipal();
role.setPrincipal(AuthPrincipal.Principal.AUTHUSER);
authRoleFacade.create(role);
}
authUserFacade.create(newUser);
addPrincipalToUser(newUser, role);
}
The actual problem
I can create the first user. But I can't create the second user . Notice that at the second user I use the existing role object, and only cascade a merge operation.
The strangest thing is that it says it duplicates the 2-AUTHUSER key where 2 is the id of the new user so cannot already be in the database. What is wrong with it or with Eclipselink or with Me?
The error what eclipselink throws
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-AUTHUSER' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO AUTHUSER_AUTHPRINCIPAL (principals_PRINCIPAL, users_UID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="principals" sql="INSERT INTO AUTHUSER_AUTHPRINCIPAL (principals_PRINCIPAL, users_UID) VALUES (?, ?)")
This was all My fault. I was not careful enough :)
1) The problem occured because of my fault, though, I don't know why the error log talked about user id 2 when I only had one user, but i'm gonna tell a possible reason
The problem was: I wanted to persist an already persisted user. In my #SessionScoped #ManagedBean I created an AuthUser in the constructor. In the first registration it got persisted but I did not create a fresh one. When I wanted to register the next one what my program actually did was: changed the username, email and password of the already persisted AuthUser and wanted to persist it again.
Back to 1) I can imagine when I called persist the second time, Eclipselink actually persisted my Entity updating the user id to 2 in both the AuthUser table and the join table. Afterwards because I defined the Merge operation on AuthUser.principals, it wanted to update again the join table and that's when it messed up. If I had looked closely to the generated Queries in the log file, I think I could have figured it out myself.
I got the hint here: http://www.eclipse.org/forums/index.php/m/692056/#msg_692056

Many-to-Many Inserts with Entity Framework

Say I have two entities with about 20 properties per entity and a Many-to-Many relationship like so:
User (Id int,Name string, .......)
Issue (Id int,Name string, .......)
IssueAssignment (UserId,RoleId)
I want to create a new Issue and assign it to a number of existing Users. If I have code like so:
foreach(var userId in existingUserIds)
{
int id = userId
var user = _db.Users.First(r => r.Id == id);
issue.AssignedUsers.add(user);
}
_db.Users.AddObject(user);
_db.SaveChanges();
I noticed it seems terrribly inefficient when I run it against my SQL Database. If I look at
the SQL Profiler it's doing the following:
SELECT TOP(1) * FROM User WHERE UserId = userId
SELECT * FROM IssueAssignment ON User.Id = userId
INSERT INTO User ....
INSERT INTO IssueAssignment
My questions are:
(a) why do (1) and (2) have to happen at all?
(b) Both (1) and (2) bring back all fields do I need to do a object projection to limit the
fields, seems like unnecessary work too.
Thanks for the help
I have some possible clues for you:
This is how EF behaves. _db.Users is actaully a query and calling First on the query means executing the query in database.
I guess you are using EFv4 with T4 template and lazy loading is turned on. T4 templates create 'clever' objects which are able to fixup their navigation properties so once you add a User to an Issue it internally triggers fixup and tries to add the Issue to the User as well. This in turns triggers lazy loading of all issues related to the user.
So the trick is using dummy objects instead of real user. You know the id and you only want to create realtion between new issue and existing user. Try this (works with EFv4+ and POCOs):
foreach(var userId in existingUserIds)
{
var user = new User { Id = userId };
var _db.Users.Attach(user); // User with this Id mustn't be already loaded
issue.AssignedUsers.Add(user);
}
context.Issues.AddObject(issue);
context.SaveChanges();