Method Illuminate\Database\Eloquent\Collection::addEagerConstraints does not exist LARAVEL - php

I need help, don't see anything suspicious :c thanks for help !
Error Collection::addEagerConstraints does not exist occurs after the call:
public function show(Request $request, User $user)
{
$user->load('permissions');
dd($with);
return UserResource::make($user);
}
User Model:
class User extends Authenticatable
{
(...)
//////////////////////////////////////////////////////////////////////////////////////////
///
/// Relationships
///
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Relationship to permissions
*
* #return RolePermissions
*/
public function permissions()
{
return $this->role()->first()->permissions;
}
}

if you are using standard laravel user ,
you have to remove your 'permissions' relation and use the ready-made one:
$permissionNames = $user->getPermissionNames(); // collection of name strings
$permissions = $user->permissions; // get the user permissions
if you want a user with its permissions:
$user->load('permissions');
more details in:
https://docs.spatie.be/laravel-permission/v3/basic-usage/basic-usage/

Related

Laravel policy not applying to model automatically

According to the Laravel docs, a policy should be auto-discovered if it follows naming conventions: it should be placed in the Policies directory, its name should be the model name plus the word Policy and the models should be in the app directory. This is all true in my case, but the policy isn't working.
The model name is Screen. The policy is named ScreenPolicy:
class ScreenPolicy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* #return void
*/
public function __construct()
{
//
}
public function delete(User $user, Screen $screen)
{
return false; //always return false for testing
}
}
And in my controller, I have the following method that deletes a Screen:
public function delete(Request $request) {
$screen = Screen::find($request->screen_id);
$screen->delete();
...
}
My expectation is that I shouldn't be able to delete the Screen here since the policy always returns false, however the Screen is successfully deleted by calling this method. What am I doing wrong?
You still need to call the authorize(). Check docs
$screen = Screen::find($id);
if ($this->authorize('delete', $screen)) {
$screen->delete();
}

How to authenticate specific route to user with specific role in laravel Milldeware

I have multiple users with multiple permissions. A user can belong to the only single role but that role can have multiple permissions like create, read, update, delete. And I have a RoleMiddleware. I am authenticating the user in roleMiddleware. But how can I protect routes in RoleMiddleware against a specific user?
For Example, I have a route create-case which can only be accessed by the operator or by Admin else everyone redirects to 404 error how Can I deal with it in RoleMiddleware.
I have written basic code for authentication where every user with their roles is authenticated but I am getting how can I code in middleware so ever route when a user hits it may go to the RoleMiddleware where middleware Authenticate route to the Role and then give him the access.
Role Middleware
class RoleMiddleware
{
public function handle($request, Closure $next, $permission = null)
{
if (Auth::check() === false)
{
return redirect('login');
}
elseif (Auth::check() === true)
{
$roles = Role::all()->pluck('slug');
if (is_null($request->user()) )
{
abort(404);
}
if (!$request->user()->hasRole($roles))
{
abort(404);
}
if ($request->user())
{
if ($request->user()->hasRole($roles))
{
return $next($request);
}
}
}
}
}
Case Controller:
<?php
namespace App\Http\Controllers\Cases;
use App\Http\Controllers\Controller;
use App\Http\Requests\CaseStoreRequest;
use Illuminate\Support\Facades\Auth;
use Session;
class CaseController extends Controller
{
use DropzoneFileUploadTraits;
public function __construct()
{
$this->middleware('role');
}
public function index()
{
$data['portal'] = Portal::all();
$data['operators'] = Operator::all();
return view('case', $data);
}
public function caseList()
{
$user = new User();
$isAdmin = $user->isAdmin();
$loggedIn = Auth::id();
$cases = Cases::with('patients', 'portal')
->when(!$isAdmin, function ($query) use ($loggedIn) {
return $query->where('user_id', $loggedIn);
})->orderBy('created_at', 'desc')->get();
$data['cases'] = $cases;
return view('case_list', $data);
}
}
Route:
Route::get('create-case', 'Cases\CaseController#index')->name('create-case');
Route::post('case-submit', 'Cases\CaseController#caseSubmit')->name('case-submit');
Route::post('edit-patient-case-submit', 'Cases\CaseController#editPatientCaseSubmit')->name('edit-patient-case-submit');
Route::get('case-list', 'Cases\CaseController#caseList')->name('case-list');
Best way to do that in a clean manner would be to create policies on the targeted entities.
Laravel policies allow you to :
Bind a route authorization logic to a policy action
Easily call a policy action result from anywhere else in the project (views, controllers and so on).
The subject is well covered in Laravel documentation so I suggest you go there and take a look. Do not forget to register the policy and bind it to your model.
Apart from that this should do the trick.
class CasePolicy
{
use HandlesAuthorization;
public function create(User $user){
$roles = ['operator','Admin']
return $user->hasRole($roles);
}
}
Then in your route file :
Route::get('create-case', 'Cases\CaseController#index')->name('create-case')->middleware('can:create,App\Case');
I haved just learned and implement Gate and Policy hope this is correct Because its working for me. Great concept thanks.
Route::get('create-case', 'Cases\CaseController#index')->name('create-case')->middleware('can:create-case,App\Model\Case');
Gate:
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
User::class => CreateCase::class
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('create-case','App\Policies\CreateCase#create_case');
}
}
Policy
class CreateCase
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* #return void
*/
public function __construct()
{
//
}
public function create_case(User $user){
if($user->hasRole(['admin']) ||$user->hasRole(['operator']) && $user->hasPermissionTo('create')){
return true;
}else
return false;
}
}

