Laravel Making Acl Users with permissions(without ranks/groups) - php

I want to create users which I can grant permissions.
I created a permissions model, which contains following attributes (id|name|displayName|desc)
1|xyz.edit|Edit xyz| Allow to edit xyz
2|xyz.create|Create xyz| Allow to create xyz
Thus I want to create relations like following:
public function getPermissions(){
return $this->hasMany('App\Permission');
}
But it does not work. Is there any way to create relations like
user has a lot of permissions but without creating same permissions for user?
I can make user model like id|pass|login|...|permissions
and in permissions storage permissions id splited with "," and in the getPermissions() function make something like this:
public function getPerms(){
foreach (explode($this->permssions,',')as $id ){
//here load from database perm by id add to array and return
}
}
Or a second option I see in this tutorial https://www.youtube.com/watch?v=Kas2w2DBuFg is to make another table like user_perms with fields
id|user_id|perms|id
but what option is the best to do this?

Can you post the code in both your models? (the user model and permission model?) without seeing that, I can't see what type of relationship you're using, although it looks like you're using a one-to-many.
Either way...
The way you can have users an assign them permissions, without worrying about groups is by using a many-to-many relationship. This requires 3 tables. The user table, the permission table, and a pivot table.
You can read about many-to-many relationships here: https://laravel.com/docs/5.5/eloquent-relationships#many-to-many
But to give you a rundown...
User model
public function permissions(){
return $this->belongsToMany('App\Permission');
}
Permission model
public function users(){
return $this->belongsToMany('App\User');
}
create_users_table migration (field names don't really matter, just make sure you have the increments() one)
$table->increments('id');
$table->string('name');
(etc, standard migration stuff)
create_permissions_table migration (field names don't really matter, just make sure you have the increments() one)
$table->increments('id');
$table->string('name');
$table->string('long_name');
(etc, standard migration stuff)
and for the pivot table, you need to use the singular name of the two tables in alphabetical order (or at least, thats the default)
create_permission_user_table (these two field names are important, laravel expects these names, you don't need any other fields... you can also setup foreign key relationships if you want to be thorough)
$table->integer('permission_id');
$table->integer('user_id');
and then to give a user a permission, you would just do
// Grab your user and permission, however you do that...
$permission = \App\Permission::first();
$user = \App\User::first();
// Then save the permission to the user (or the other way around, but it makes more sense this way)
$user->permissions()->save($permission);
And that will let you have users with permissions :)
then you can access an array of permissions with $user->permissions and do whatever logic checks you need to check if they are allowed to do things!

Related

How to extend a User table with new table in laravel to hold more columns (not a pivot table)?

So in laravel I have a User table holding info such as id, home_address, email_address, phone_number, etc.
Users can participate in dinners (activity). If they don't show up to the dinners, admins should be able to ban the user from participating until they are manually unbanned.
The user table is "large enough" that it doesn't make sense to add new columns to the user table, but instead create a new table to keep the information seperate.
In my User migration file I have
Schema::create('dinner_ban', function (Blueprint $table) {
$table->unsignedBigInteger('id');
$table->foreign('id')->references('id')->on('users');
$table->dateTime('date_ban');
$table->dateTime('date_unban');
$table->string('reason')->nullable();
});
and in my User Model file I have
public function userBanned() {
return $this->belongsTo('App\Models\User', 'dinner_ban');
}
NOTE: dinner_ban does not have it's own individual Model Class.
The table is one way. When a user is banned, they are added to the dinner_ban table with today's date, an estimate future date where they would be unbanned (but of course can be before or after this date (in the future it would be automatic), and a reason for the ban (which doesn't need to be added/provided). When they are unbanned, they are removed from the table. No tracking is done as for how long or how many times someone has been banned. Its a simple check to see the user is banned or not. If they are banned, then they can not participate in dinners.
What I have above, is this a correct way of adding dinner_ban?
In the future there are 3 ways of using it.
If it's the admins, I want to return the whole DB and display the results on the page.
If it's the individual user, I want to check if their id exists in the ban database and return true (with when the ban started, when it ends, and the reason) or false.
And of course admins can edit the individual banned members and edit the reason/estimated unban list.
It is not a good idea to create a custom table like "dinner_ban" to store this kind of information.
It is better to create (if you don not already have) a "user_preferences" table with something like the following structure:
Schema::create('user_preferences', function (Blueprint $table) {
$table->unsignedBigInteger('id');
$table->foreign('user_id')->references('id')->on('users');
$table->json('value')->nullable();
});
and define the relation (and Cast) in "User" Model:
public function userPreferences() {
return $this->hasOne(UserPreferences::class);
}
Note: It's better to make "user_id" unique in preferences table and set "value" as json.

Deleting a model and all references to it in his relationships in Laravel

I'm still new to Laravel and Eloquent but joining on a project previously built.
I have a User model that have many relationships like for example Actions(HasMany), Roles(BelongsToMany), Type(BelongsTo),..and many others...
What I want to do is to delete an User model and it's data and when I'm doing it, deleting all traces of him in his relations but not the content itself, basically I want to keep his relations models (Actions, Roles, Type,...) in my database but deleting his FK so that it cannot be linked to my user anymore while keeping the entry previously associated to him.
I tried a few things without success like
$user = User::findOrFail($id)
$user->delete();
// This one giving me a SQLSTATE[23000] The DELETE statement conflicted with the REFERENCE constraint
$user->actions()->detach()
// or
$user->actions()->dissociate()
// But undefined for HasMany relations
and I was wondering if there was a "clean" and simple way to do it other than updating all those Foreign_Keys to a "NULL" value in each of the relationships
$user->userActions()->update(['id_user' => null]);
$user->userRoles()->update(['id_user' => null]);
//...and on and on... before being able to do a
$user->delete();
I hope I was clear enough.
Thanks.
You can delete the user as:
$user = User::findOrFail($id)
$user->delete();
The problem here is that, you don't want to delete all the information from the user in the other tables. In SQL you define Foreign Keys specially for this purpose so you don't have data in the table that is "lost in space" because it does not relate to anything. This builds (space) the tables with information that is difficult to access because it's not related to nothing.
For that purpose usually people use "ON DELETE CASCADE" to delete all reference (related by FK) in the other tables. If you don't have this you have to manually delete the information in other tables before deleting the user.
My recommendation is rethink why you want to remove the user and keep the data, if you want to keep the data just disable the user, at the end of the day you will want to know the information of the user for the data in the other tables.
I hope this can help and clarify your issue.
If you are using Hard Delete, I recommand using the ondelete set null :
Schema::table('roles', function (Blueprint $table) {
$table->unsignedInteger('id_user')->nullable();
$table->foreign('id_user')->references('id')->on('users')->onDelete('set null');
});
Schema::table('actions', function (Blueprint $table) {
$table->unsignedInteger('id_user')->nullable();
$table->foreign('id_user')->references('id')->on('users')->onDelete('set null');
});
No need for model event or managing it yourself, mysql is managing everything for you
You must delete all related records first , then delete the record itself :
$user = User::findOrFail($id);
$user->actions()->detach();
$user->delete();
or you may register closures that execute when various model events are dispatched. Typically, you should register these closures in the booted method of your model :
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
protected static function booted()
{
static::deleting(function ($user) {
$user->actions()->detach();
});
}
}
Reference

