laravel model method to check a condition like hasRole(['admin','superadmin')? - php

I'm trying laravel authorization and i need a method on User model that checks an condition and return boolean!
for example?
$user->hasRole(['superadmin']); // should return true/false
and in the model:
class User extends Model{
/*****
*****
*****/
public function roles()
{
return $this->hasMany('App\Role');
}
public function hasRole($roles)
{
// some validation and return boolean
}
}
How do i do this via laravel Models? Is any way?

public function hasRole($roles)
{
return !$this->roles->pluck('role_column')->intersect($roles)->isEmpty();
}
Don't forget to change 'role_column'

Since you have an array of strings, and I assume your roles have names, you could do this with a simple sql query.
public function hasRole($roles) {
return $this->roles->whereIn('role', $strings)->exists();
}

Related

Cannot access Intermediate table in HasManyThrough relationship

My DB schema looks like this.
Now, in artisan tinker mode, When I try to query Details table from user Model, it shows me the records of the details table but I cannot access the the Cases Model for some reason, it always returns NULL in tinker.
This is my User Model
public function details()
{
return $this->hasManyThrough('App\Models\Detail', 'App\Models\Cases', 'user_id', 'case_id', 'id', 'id');
}
What am I doing wrong?
If for convenience you want to access Details directly from the User model then you can define relations as - (may seem like a little duplication but worth if it results in ease)
class User extends Model
{
public function cases()
{
return $this->hasMany(Case::class);
}
public function details()
{
return $this->hasManyThrough(Detail::class, Case::class);
}
}
class Case extends Model
{
public function details()
{
return $this->hasMany(Detail::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class Detail extends Model
{
public function case()
{
return $this->belongsTo(Case::class);
}
}
Now both cases and details can be directly accessed via User record
$user->cases;
$user->details;
The idea of hasManyThrough is to skip the intermediate table. If you need to look at the cases and the details maybe you should define other relations for it.
// User model
public function cases()
{
return $this->hasMany(Cases::class, 'user_id');
}
// Cases model
public function details()
{
return $this->hasMany(Detail::class, 'user_id');
}
$users = User::with('cases.details')->get();
foreach ($users as $user) {
// an user
foreach ($user->cases as case) {
// a case
foreach ($case->details as $detail) {
// the details of a case
}
}
}

Eloquent all records relationship

I'm trying to build an alternative relationship that returns all records instead of only related records. I have tried returning a query builder, but that doesn't work, it must be a relationship. What should I return to make this work?
public function devices()
{
if ($this->admin) {
// return all devices relationship instead
} else {
return $this->belongsToMany('Device', 'permissions');
}
}
Fiddle: https://implode.io/XXLGG8
Edit: I'd like to continue building the query in most cases, not just get the devices.
The devices() function in your model is expected to return a relation, you shouldn't add the if statement there. Make your devices() function like this:
public function devices()
{
return $this->belongsToMany('Device', 'permissions');
}
In your User model add a new function:
public function getDevices() {
if($this->admin === true) {
return Device::all();
}
return $this->devices();
}
Now you can do:
$admin->getDevices(); // will return all devices
$user->getDevices(); // will return only relations
I actually went a slightly different way and used a scope:
protected function scopeHasAccess($query, User $user)
{
if ($user->admin) {
return $query;
}
return $query->join('permissions', 'permissions.device_id', "devices.id")
->where('permissions.user_id', $user->user_id);
}
Add devices accessor method to the User model and implement your logic there.
public function getDevicesAttribute() {
if ($this->admin) {
return Device::all();
}
return $this->getRelationValue('devices');
}
See updated "fiddle".

Laravel morphedByMany Query Returns NULL

I have this model
class Permission extends Model
{
public function details(): MorphToMany
{
return $this->morphedByMany('App\Models\Details', 'model', 'model_has_permissions', 'permission_id', 'model_id');
}
}
class Details extends Model
{
public function permission()
{
return $this->morphedByMany('App\Models\Permission','model','model_has_permissions','model_id','permission_id');
}
}
I'm execute this query
Details::with('permission')->find(55);
and got empty array
why happen this?and what is the correct query?
You have a typo in your permission() method
change this
return $this->morphedByMany('App\Models\Permission','model','.model_has_permissions','model_id','permission_id');
to this
return $this->morphedByMany('App\Models\Permission','model','model_has_permissions','model_id','permission_id');
I don't think it's possible to chain find after with. Here are your options.
Lazy Loading.
Details::find(55)->load('permissions');
Eager loading with where clause
Details::with('permissions')->where('id', 55)->get();
UPDATE
Shouldn't this be morphToMany?
public function details(): MorphToMany
{
return $this->morphedByMany('App\Models\Details', 'model', 'model_has_permissions', 'permission_id', 'model_id');
}
Or this?
public function permission()
{
return $this->morphedByMany('App\Models\Permission','model','model_has_permissions','model_id','permission_id');
}

Laravel - Eloquent return only one value

How can I return one value from a belongsToMany relation.
This is my relation:
public function role() {
return $this->belongsToMany('App\Role');
}
Now when I want to access the Role Name I have to do the folowing:
Auth::user()->role[0]->name
But I just want to do
Auth::user()->role
But now my question is: "How can I do that?"
to do this you need add custom attribute to user model as the following:
User Model:
protected $appends = ['role']; // add new attribute to user model
public function getRoleAttribute()
{
return $this->roles->first(); // don't use roles() it would execute every time
}
public function roles() {
return $this->belongsToMany('App\Role');
}
now you can use
Auth::user()->role
Auth::user()->role()->first()
Besides the question itself, i suggest you to use the plural name for belogsToMany relations: it is a common best practice and makes the code much more expressive.
public function roles() {
return $this->belongsToMany('App\Role');
}
Auth::user()->roles()->first()

Eager loading on polymorphic relations

class Admin {
public function user()
{
return $this->morphOne('App\Models\User', 'humanable');
}
public function master()
{
return $this->hasOne('App\Models\Master');
}
}
class Master {
public function admin()
{
return $this->hasOne('App\Models\Admin');
}
}
class User {
public function humanable()
{
return $this->morphTo();
}
public function images()
{
return $this->hasOne('\App\Models\Image');
}
}
class Image {
public function user()
{
return $this->belongsTo('\App\Models\User');
}
}
Now if I dump this:
return \App\Models\Admin::where('id',1)->with(array('user.images','master'))->first();
I get the perfect result one master, one user and one image record.
But if I do this
return $user = \App\Models\User::where('id',1)->with(array('humanable','humanable.master'))->first();
I only get one Admin record, the query get * from masters doesn't even run.
Any idea what I'm doing wrong, I'm sure this is possible.
If I remember correctly Laravel has lots of pitfall. You can try to use the protected $with var in Admin model instead of query builder with function.
class Admin {
protected $with = ['master'];
public function user() {
return $this->morphOne('App\Models\User', 'humanable');
}
public function master() {
return $this->hasOne('App\Models\Master');
}
}
In query builder, only need to include humanable. Now you should see master inside the humanable object.
return $user = \App\Models\User::where('id',1)->with('humanable')->first();
Hope this help.

Categories