I have multiplied users on joined request - laravel

In laravel 6 app I have multiplied users getting users with related spatie/persmissions,
as I need filter on selected permission and to show names of user's permissions :
I make as :
$users = User
::getByName($this->filter_name)
->getByStatus($this->filter_status)
->leftJoin('model_has_permissions', 'model_has_permissions.model_id', '=', 'users.id')
->getByUserPermission($this->filter_user_permission, 'model_has_permissions')
->getByUserPermissionModelType( 'model_has_permissions')
->orderBy($this->order_by, $this->order_direction)
->offset($limit_start)
->take($backend_items_per_page)
->distinct()
->paginate($backend_items_per_page);
$users->getCollection()->transform(function ($user) {
$permissions_text= '';
$permissions = $user->permissions;
foreach( $permissions as $nextPermission ) {
$permissions_text.= $nextPermission->name . ', ';
}
$user->permission_text = MyFuncsClass::trimRightSubString( $permissions_text, ', ' );
return $user;
});
I expected that using of
->distinct()
would salve it m but I failed.
Why?
UPDATED BLOCK :
Code with whereHas :
->whereHas('permissions', function ($query): void {
$query->where('id', $this->filter_user_permission);
})
works ok if $this->filter_user_permission is not empty. In case it is empty. It does not work.
That is backend listing form, so $this->filter_user_permission) can be filled or empty.
To salve it I used next scope:
public function scopeGetByUserPermission($query, $permission_id= null, $table_name)
{
if (!empty($permission_id)) {
if ( is_array($permission_id) ) {
$query->whereIn($table_name.'.permission_id', $permission_id);
} else {
$query->where($table_name.'.permission_id', $permission_id);
}
}
return $query;
}
It was used in my original post.
I tried to use it as :
->whereHas('permissions', function ($query): void {
$query->getByUserPermission($this->filter_user_permission, 'model_has_permissions');
})
but got error :
Call to undefined method
Illuminate\Database\Eloquent\Builder::getByUserPermission()
If there is a valid way ?
Thanks!

If you're using "spatie/laravel-permission" package, You may do it like so:
$users = User
::getByName($this->filter_name)
->getByStatus($this->filter_status)
/** ->leftJoin('model_has_permissions', 'model_has_permissions.model_id', '=', 'users.id') */
/** I don't really know what this 2 methods below does
->getByUserPermission($this->filter_user_permission, 'model_has_permissions')
->getByUserPermissionModelType( 'model_has_permissions') */
->whereHas('permissions', function ($query): void {
/** Change "permission_field" here to column which you use (example: id,name) from the "permissions" table of your database */
$query->where('permission_field', $this->filter_user_permission);
})
->orderBy($this->order_by, $this->order_direction)
->offset($limit_start)
->take($backend_items_per_page)
->distinct()
->paginate($backend_items_per_page);
I hope this help for You

Your scope method "scopeGetByUserPermission" defined in your User model, but in the "whereHas" method $query variable related to Permission model.
If You want to use some scope in your Model, try this.
public function scopeGetByUserPermission($query, $permission_id=null/**, $table_name */)
{
if (!empty($permission_id)) {
$query->whereHas('permissions', function ($query) use ($permission_id): void {
/** Cast your $permission_id variable to array and use "whereIn" method */
$query->whereIn('permission_id', (array)$permission_id);
});
}
return $query;
}
Also you can use php7 features for arguments in your scope method
public function scopeGetByUserPermission($query, array $permission_id=[])
{
/** Your scope logic from the code above */
}
And calling this scope will be looking like so
$users = User
::getByName($this->filter_name)
->getByStatus($this->filter_status)
->getByUserPermission((array)$this->filter_user_permission)
->orderBy($this->order_by, $this->order_direction)
->offset($limit_start)
->take($backend_items_per_page)
->distinct()
->paginate($backend_items_per_page);

Related

Laravel: pass an argument to the relationship function (not the query callback function) when using whereHas