How to setup laravel hasOne relationships with itself?

I am working on an application in Laravel where users can be matched up with one users. I am trying to figure out how to setup the hasOne relationship between users.
Here is how I have done it so far. Users when they are created have a match_id which represents the user that they are matched to. When they are matched this record gets updated. Here is my table:
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('event_id');
$table->foreign('event_id')->references('id')->on('events')->onDelete('cascade');
$table->unsignedBigInteger('match_id')->nullable();
$table->foreign('match_id')->references('id')->on('users')->onDelete('cascade');
$table->string('name');
$table->string('email');
$table->timestamps();
});
In the user model I have set up this relationship:
public function match() {
return $this->hasOne(User::class);
}
but as of right now I cannot do something like this and get the expected matched user's information:
$user->match()->get();
After reading the docs here, I see that in such situations you can setup the foreign key and local key. Nevertheless I am having a difficult time figuring this out.
Could someone give me some guidance on how I could do this? I am also open to any suggestions on different implementations.
-----edit------
after discussing with some people in the comments I made some progress in my endeavors. I am able to get some results and have experimented by logging the matches. For whatever reason though I am getting the wrong matches. Look below for more information:
in my controller I log the users name and match after it has been formed:
Log::error($user->match);
Log::error('has matched with:');
Log::error($user);
Log::error('participant:');
in the database here are the users and their matches:
yet in the logs, the match is not the same:
Since the match_id is on the users table, the relationship should be belongsTo, not hasOne.
belongsTo is specified on the model that contains the foreign key.
Also, you should specify the foreign key:
return $this->belongsTo('App\User', 'match_id');

