Laravel attempt to read property on null - php

I'm using simple Gates user permissions on my Laravel.
I have a permissions table with hasOne relationship:
class User extends Authenticatable
{
public function permissions()
{
return $this->hasOne(UserPermission::class);
}
}
and in an AuthServiceProvider I have to register permission:
public function boot()
{
$this->registerPolicies();
Gate::define('is_admin', fn(User $user) => $user->permissions->is_admin);
Gate::define('is_test_user', fn(User $user) => $user->permissions->is_test_user);
//
}
but when the record doesn't exist in my permission table then I have an error: Attempt to read property "is_admin" on null.
What's the best solution for this problem?

The optional helper is intended for this purpose.
optional($user->permissions)->is_admin

Related

Laravel 8: Call to undefined method Model::contains()

I'm using Laravel 8 for my project and in this project and I have created a custom Middleware called Admin that goes like this:
public function handle(Request $request, Closure $next)
{
if (Auth::user()->isAdmin()) {
return $next($request);
}
return redirect('/');
}
And I tried applying it like this:
Route::middleware('admin')->group(function () {
Route::get('test', function () { return "User is authenticated and is an admin."; });
});
And on Kernel.php at $middlewareGroups section:
'admin' => [
'auth',
\App\Http\Middleware\Admin::class
],
So I called the isAdmin() at User Model which simply checks if the role of the user is correct or not:
public function role()
{
return $this->belongsTo(Role::class);
}
public function isAdmin()
{
return $this->role->contains('slug', 'super-admin');
}
But now the problem is, when I go to /test uri, it does not redirect me to ('/') uri and shows me this error:
BadMethodCallException
Call to undefined method App\Models\Role::contains()
So what is going wrong here? How can I fix this issue?
Note that the relationship between roles and users table is One To Many. That's why I wrote role() instead of roles() because each user has only one role.
When you use a belongsTo relationship, laravel will return the model directly and not a collection of models.
When you call:
$this->role
What you get is either null (if the relationship does not exist) or a Role model. You can write your isAdmin function accordingly:
public function isAdmin()
{
return empty($this->role) ? false : $this->role->slug === 'super-admin';
}
You probably copied the code from an example with a Many to many relationship: in that case Laravel will return a collection of models and you can invoke collection methods on it, such as contains
could we see a snip of your Role Model.

Eloquent relationship in Laravel 6. Can't modify/overwrite relationship attribute

I built a one to one relationship between two Eloquent models and I want to replace an relationship attribute called profile_picture inside my User Eloquent model but I don't know how to do it.
This is a summary of my User model.
class User extends Authenticatable implements JWTSubject{
use Notifiable;
public function profile_picture(){
return $this->hasOne(UserProfilePicture::class);
}
}
And this is a summary of my UserProfilePicture model.
class UserProfilePicture extends Model{
public function user(){
return $this->belongsTo(User::class);
}
}
This is how I pretent to do it in my UserController file.
public function publicUserData($username){
$user = User::where("username", $username)->with("profile_picture")->first();
if($user){
if(!$user->profile_picture){
$user->profile_picture = UserProfilePicture::$defaultUserProfilePicture;
}
return response()->json($user);
}
return response()->json(false);
}
This is $defaultUserProfilePicture static array.
public static $defaultUserProfilePicture = [
"url" => "public/avatars/defaultUserPhoto.jpg",
"size" => 5229
];
I tried to use mutators but looks like it isn't work on relationships attributes.
I found this but doesn't work for me.
Thanks in advance.
You can provides the relation default model with attributes, you may pass an array or Closure to the withDefault method
public function profile_picture(){
return $this->hasOne(UserProfilePicture::class)->withDefault(UserProfilePicture::$defaultUserProfilePicture);
}
Reference: https://laravel.com/docs/7.x/eloquent-relationships#default-models
I solved it, first I put the static array UserProfilePicture::$defaultUserProfilePicture into my database then I do:
$user->setRelation("profile_picture", UserProfilePicture::find(1));
That take one more database request but is the one solution to I have for now.

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

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/

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

How to get reference table data in Laravel 5.1

I created a Model Account with accountgroup_id which refer from Account_group Model. Then I call it from route like this
Route::get('test', function () {
return \App\Account::get()->account_group;
});
Account Model has belogsto Relationship with Account_group
class Account extends Model
{
protected $fillable = ['accountgroup_id', 'accountno', 'accountname','address','contactno'];
public function account_group()
{
return $this->belongsTo('App\Account_group');
}
}
Account_group Model has hasMany relationship with Account
class Account_group extends Model
{
protected $fillable =['name','under'];
public function account()
{
return $this->hasMany('App\Account','accountgroup_id');
}
}
But after calling the route; I got following error.
Undefined property:
Illuminate\Database\Eloquent\Collection::$account_group
First, the second class should be named AccountGroup.
And reading the error carefully will give you a clue as to what's up - \App\Account::get() returns a collection of Account objects, each of which will have an AccountGroup. So you need to choose which specific Account you want, then you'll be able to access the account_group property on it:
\App\Account::find(1)->account_group; // get Account with ID 1
\App\Account::first()->account_group; // get the first Account record
My issue solved by calling Account with account group
\App\Account::with('account_group')->get();

Categories