I would like to know how to pass an argument to a model relationship function. Just to be clear, I'm NOT talking about the query callback.
Consider a model like so:
class UserRelationships extends Model
{
// other stuff
// dynamic scope:
/**
* Scope a query to only include users of a given type.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param mixed $type
* #return \Illuminate\Database\Eloquent\Builder
*/
// $relationships = UserRelationships::at( Carbon::parse('2022-10-10') )->get();
public function scopeAt($query, Carbon $date)
{
return $query->where('superseded_at', '>', $date )
->where('created_at', '<=', $date );
}
}
And a related model featuring the following relationships:
class User extends Authenticatable
{
public function progenial_relation(Carbon $date=null) // returns this user record in the userRelationships table, which can be used to retrieve this users parent (directly lookup the sponsor_id)
// when eager loading, this is useful for getting all users whose parent is x, hence the name
{
return $this->hasOne(UserRelationships::class, 'user_id', 'id')
->at( #$date ?: Carbon::now() ) // local dynamic scope in userRelationships
->orderByDesc('created_at')
->limit(1);
}
public function parental_relation(Carbon $date=null) // returns records from the userRelationships table, of all the users which refer to this user as their sponsor
// when eager loading, this is useful for getting the user whose child is x, hence the name
{
return $this->hasMany(UserRelationships::class, 'sponsor_id', 'id')
->at( #$date ?: Carbon::now() ); // local dynamic scope in userRelationships
}
}
As you can see my relationships accept an argument (the date).
Now, if you wanted to use those relationships straightforwardly like so, there's no issues:
$date = Carbon\Carbon::parse('2022-06-01');
$relations_at_date = User::find(1)->parental_relation( $date )->get();
But what happens if you need to use eager-loading methods such as has(), whereHas(), doesntHave(), whereDoesntHave()?
How do you pass an argument to the relationship? For example, I wanted to add other relationships to my User model.
public function children(Carbon $date=null)
{
$date = #$date ?: Carbon::now();
return self::whereHas('progenial_relation', function($q) {
$q->where('sponsor_id', $this->id);
}, $date); // not working
}
I tried with these syntax, but it doesn't work:
whereHas( 'relationship_name', $callback, $argument )
whereHas( 'relationship_name', $argument, $callback )
whereHas( 'relationship_name', [$argument], $callback )
whereHas( 'relationship_name', $callback, [$argument] )
Is it somehow possible?
Are there any alternatives?
For completeness I'm going to add what happens if I use a normal closure:
public function children(Carbon $date=null)
{
$date = #$date ?: Carbon::now();
return self::whereHas('progenial_relation', function($q) use ($date) {
$q->at($date)->where('sponsor_id', $this->id);
});
}
This is the resulting SQL. As you can see the constraints are applied twice. Once by the query callback and once by the relationship. But since I cannot pass the correct argument to the relationship, it gets the default one. The 2 constraints collide and the query does not work.
"select * from `users`
where exists (
select *
from `user_relationships`
where `users`.`id` = `user_relationships`.`user_id`
and `user_relationships`.`superseded_at` > ?
and `user_relationships`.`created_at` <= ?
and `sponsor_id` = ?
and `user_relationships`.`superseded_at` > ?
and `user_relationships`.`created_at` <= ?
)
and `users`.`deleted_at` is null"
I don't think that its possible to pass variables to relationship methods when eager-loading like this.
But you can apply a sub-query to the wherehas:
$date = #$date ?: Carbon::now();
return self::whereHas('progenial_relation', function($q) use ($date) {
$q
->where('sponsor_id', $this->id)
->at( #$date ?: Carbon::now() );
}, $date);
Although I'm not sure what the ->at method/scope you added does.

Laravel - Return only multiple relationships with records from controller or model

I'm returing a model with all the related models.
My problem is that perhaps some models don't have records so they are returned in my $property variable as empty and I have to evaluate their existence in blade (not done yet)
Is there any way in controller or parent model to return only the child relationships which have records? Or any blade directive to evaluate these cases?
All relationships are 1:M.
Regards!
Controller Code
class PropertyController extends Controller
{
public function details($id)
{
$property = Property::with('attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services')
->where('prop_id', $id)
->first();
// dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}
}
Sir u have the same result with this code?? (just trying to help, im new in this world)
return view('pages.processes.offerdemand.matchs.propertymodal', compact(array('property')));
class PropertyController extends Controller
{
public function details($id)
{
$relations = ['attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services']
$property = Property::with($relations)->whereHas($relations)
->where('prop_id', $id)
->first();
// dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}
}
SOLUTION
I changed code as follows and recieved the expected result:
public function details($id)
{
$property = Property::with(['attributes' => function ($builder) {
$builder->where('pa_value', '!=', '');
}])
->with('addons', 'distributions', 'images', 'attributes_2', 'services')
->where('prop_id', $id)
->withCount('attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services')
->first();
dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}

Orwhere has method does not allow null

enter image description hereI am trying to implement a many to many relationship search with 2 models.
i get input from multiple checkbox values and want to search for items that match A or B when there is an input of data.
I read this url and wrote the same logic.
https://laracasts.com/discuss/channels/laravel/many-to-many-relationship-with-2-pivot-table-data-search
public function search(Request $request)
{
$languages = $request->lang;
$fields = $request->field;
$agencies = Agency::with('languages')->with('specialized_fields')
->orWhereHas('languages', function($query) use ($languages) {
$query->whereIn('language_id', $languages);
})
->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
dd($agencies);
}
i expected to achieve A or B search but instead I got this error.
Argument 1 passed to Illuminate\Database\Query\Builder::cleanBindings() must be of the type array, null given, called in /var/www/jtf/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php on line 907
it seems that it causes this error if either A or B is null, but why??? Does the OrWhereHas method work only when theres an input??
/added info/
my error message
my agency model
class Agency extends Model {
protected $guarded = [
'id'
];
public function languages(){
return $this->belongsToMany('App\Language');
}
public function specialized_fields(){
return $this->belongsToMany('App\SpecializedField');
}
public function region(){
return $this->hasOne('App\Region');
} }
I believe it's because either $languages or $fields is null.
Since ->whereIn() is expecting an array, but you're passing null.
You just need to make sure you're passing an array.
$languages = array_filter((array) $request->lang); // cast to array & remove null value
$fields = array_filter((array) $request->field);
$agencies = Agency::with('languages', 'specialized_fields')
->orWhereHas('languages', function($query) use ($languages) {
$query->whereIn('language_id', $languages);
})
->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
I'm speculating that you started your where query chain with an orWhereHas() which may have caused the problem, try starting with whereHas() instead.
public function search(Request $request){
$languages = $request->lang;
$fields = $request->field;
$agencies = Agency::with('languages', 'specialized_fields') // you can get away by just using one with(), not needed but its cleaner this way
->whereHas('languages', function($query) use ($languages) { // previously orwherehas
$query->whereIn('language_id', $languages);
}) ->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
dd($agencies);
}

Laravel 5 - Use 'isAdmin' role check method when querying a model

I have implemented a basic role system that uses a table 'role_user'.
On my user model I have a few methods that check the roles, one of them is:
public function isStaff()
{
foreach ($this->roles()->get() as $role)
{
if ($role->id == 3)
{
return true;
}
}
return false;
}
How can I use this method when I am querying users?
This query here:
return User::where('name', 'like', "%".$request->name."%")
->orWhere('email', 'like', "%".$request->name."%")
->whereDoesntHave('Teams', function ($query) use($teamId) {
$query->whereId($teamId);
})
->with('teams')
->get();
Currently returns all users, but I only wish to return users that have a role of 3 (isStaff)
You can using Scopes With Laravel instead of multiple methods to check for different methods.
public function scopeRole($query, $flag)
{
return $query->where('role', $flag);
}
and then
$users= User::role(3)->get();
check the reference tutorial for Creating Dynamic Scopes
it's better to do condition
return User::where('name', 'like', "%".$request->name."%")
->orWhere('email', 'like', "%".$request->name."%")
->whereDoesntHave('Teams', function ($query) use($teamId) {
$query->whereId($teamId);
})
->whereHas('roles', function($q) use ($role_id){
$q->where('id',$role_id);
})
->with('teams')
->get();
or also you can create a method for above query and based on param reurn result
You can have a scope called staff in your User model, then use that to narrow down your result:
public function scopeStaff($query, $roll_id = 3)
{
return $query->where('role_id', '=', $roll_id)
}
So when checking (with the model) for staff roles, you can improve the function that does that:
public function isStaff($role_id = 3)
{
return $this->role_id = $role_id ? $this : false;
}
Therefore, when using the query builder you can use the first method to narrow the result to those with the specified id, as you can see the default is 3 but will change to any value you give:
$staff_users = User::staff()->get();
Then the other one for verifying if a matched user model is a staff:
$user = User::find(1);
$is_staff = $user->isStaff(); //false or returns the same model
Hope this helps

Laravel Error: Trying to get property of non-object

I am getting an error
Trying to get property of non-object (View: C:\xampp\htdocs\laravel\proj\resources\views\mycases.blade.php)
I have defined a relationship between two models Ccase and Hiring.
public function hirings()
{
return $this -> hasMany('App\Hiring', 'case_ID')->orderBy('id','desc');
}
and paginating the results using a method below
public function getHiringsPaginateAttribute($perPage)
{
return $this->hirings()->paginate($perPage);
}
The other model 'Hiring' has a method to define relationship with Ccase as follows:
public function ccase()
{
return $this->belongsTo('App\Ccase', 'id');
}
In my controller, I have following code:
if(isset($search_term))
{
$search_term = preg_replace('/\s+/', ' ', $search_term);
$search_term = trim($search_term);
if (strlen($search_term) > 0 && strlen(trim($search_term)) == 0)
$search_term = NULL;
$search_terms = explode(' ',$search_term);
$fields = array('id', 'title', 'case');
$hirings = $hirings->whereHas('ccase', function($q) use ($search_terms, $fields){
foreach ($search_terms as $term)
{
foreach ($fields as $field)
{
$q->orWhere($field, 'LIKE', '%'. $term .'%');
}
}
});
}
$hirings = $hirings->getHiringsPaginateAttribute($results_per_page);
In mycases.blade.php, my code is
{{$hiring->ccase->id}}
This line is throwing the above said error while the output of {{$hiring->ccase}} is:
{"id":1,"case":"HI this is a sample case i am putting just for test.","created_at":"2015-02-22 11:54:09","updated_at":"2015-02-22 11:54:09"}
What might be wrong with the code?
Unfortunately, you can't use related models in views. Here's the detailed explanation why.
Your case can be solved by specifying the name of the associated column on the parent table:
return $this->belongsTo('App\Ccase', 'ccaseId', 'id');
In model Hiring, it will be like
public function ccase()
{
return $this->belongsTo('App\Ccase', 'case_ID', 'id');
}
And now in view use it like this:
{{ $hiring->ccase->id }}
Not sure, but i think that you could use relations in view, you should use an eager loading in controller where you are quering for $hirings, just add a:
with(['hirings.ccase'])
Could you please provide a peace of code from controller where you make a query to Hiring model for clear?

Resources