I have three models user,roles,model_has_roles,when i assign any role to the user from the roles table it's creating an instance of the model in model_has_roles table.I am using removeRole() laravel method for removing roles ,i am giving some details what are the roles present inside my database like(super-market,notification,all...).it's deleteing all roles except notification,all.
$roleName = implode(' ,',$roleName); // "super-market" or "all"
foreach ($roles->pluck('name')->toArray() as $roleName) {
$user->removeRole($roleName);
}
Now what i need is irrespective of the role(any role) i want to delete that role ,some of the roles it's deleteing and remove the instance of the model from the model_has_roles and some of the roles are not deleted (for example all,notification),please help me to fix this issue
there is no built-in laravel package for managing roles and permission and removeRole() is not in laravel (I dont know why you did wrote removeRole laravel method ), there is a package called laravel-permission that has the same table design of yours. if you are using this package you can find docs in here. if you are writing your own try debugging it with dd() and tinker to find out what's wrong with some that they won't be deleted.
I got it reverse in first place, you must use explode instead, and move first line into foreach loop
foreach ($roles->pluck('name')->toArray() as $roleName) {
$roleName = explode(',',$roleName);
$user->removeRole($roleName);
}
Related
In a fresh installation of Laravel 8.20.1, I have created two models Company and User with a pivot table between to facilitate a many-to-many relationship. The pivot has a role attribute.
I can add a user to a company using:
$company->users()->attach($user);
I've added a utility method addUser so that I can first check existence of the relationship to avoid duplication:
public function addUser(User $user) {
if($this->users->contains($user)) {
// if the user already has a role, update it
Log::info("User #{$user->id} present - updating");
$this->users()->updateExistingPivot($user, ['role' => 'user'], true);
} else {
// if the user doesn't have a role, add it
Log::info("User #{$user->id} not present - adding");
$this->users()->attach($user, ['role' => 'user'], true);
}
}
The first time I run this using a refreshed database, it should see that the user is not yet related to the company and run the else part of the switch to add a new user. Running this in Tinker, it appears to do this - and the logs show 'User #1 not present - adding' - but when I check for presence using contains, it returns false:
$user = User::factory()->create();
$company = Company::factory()->create();
$company->addUser($user);
print_r($company->users->contains($user)); //false
I've tried logging the queries for this function, and I they look fine - one for checking existence of the user, a second for inserting the pivot.
Also, I can see a pivot record for user #1 and company #1, and if I then test this in Tinker, I get true:
print_r(Company::find(1)->users->contains(User::find(1))); // true
It's almost as if the database is running async, which I know isn't the case in PHP. I'm using Sqlite v3.31.0.
The issue is definitely in my addUser method, as if I replace this call in Tinker with calling attach directly, it works.
I'm really keen to use this utility method (and others), because:
I want to avoid multiple pivot records for the same company/user (without using compound indexes)
I need several more utility methods such as addAdmin, addOwner, etc
I'm using spatie/laravel-menu and spatie/laravel-persmissions in my laravel project.
I have created a permission, assigned it to a role, and assigned the role to my user. This works fine.
Then I have generated a menu the middleware way using a macro like so:
\Menu::macro('main', function () use ($request) {
return \Menu::new()
->withoutWrapperTag()
->withoutParentTag()
->setActiveClassOnLink()
->route('preparation', 'Anstehende Termine')
->route('postprocessing', 'Nachbereitung')
->routeIfCan('administrate', 'protocols', 'Protokolle')
->addItemClass('nav-link')
->setActive($request->url());
});
In my application I have two User models with different connections:
App\User; using connection_a with database db_a and
App\DirectoryA\User; using connection_b with database db_b
In the auth config the first one is defined, and using Auth::user()->can('administrate') works fine, even in the Middleware that defines the menu.
Since I have added the menu item via routeIfCan, I'm getting an error. It tells
Base table or view not found: 1146 Table 'db_b.permissions' doesn't exist (SQL: select permissions.*, model_has_permissions.model_id as pivot_model_id, model_has_permissions.permission_id as pivot_permission_id, model_has_permissions.model_type as pivot_model_type from permissions inner join model_has_permissions on permissions.id = model_has_permissions.permission_id where model_has_permissions.model_id = 1 and model_has_permissions.model_type = App\User)
What is going wrong here? It should use the App\User model. Placing a dd() at the point the framework throws the exception shows me the correct connection...
Please help.
this mean table permissions not exist on your database maybe you forgot to run php artisan migrate after install laravel-permission?
A member of spatie helped to solve the problem:
Under the hood, routeIfCan calls app(Gate::class)->allows($ability, $ablityArguments). I assume Gate behaves slightly different than Auth::user() when it comes to multiple guards.
I don't see much room in routeIfCan to add an additional $guard or $connection argument, so I suggest you use $menu->addIf(Auth::user()->can('administrate'), ...) instead.
So what i'm basically trying to do is refactor my long bit of code to something more simpler. I found this snippet of code at this website and I don't really understand what's going on inside the code. I don't think that this snippet of code will work considering I am using different policies and methods then what's standard.
Code Snippet From Site:
//PermissionsServiceProvider.php
public function boot()
{
Permission::get()->map(function($permission){
Gate::define($permission->slug, function($user) use ($permission){
return $user->hasPermissionTo($permission);
});
});
}
Can someone please explain what exactly is going on in this bit of code?
My Code:
// Posts Policy
Gate::define('post.view', 'App\Policies\Blog\PostsPolicy#view');
Gate::define('post.create', 'App\Policies\Blog\PostsPolicy#create');
Gate::define('post.update', 'App\Policies\Blog\PostsPolicy#update');
Gate::define('post.delete', 'App\Policies\Blog\PostsPolicy#delete');
Gate::define('post.publish', 'App\Policies\Blog\PostsPolicy#publish');
Gate::define('post.edit', 'App\Policies\Blog\PostsPolicy#edit');
Gate::define('post.global', 'App\Policies\Blog\PostsPolicy#global');
// Categories Policy
Gate::define('category.view', 'App\Policies\Blog\CategoriesPolicy#view');
Gate::define('category.create', 'App\Policies\Blog\CategoriesPolicy#create');
Gate::define('category.update', 'App\Policies\Blog\CategoriesPolicy#update');
Gate::define('category.delete', 'App\Policies\Blog\CategoriesPolicy#delete');
Gate::define('category.edit', 'App\Policies\Blog\CategoriesPolicy#edit');
Gate::define('category.global', 'App\Policies\Blog\CategoriesPolicy#global');
// Tags Policy
Gate::define('tag.view', 'App\Policies\Blog\TagsPolicy#view');
Gate::define('tag.create', 'App\Policies\Blog\TagsPolicy#create');
Gate::define('tag.update', 'App\Policies\Blog\TagsPolicy#update');
Gate::define('tag.delete', 'App\Policies\Blog\TagsPolicy#delete');
Gate::define('tag.edit', 'App\Policies\Blog\TagsPolicy#edit');
Gate::define('tag.global', 'App\Policies\Blog\TagsPolicy#global');
// Parts Section Policy
Gate::define('part.section.view', 'App\Policies\Parts\PartSectionsPolicy#view');
Gate::define('part.section.create', 'App\Policies\Parts\PartSectionsPolicy#create');
Gate::define('part.section.update', 'App\Policies\Parts\PartSectionsPolicy#update');
Gate::define('part.section.delete', 'App\Policies\Parts\PartSectionsPolicy#delete');
Gate::define('part.section.edit', 'App\Policies\Parts\PartSectionsPolicy#edit');
Gate::define('part.section.global', 'App\Policies\Parts\PartSectionsPolicy#global');
// Parts Policy
Gate::define('part.view', 'App\Policies\Parts\PartsPolicy#view');
Gate::define('part.create', 'App\Policies\Parts\PartsPolicy#create');
Gate::define('part.update', 'App\Policies\Parts\PartsPolicy#update');
Gate::define('part.delete', 'App\Policies\Parts\PartsPolicy#delete');
Gate::define('part.edit', 'App\Policies\Parts\PartsPolicy#edit');
Gate::define('part.global', 'App\Policies\Parts\PartsPolicy#global');
// Admin Management Policy
Gate::define('admin.global', 'App\Policies\AdminManagementPolicy#global');
// User Management Policy
Gate::define('user.global', 'App\Policies\UserManagementPolicy#global');
Is there a way to do this as a foreach loop from my permissions table? Here's some Pseudo code:
foreach($permissions as $permission) {
Gate::define($permission->slug, 'App\Policies\' . $permission->category . 'Policy#' . $permission->name);
}
Question: Any way to make my code more compact and easier to read like the code snippet from the website?
First of all, the author of that article did not use policies at all, he created a permissions table and then bound the permissions he created to laravel gates by the code snippet
Permission::get()->map(function($permission){
Gate::define($permission->slug, function($user) use ($permission){
return $user->hasPermissionTo($permission);
});
});
Let's break it line by line
Permission::get() // Query all permissions defined in permissions database table
->map(function($permission){ // Foreach permission do the following
Gate::define($permission->slug, // Create new gate with the permission slug
function($user) use ($permission){
return $user->hasPermissionTo($permission); // the user table has many to many relation with permissions table, here we only check if $user is associated with $permission
});
});
To make your code more dynamic, I suggest you to do the following:
Database structure
Create permission database table
Create roles database table
Create permission_role pivot database table
Create role_user pivot database table
Define Relationships
Role has many permissions ( many to many relationship, define it with belongsToMany )
Permission belongs to many roles ( many to many relationship, define it with belongsToMany )
User has many roles ( many to many relationship, define it with belongsToMany )
Reduce the number of global permissions
By utilising Gate::before you can allow specific user who has global or root permission to authorise all defined abilities:
Gate::before(function ($user, $ability) {
if ($user->hasPermission('root-access')) {
return true;
}
});
If you implement the database permissions you no longer need to create policies for every model, and the gates will be defined using the above code dynamically.
Personally, your existing code is fine. It works. It is readable. While it might become more verbose as your app grows, it also might not. So why improve it?
That said, here are some ideas. Most of your code is a mapping between permission and policy implementation. For example 'part.view' maps to 'App\Policies\Parts\PartsPolicy#view. The "weight" of this mapping can't be removed: it can only be moved.
You might consider moving it to a simpler configuration file, something that looks like this:
// config/permission-map.php
<?php return [
'post.view' => 'App\Policies\Blog\PostsPolicy#view',
'post.create' => 'App\Policies\Blog\PostsPolicy#create',
'post.update' => 'App\Policies\Blog\PostsPolicy#update',
'post.delete' => 'App\Policies\Blog\PostsPolicy#delete',
// etc...
];
Then in your boot you read that config and iterate:
// boot permissions
$permission_map = require_once('config/permission_map.php');
foreach ($permission_map as $permission => $policy_implementation) {
Gate::define($permission, $policy_implementation);
}
Advantage: adding a new policy mapping only changes the most salient information, and you don't have to think about how to make that mapping happen - today that is Gate::define but tomorrow maybe it's Sentry::policy. Additionally, by separating data from the code, you can test the code more freely.
Another approach could be annotations: in the DocBlock of your policy implementation, you write your own annotation syntax, which you then parse and compile into the configuration file. Along the lines of
namespace App\Policies\Blog;
class PostsPolicy {
/**
* #permission post.view
*/
public function view() { /* ... */ }
}
I, personally, am not a fan of this: it adds a layer of inner framework whose value I find it hard to measure.
So, going into the problem straight away. someone told me that we dont need to make a pivot table if we only want to have ids of the table. laravel can itself handle this situation. I dont know how this works. I have a table community and another table idea. relation is like this;
One community can contain many ideas and an idea can be found in many
communities.
Relation in idea Model:
public function community() {
return $this->belongsToMany('App\Community')->withTimestamps();
}
Relation in community Model:
public function idea() {
return $this->belongsToMany('App\idea');
}
Now i want to fetch all the records related to a single community to show on its page Let's say the community is Arts.
Here is Controller function:
public function showCommunities($id) {
$community = Community::findOrFail($id)->community()->get();
return view('publicPages.ideas_in_community', compact('community'));
}
When i attach ->community()->get() to the Community::findOrFail($id) Then it throws the error
SQLSTATE[42S02]: Base table or view not found laravel
Any help would be appreciated.
Edit:
Logically, this piece of code Community::findOrFail($id)->community()->get() should be like this Community::findOrFail($id)->idea()->get(). Now it is true but it has little issue. it throws an error
Fatal error: Class 'App\idea' not found
The way you define the many-to-many relation looks ok - I'd just call them communities() and ideas(), as they'll return a collection of objects, not a single object.
Make sure you use correct class names - I can see you refering to your model classes using different case - see App\Community and App\idea.
In order to find related models, Eloquent will look for matching rows in the pivot table - in your case it should be named community_idea and have 3 fields: community_id, idea_id and autoincrement primary key id.
With that in place, you should be able to get all ideas linked to given community with:
$ideas = Community::findOrFail($communityId)->ideas;
If you need communities linked to given idea, just do:
$communities = Idea::findOrFail($ideaId)->communities;
You can read more about how to use many-to-many relationships here: https://laravel.com/docs/5.1/eloquent-relationships#many-to-many
someone told me that we dont need to make a pivot table if we only want to have ids of the table
The above is not true (unless I've just misunderstood).
For a many-to-many (belongsToMany) their must be the two related table and then an intermediate (pivot) table. The intermediate table will contain the primary key for table 1 and the primary key for table 2.
In laravel, the convention for naming tables is plural for your main tables i.e. Community = 'communities' and Idea = 'ideas'. The pivot table name will be derived from the alphabetical order of the related model names i.e.
community_idea.
Now, if you don't want/can't to follow these conventions that's absolutely fine. For more information you can refer to the documentation: https://laravel.com/docs/5.2/eloquent-relationships#many-to-many
Once you're happy that you have the necessary tables with the necessary fields you can access the relationship by:
$ideas = $community->ideas()->get();
//or
$ideas = $community->ideas;
So you controller would look something like:
public function showCommunities($id)
{
$community = Community::findOrFail($id);
//The below isn't necessary as you're passing the Model to a view
// but it's good for self documentation
$community->load('ideas');
return view('publicPages.ideas_in_community', compact('community'));
}
Alternatively, you could add the ideas to the array of data passed to the view to be a bit more verbose:
public function showCommunities($id)
{
$community = Community::findOrFail($id);
$ideas = $community->ideas
return view('publicPages.ideas_in_community', compact('community', 'ideas));
}
Hope this helps!
UPDATE
I would imagine the reason that you're receiving the App\idea not found is because the model names don't match. It's good practice (and in certain environments essential) to Capitalise you class names so make sure of the following:
Your class name is Idea and it's file is called Idea.php
The class has it's namespace declared i.e. namespace App;
If you've added a new class and it's not being found you might need to run composer dump-autoload from the command line to update the autoloader.
I have setup Kohana models with relationships define (1:* and : using through) but wanted to know the best way to use them in WHERE statements.
Example: I have a User model and a Post model. Post has a foreign key (userID) to User
I have setup a $_has_many relationship on the User model with an alias user_created
I know I can use the actual field userID, but I want to be able to do something like this
$user->where('user_created', 'IS', NULL);
This would also be handy for checking whether a many-to-many with something like this
'm2m_relation_count', '>', 0
Is this possible? Thanks!
You can do this with the ORM in Kohana in the following way.
$user->user_created->find_all();
This will result in all Posts the User created.
If you want to check if the User has created posts you just use the same function and do a count() on the result and check if it is greater than 0.
I don't think you can use the alias in a where clause itself. You have to call it from the ORM object.
Many-to-many relations work the same way.
1) There is no quick method to get all users without posts. Id prefer to add special column post_count and increment it after posting.
2) You can check user for m:m relationship with has()/has_any() methods:
if ($user->has('roles')) {
// user has at least one role
}
if ($user->has('roles', $roleId) {
// user has role with id=$roleId
}
if ($user->has('roles', array($roleId1, $roleId2))) {
// user has both $roleId and $roleId2 roles
}
if ($user->has_any('roles', array($roleId1, $roleId2))) {
// user has on of the $roleId1 or $roleId2
}