Laravel many to many with a pivot relationship.

User
uid
Provider
pid
Resolution
rid
ProviderResolution
prid
pid
rid
active
ProviderResolutionUser
prid
uid
class Provider extends Model {
public function resolutions()
{
return $this->belongsToMany('App\Models\Resolution')->withPivot('active')->withTimestamps();
}
}
class Resolution extends Model {
public function providers()
{
return $this->belongsToMany('App\Models\Provider')->withPivot('active')->withTimestamps();
}
}
class User extends Model
{
}
Trying to create a Eloquent relationship with this.
I'm trying to figure out how to fit user into this model. It seems like it's suppose to belongsToMany. Do I need to create a class that represents the pivot?
Then from the case of the User how would I query a list resolutions?
You didn't ask but I personally think it's a lot easier to let the primary key of each table be 'id.' Also, in the case of ProviderResolution, unless you have a specific case for it, you don't need (and shouldn't use) 'prid' at all. Just 'pid', 'rid' and 'active' should be sufficient. The 'pid' and 'rid' make the composite primary key on their own. If you add yet another key ('prid'), then there will be a three-key composite which will technically enable you to have duplicates with your other two primary keys. Yuck. Example: PRID:1, PID:1, RID:1, then PRID:2, PID:1, RID:1. Now you have duplicates but your record is technically still unique because of the PRID key. But, maybe you want it this way for some reason?
For the answer I'm going to assume you are using Laravel 5.4+.
So first off, you don't need a class for the pivot. And secondly, you are currently trying to create a relationship between the user and the existing pivot table between Provider and Resolution by creating a table called 'provider_resolution_user'. If you want to query resolutions for a user, just use the relationship methods which gives you access to the attributes on the pivot table and the related models/tables.
First, setup the 'hasMany' relationships in both classes: Users and Resolutions (Providers already has a relationship to Resolutions, so you can use that relationship if you want to see the related Provider.) Then you'll need a pivot table called 'resolution_user'. Put the 'uid' and the 'rid' in the table. Make the relationships to the corresponding foreign key fields to their parent tables.
Now you can access the relationship directly like:
$user->resolutions->rid (or whatever the attribute is you want)
The previous example assumes you have already created a way to insert records into the pivot table (resolution_user) that relate the user and the resolution together.
If you want to access one of the attributes on the pivot table, 'pivot' creates an object instance with it's own attributes (from the table). You can access it like this:
$user->resolutions->pivot->active;
Of course, these methods are chainable so if you just wanted to see the active resolutions, you could also add a ->where statement.
Hope that helps and wasn't too muddy. I'm happy to clarify any points if need be.
EDITED ANSWER:
Because what you want to do is to disable a row in the provider_resolution table and have that reflect on the correct user, then just create a relationship in both the User model and the Resolution model. So when you disable a row in provider_resolution (pid, rid, active), you can lookup the appropriate user to update by using the inverse relationship between resolution and user. This should give you the user that is assigned to that particular resolution/provider combination. If for some reason you do need to find the user based on a unique combination of the TWO: resolution AND provider, then we might need to talk about polymorphic relationships. Let me know.

How to retrieve data from additional columns in a pivot table and also be able to update it in laravel 5.2

I was working on making a group functionality for my website which uses a many to many relationship between groups and users.
My User model looks like this:
public function groups(){
return $this->belongsToMany('App\Group')->withPivot('role')->withTimestamps();
}
My Groups model looks like this:
public function users(){
return $this->belongsToMany('App\User')->withPivot('role')->withTimestamps();
}
So my third column has the name of role which is a string variable and is set to a default of "member" for members of my group and I set it to "admin" for the actual user who creates a new group. But I want the admin to have the option of making multiple members admins as well which would require me to check weather the current current user who sent the request is an admin or not. If he is, then I wanna be able to take his request of making a member an admin which would require me to update the role for that particular "member" to an "admin".
In the laravel documentation it only shows you how to attach and detach data in a pivot table and else where I have only seen methods of retrieving data from the first two columns but how can I do the same for additional columns and also be able to update it using the updateExistingPivot method?
You could access the column simply using pivot e.g :
$user->pivot->role
Take a look at Retrieving Intermediate Table Columns in documentation Eloquent Relationships.
Hope this helps.

Categories