Laravel 5.5: Authorization with pivot table

I have four table users, groups, posts, and group_user. Users can follow different groups. The group_user table is for many-to-many relationship.
Every post belongs to a group and a user. I want that a user can only post a post on a group if he follows that group. I can easily check using if statement that whether a user follows that group or not. But how can I authorize the user for posting using policies.
Create a policy:php artisan make:policy.
Register policy:
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
Post::class => PostPolicy::class,
];
In your policy the logic, for instance:
public function create(User $user, Post $post)
{
$groupsOfUser = $user->groups()->get();
foreach ($groupsOfUser as $group) {
if($group->id == request('groupId'))return true;
}
}
And in your Controller:
public function store(Post $post, $groupId)
{
$this->authorize('create', $post);
Post::create([
'user_id' =>auth()->id(),
'group_id' => $groupId,
'title' => 'sometitle',
]);
}
And i have tested it with route:
Route::get('/post/{groupId}', 'PostController#store');
But you may be getting groupId via input, but you get the idea.
There doesn't seem to be any good answers for this so I'll share what I did to help out anyone looking for this in the future.
This was tested in laravel 5.8 but I think it will work at least a few versions back.
First create a pivot model
use Illuminate\Database\Eloquent\Relations\Pivot;
class GroupUser extends Pivot {}
Update your groups relationship in the User model
public function groups() {
return $this->belongsToMany(Group::class)
->using(GroupUser::class);
}
And update your users relationship in the Group model
public function users() {
return $this->belongsToMany(User::class)
->using(GroupUser::class);
}
Create your GroupUserPolicy class
use Illuminate\Auth\Access\HandlesAuthorization;
class GroupUserPolicy {
use HandlesAuthorization;
public function update(User $user, GroupUser $pivot) {
// check permissions
}
// additional authorization methods
}
And link them up in the AuthServiceProvider if not using auto discover
protected $policies = [
GroupUser::class => GroupUserPolicy::class,
// ...
];
Then when you want to show or update a pivot, in a controller for example, then you can just pass the pivot to the authorize check
$group = $user->groups()->where('group_id', $groupId)->firstOrFail();
$this->authorize('update', $group->pivot);
// or ...
foreach ($user->groups as $group) {
$this->authorize('update', $group->pivot);
}
There is a complex solution for Access Control List. Read about Zizaco Entrust Library. Here you can set permissions for each route in your system. By that routing and permission you can prepare few groups.
Topic is hard but realy worth to implement:
https://github.com/Zizaco/entrust

Retrieve and order data from two columns

I'm starting to get confused. I have an user model and an event models. A user can create an event, he is the owner. But he can also get invited to an event, he is a guest. The first relation is a OneToMany relation and the other one is a ManyToMany relation.
Now, I would like to retrive all the events of an user, owned or guest. Every events and order them by the field date.
This my user model :
class User extends Authenticatable
{
/**
* Get the events of the user.
*/
public function owned_events()
{
return $this->hasMany('App\Event', 'owner_id');
}
/**
* Get the events list.
*/
public function events()
{
return $this->belongsToMany('App\Event')
->using('App\Invitation')
->withPivot('status')
->withTimestamps();
}
}
This is the event model :
class Event extends Model
{
/**
* Get the owner of the event.
*/
public function owner()
{
return $this->belongsTo('App\User');
}
/**
* Get the guests list.
*/
public function guests()
{
return $this->belongsToMany('App\User')
->using('App\Invitation')
->withPivot('status')
->withTimestamps();
}
}
This is what I tried (inside an EventRepository)
/**
* Return all the events (owner and guest) of a user ($id).
*/
public function all($id)
{
$guested = $this->event->guests()->where('user_id', $id)->get()->load('owner', 'guests');
$own = $this->event->where('owner_id', $id)->get()->load('owner', 'guests');
return $guested->merge($own)->sortBy('date');
}
It works... but it gives me only the owned events, not where my user is the guest... I think that there is a much better way to do it. Two calls for one request is pretty bad and two loads is even worse no?
I think I found the solution :
$user = Auth::user()->load('owned_events', 'events'));
$events = $user->events->merge($user->owned_events)->sortBy('date');
return view('events.index', compact('events'));
Go throught the event model was way too hard while go throught the user model is much easier :)

How to redirect the users based on there roles laravel 5.1

Hello their i am using laravel 5.1 and i have problem with redirecting the path of user. i have a roles table, a users table and role_user table means pivot table. Every thing is fine nice but i failed to redirect the page of different user. . This is my User Model. I have not created any middleware
public function roles()
{
return $this->belongsToMany('my\role');
}
public function hasRole($role)
{
if (is_string($role)){
return $this->roles->contains('name', $role);
}
return !! $role->intersect($this->roles)->count();
}
This is my AuthController:
public function redirectPath()
{
// Logic that determines where to send the user
if (\Auth::user()->hasRole('GlobalAdmin', 'SuperAdmin', 'CompanyAdmin', 'GroupAdmin'))
{
return '/adminpanel';
}
return '/dashboard';
}
When I login as role globleadmin It works fine and redirect to adminpanel but when i login as another assigned role it redirect to dashboard. I didn't get it perhaps I passed arguments in AuthController as:
if (\Auth::user()->hasRole('GlobalAdmin', 'SuperAdmin', 'CompanyAdmin', 'GroupAdmin'))
is incorrect. Please help me.
This is my route:
Route::get('/adminpanel', 'Admin\adminscontroller#index');
And This is my adminscontroller:
class adminscontroller extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('Admin.AdminPanel');
}
}
Thanks in advance.

Categories