Laravel how to get users by roles dynamically from api endpoint - php

How can I get the users based on which role they have in laravel? I have roles like for example "admin", "author", "editor" and I want to have a dynamic api-endpoint.
so in my api.php I have:
Route::get('users/{role}', "Api\UserController#role");
and my Controller looks like this:
public function show()
{
$user_role = User::whereHas(
'roles',
function ($q) {
$q->where('name', 'admin');
}
)->get();
return $user_role;
}
This works fine so far, but I want the endpoint to be dynamic, like if want all my editor users the endpoint should be api/users/editors etc. etc.
How can I achieve this?

public function show($role) //example: $role = 'admin'
{
return User::whereHas('roles', function ($q) use ($role) {
$q->where('name', $role);
})->get();
}

Your controller function should look like this:
public function show(Role $role)
{
$users = $role->users;
return $users;
}
And your Role Eloquent Model should have these methods:
public function users()
{
return $this->belongsToMany(User::class, 'user_role')
}
public function getRouteKeyName()
{
return 'name';
}

Related

Filtering data with spatie query builder using trait

I put logic from my function index() of UserController in trait taht i created:
public function index()
{
$this->authorize('view', Auth::user());
$users = QueryBuilder::for(User::class)
->allowedIncludes('kids','roles','articles','recordings')
->allowedFilters('first_name', 'last_name', 'email')
->get();
return UserResource::collection($users);
}
and this is my trait :
<?php
namespace App\Http\Traits;
use App\Models\User;
use Spatie\QueryBuilder\QueryBuilder;
trait Filterable
{
public function filter()
{
$users = QueryBuilder::for(User::class)
->allowedIncludes('kids','roles','articles','recordings')
->allowedFilters('first_name', 'last_name', 'email')
->get();
return $users;
}
}
So now my function index() looks like this:
use Filterable;
public function index()
{
$this->authorize('view', Auth::user());
$users = $this->filter();
return UserResource::collection($users);
Now when i do this in my postman
{{url}}/api/users?filter[first_name]=anna
it works and it returns anna from my database but when I try
{{url}}/api/users?include=roles
it return every user from database but does not include roles.
Can somebody help me with this?
This is taken straight from the github page: https://github.com/spatie/laravel-query-builder#custom-filters
Custom filters
use Spatie\QueryBuilder\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;
class FiltersUserPermission implements Filter
{
public function __invoke(Builder $query, $value, string $property) : Builder
{
return $query->whereHas('permissions', function (Builder $query) use ($value) {
$query->where('name', $value);
});
}
}
use Spatie\QueryBuilder\Filter;
// GET /users?filter[permission]=createPosts
$users = QueryBuilder::for(User::class)
->allowedFilters(Filter::custom('permission', FiltersUserPermission::class))
->get();
// $users will contain all users that have the `createPosts` permission

Laravel function in User model that will be called for User::all()

Imagine I have this function in User model:
public function loadOrderStatus(){
$this->load(['status' => function ($query) {
$query->ofTranslatedStatuses();
}]);
}
This function works fine for single model instance like : User::first()
but how this function can be used with User::all() ?
You can use a scopeQuery in the User model
public function scopeStatusAll($query)
{
return $query->with(['status' => function ($query) {
$query->ofTranslatedStatuses();
}]);
}
call it with
User::statusAll()->get();
Or you could put a static function in User model
public static function statusAll()
{
$users = User::all();
return $users->load(['status' => function ($query) {
$query->ofTranslatedStatuses();
}]);
}
call it with
User::statusAll();

Laravel Eloquent deep nested query

I'm still learning Laravel and I can't find the solution for this problem.
I need to get invoices(with expenses) that are related to specific Partner Type.
I tried this:
$p = Project::with(['invoices.partner.partnerType' => function($query){
$query->where('partnerTypeName', 'Lieferant');
}, 'expenses'
])->where('id', $id)
->first();
I want to select invoices for Lieferant, but I get all invoices for one project.
Project Model:
public function invoices()
{
return $this->hasMany('App\Invoice');
}
Invoice Model
public function expenses()
{
return $this->hasMany('App\Expense');
}
public function partner()
{
return $this->belongsTo('App\Partner');
}
Partner Model
public function partnerType()
{
return $this->belongsTo('App\PartnerType');
}
Edit: PartnerType Model
public function partners()
{
return $this->hasMany('App\Partner');
}
Edit 2: Database
Partner(partnerID, name, partnerTypeId)
PartnerType(partnerTypeId, partnerTypeName)
Project(projectID, name)
Invoice(invoiceID, name, projectID, partnerID)
Expenses(expenseID, invoiceID)
If your models look like that.
Should be like :
$p = Project::with(['invoices' => function($query){
$query->where('partnerTypeName', 'Lieferant')
->with(['expenses','partner' => function($q){
$q->with('partnerType');
}]);
}])->where('id', $id)
->first();
return dd($p);
The solution to your problem is to update your query like this:
$p = Project::with(['invoices' => function($query){
$query->with('expenses')->whereHas('partner.partnerType', function($q){
$q->where('partnerTypeName', 'Lieferant');
});
}])
->where('id', $id)
->first();
But a cleaner solution would be using a scope for your problem.
In your Invoice model.
// Invoice.php
public function scopeByPartnerType($query, $partnerType)
{
$query->whereHas('partner.partnerType', function($q) use ($partnerType) {
$q->where('partnerTypeName', $partnerType);
});
}
And then in your Project model, add another relation that will just get Invoices with a particular partner type.
// Project.php
public function lieferantInvoices()
{
return $this->hasMany('App\Invoices')->byPartnerType('Lieferant');
}
Now you can do just this:
$project->find($id)->load('lieferantInvoices');

Getting all related fields to related field in Eloquent

I have this DB structure:
classroom_user is Elqouent many-to-many pivot.
User.php:
public function classrooms() {
return $this->belongsToMany(ClassRoom::class, 'classroom_user', 'classroom_id', 'user_id');
}
ClassRoom.php:
public function users()
{
return $this->belongsToMany(User::class, 'classroom_user', 'user_id', 'classroom_id');
}
I have a $user and want to write Eloquent method which gets all users who are in at least one of classrooms the $useralso is in. Something like $user->classrooms->users. But this falls with error Property [users] does not exist on this collection instance.. How can I do this?
I think you can do it like this:
$users = User::whereHas('classrooms', function($query) {
$query->whereHas('users', function($query) {
$query->where('id', auth()->user()->id);
});
})->get();
You could store this as a scope in your User model:
public function scopeClassmatesOf($query, $user)
{
$query->whereHas('classrooms', function($query) use($user) {
$query->whereHas('users', function($query) use($user) {
$query->where('id', $user->id);
});
})
}
And then call it from a controller like:
$user = auth()->user();
$users = User::classmatesOf($user)->get();
Your asking for users of a collection. $user->classrooms is a collection of classrooms.
If you do $user->classrooms->first()->users you will get all the users from the first classroom.
To get all users from all classrooms, you can do something like;
$users = $user->classrooms->map(function ($classroom) {
return $classroom->users;
});
Note that will return duplicate users and authenticated user him self witin the collection. So, you might need to do some filtering to the result.
This worked for me:
public function classmates() {
$self = $this;
$a = User::with('classrooms')->whereHas('classrooms', function ($query) use ($self) {
$query->whereHas('users', function ($query) use ($self) {
$query->where('users.id', $self->id);
});
});
return $a;
}

Laravel 4.1 eager loading

I'm having trouble on the eager loading.
Let's say I have models of Members, TrainingCategory, TrainingCategoryResult and Registration
Member Model:
public function registration() {
return $this->hasMany('Registration', 'member_id');
}
public function trainingResults(){
return $this->hasMany('trainingResult', 'member_id');
}
public function trainingCategoryResults() {
return $this->hasMany('TrainingCategoryResult', 'member_id');
}
TrainingCategory Model:
public function trainings() {
return $this->hasMany('Training', 'id');
}
public function trainingCategoryResults() {
return $this->hasMany('trainingCategoryResult', 'category_id');
}
TraningCategoryResult Model:
public function category() {
return $this->belongsTo('TrainingCategory', 'id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
Registration Model:
public function course() {
return $this->belongsTo('Course', 'course_id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
I am trying to eager load all the registration info and its related info including the TraningCategoryResult info but I not sure how to get that TraningCategoryResult which required two foreign keys (category_id and member_id), is there any way to do that?
Here is my code atm:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
});
$members= $members
->with('registration.course',
'registration.course.traningCategories',
->get(['member.id']);
Thank you.
This will not work Member::with('categoryResult')->with('registration')->get()
You can make a new relation in Member Model
public function categoryResult()
{
return $this->belongsTo('Category')->with('Registration');
}
//and then call
Member::with('categoryResult')->get();
You could use a few options to achieve that:
OPTION 1: create a variable relationship
Change your relation in the Member model
public function trainingCategoryResults($category_id = null) {
if(empty($category_id))
return $this->hasMany('TrainingCategoryResult', 'member_id');
else
return $this->hasMany('TrainingCategoryResult', 'member_id')
->where('category_id', $category_id);
}
The code above might have limitations and it doesn't take advantage of many laravel features, but it will work.
OPTION 2: Access from relationship
You can keep everything as is, and load as follow:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
})
->where('id', $member_id) // set the id of the memeber
->with(array(
'traningCategoryResults' => function($q)use($category_id){
$q->where('category_id', $category_id); // This makes sure you get only desired results
}
))
In this way you will have only what you need, assuming you know the $category_id

Categories