Laravel Role HasMany relationship - laravel

I have a User Model and a Role Model which are in many-to-many relation.
I have two roles: admin and manager.
I also have an Order Model. Managers need to have many orders. Where do I state such relations? Do I have to state it in the User Class?
Do I need to create separate models for Admins and Managers?

Managers & admins are a subset of your users, defined by their roles.
Therefore, we'll use scope to filter users who are managers by their roles.
App\Scopes\ManagerUserScope.php
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class ManagerUserScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
//Assuming Your user has a relationship (has many) role
$builder->whereHas('role', function($roleQuery) {
//Assuming that in the role table, the manager role entry has ID 1
$roleQuery->where('id','=',1);
});
}
}
Then, we extend the User model to create a manager model which has the above scope automatically applied.
App\Models\Manager.php
namespace App\Models;
use App\Scopes\ManagerUserScope;
class Manager extends User {
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new ManagerUserScope);
}
/**
* Relationship: Orders (Has many)
*/
public function orders()
{
return $this->hasMany('orders');
}
}

Your many-to-many relationship between User and Role can be perfectly described with belongsToMany Eloquent relation methods both ways. Also since each Order must have responsible manager for it we also have one-to-many relation between Manager and Order which will be described with hasMany/belongsTo methods.
So, your User model will have:
public function roles()
{
return $this->belongsToMany('App\Role');
}
public function orders()
{
return $this->hasMany('App\Order');
}
For your Role model:
public function users()
{
return $this->belongsToMany('App\User');
}
And lastly your Order model:
public function manager()
{
return $this->belongsTo('App\User');
}
There is no need to create a certain limitations (like "only users with role manager can have orders") on DB schema level, it's easier to implement in the code. So, for example, you might want to implement a method that will assign order to user and check his roles first.

Related

laravel eloquent relationship for indirectly related model

I want a relationship where two unrelated models are linked together with a linker model.
My tables are like:
card table
id(pk)
name
User table
id(pk)
username
password
card_id(fk)
Feature table
id(pk)
name
card_id(fk)
How can i define an eloquent relationship to access all the features of user's card from user model like this:
class User extends Model
{
use HasFactory;
public function features(){
return $this->relatonship_statement;
}
}
and when I tried in Card Model:
class Card extends Model
{
use HasFactory;
public function features(){
return $this->hasMany(Feature::class);
}
}
and in User model:
class User extends Model
{
use HasFactory;
public function card(){
return $this->belongsTo(User::class);
}
public function features(){
return $this->card->features;
}
}
I get error:
App\Models\User::features must return a relationship instance.
What you really want is an accessor function, not a relationship. This is how you would do to achieve what you want.
class User extends Model
{
use HasFactory;
protected $appends = ['features']; //to append features in the response json
public function card(){
return $this->belongsTo(Card::class); //editted User to Card
}
public function getFeatures(){ //accessor method
return $this->card->features()->get();
}
}
sample result after returning user in a controller function
return User::query()->with('card')->first();
However, the right way is to access the features through the Card Relationship because these two models have a direct relationship.

How to filter User by role based on the called Model?

I'm using Laratrust to manage these roles:
Patient
Doctor
I've a class called User which is the main entity, then I have a specific class for each role: Patient and Doctor.
Problem
To retrieve a list of users with the role of doctor I have to write:
User::whereRoleIs('doctor')->get();
The problem's that I have defined some relationship within the class Doctor, eg:
<?php
namespace App\Models;
use App\Models\Boilerplate\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Doctor extends User
{
use HasFactory;
protected $table = 'users';
public function patients()
{
return $this->belongsToMany(Patient::class, 'invites', 'doctor_id', 'user_id');
}
}
and I cannot access to the relationship patients from the User model.
Is there a way to call Doctor:with('patients')->get() and return automatically the users which have the role of Doctor?
So if I type: Doctor:all() the result must be equal to User::whereRoleIs('doctor')->get()
How can I do this?
Splitting data like this by model isn't really the intended use for models. Models in Laravel contain data on a per-table basis. In order to achieve what you want I would either make a DocterService that has methods in it to retrieve docter users by calling User::whereRoleIs('doctor') or just use this method straight away.
If you really want to use the model though you can use scopes. (https://laravel.com/docs/9.x/eloquent#query-scopes) Create a new scope that includes the whereRoleIs('doctor') method
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class DoctorScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->whereRoleIs('doctor');
}
}
and apply it to the model by adding the following to the model:
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
static::addGlobalScope(new DoctorScope);
}

Laravel responsibility in the classes

I have a project on Laravel and need to do refactoring. I've read about Service provider and Dependency injection and have some questions.
This is a short structure: user model, event model, favorite user model and etc. Also, there are controllers for all models. Every event has a creator and client (user relationship). In every class, I am injecting appropriate service: User Service, Event service, Favorite user service and etc.
Let's consider the example - I want to delete the user:
class UserController extends Controller
{
/**
* #var UserService $userService
*/
protected $userService;
/**
* UserController constructor.
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
protected function delete(int $id)
{
$user = User::find($id);
if ($user) {
$this->userService->setUser($user);
$this->userService->delete();
}
}
Inside User service, I am processing user deleting - update the appropriate field. Also, I need to cancel all user events and delete favorite users.
My question is where should I do it? Should I inject event and favorite user service in UserController or in UserService? Or maybe there is a better way to do this action. Thx in advance
Seems like you have many actions depending on deleting user, so I would consider using Events and inside each listener handle the specifics of it.
class UserController extends Controller
{
/**
* #var UserService $userService
*/
protected $userService;
/**
* UserController constructor.
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
protected function delete(int $id)
{
if(!$this->userService->delete($id)) {
// return some error;
}
event(new UserWasRemoved($id));
// return success response
}
class DeleteUserService {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function delete($id){
return $this->user->delete($id);
}
}
// app/Providers/EventServiceProvider
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
UserWasRemoved::class => [
CancelUserEvents::class,
RemoveUserFavorites::class,
// etc...
],
];
}
if deleting a user is much code, I will create DeleteUserService class which will contain all the code needed to delete a user and the effects of the delete.
class DeleteUserService {
public function __construct(int $userId)
{
$this->userId = $userId;
}
public function delete(){
$this->deleteUser();
$this->updateAppropriateFields(); // of course the name should be clearer
$this->deleteEvents();
$this->deleteFavoriteUser();
...
}
private function deleteUser(){...}
private function updateAppropriateFields(){...}
private function deleteEvents(){...}
private function deleteFavoriteUser(){...}
...
}
and in your controller either you inject the service or instantiate a new instance in the controller method
class UserController extends Controller
{
...
public function delete(int $id)
{
$user = User::findOrFail($id);
$deleteService = new DeleteUserService($user->id);
$deleteService->delete();
}
}
it's always a good idea to break your large function into one or more classes.
I suggest you abandon your approach to using services like this. Everything that you implement with services has already been implemented in Laravel, only even easier. You are now implementing more cumbersome logic on top of a simple, ready-made one.
For each object of your subject area (user, event, favorite user) add model class. Add in them the information of tables, the data from which belong to them - unless of course you use relational storage Eloquent Model Conventions. Here I have a question for you - does the favorite user entity need a separate class? If the User and the FavoriteUser have the same characteristics (that is, class members in the implementation), then there is no need to distribute them into different classes, and it is enough to add an additional isFavourite() (bool) attribute - in the class and in the table.
Implement the necessary methods in the controllers for each of your model classes as described in the documentation Defining Controllers. Depending on the type of the client part, the return of the response can be either JSON for the RESTful API, or a blade template with the transmitted data Views. Here, in the controller, you should implement a method to delete the model.
If you do not want the logic to be similar, that is, get rid of the similar methods all(), get(), post(), put(), delete() and others for UserController, for EventController, ... (with the exception of model classes - which will be different), then I advise you use the following architectural trick (this is optional, of course). Develop a universal layer - a class of a universal model, a class of a universal controller, a class of a universal model repository (if you use it in development). And in the controller, describe the common logic for all model classes, all(), get(), post(), put(), delete(). And then inherit each concrete class of the model from the universal, each concrete class of the controller from the universal - and so on. But!
In a concrete class of the model, it is necessary, for example, in an array, to list the attributes of the relational storage table, where you get the data from; it is also necessary to specify the name of the class in the variable - so that the controller can understand which class it should work with.
And in the controller in any way pass data about the model class - for example, using DependencyInjection Dependency Injection & Controllers.
With this approach, the classes of concrete controllers become thin, and the increase in code in them occurs only due to the redefinition of universal methods or the implementation of custom ones.
The advantage of this approach is also that there is no need to add routes of a similar structure. For example, a universal route will suffice for you
Route::get('{entity}/{id}', function ($entity, $id) {
$module = ucfirst($entity);
Route::get("{$entity}/{$id}", "{$module}Controller#get");
});
instead of many of the same type
Route::get('user/{id}', 'UserController#get');
Route::get('event/{id}', 'EventController#get');
and the like.

How to get access to relationship in model User Laravel?

I have default User model:
class User extends Authenticatable implements HasRoleContract
{
use Notifiable, HasRole;
}
With one relationship inside:
public function distributor() {
return $this->hasOne('App\DistributorContacts', 'distributor_id', 'id');
}
So, when user passed authorization I can not see this relation in object:
{{dd(Auth::user())}}
you may use ->with('distributor') on your user Object to get relationships loaded.
e.g.
$user = new User()->with('distributor');
dd($user->distributor);
or
Auth::user()->with('distributor');

Eloquent ORM relationships one-to-one or one-to-many?

I see the power of using Eloquent but have yet to put it to good use in my project. With two tables I want to achieve this:
//output - LinkedIn
echo User::find(42)->SocialProvider->Name
I have a user table and I have a SocialProvider table with a list of social sites with their name and api key info.
Record 42 in my user's table has a column 'SocialProviderID' with the the id of the LinkedIn record in my SocialProvider table.
I have defined the relationships in model classes as following
class User extends BaseModel implements UserInterface, RemindableInterface {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'dbo_RegisteredUser';
protected $primaryKey = "UserID";
public function SocialProvider() { return $this->hasOne('SocialProvider','id'); }
AND
class SocialProvider extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'dbo_SocialProvider';
public function user() { return $this->belongsTo('User','SocialProviderID'); }
But the queries and results are not executing as I'd like.
Does anyone have an ideas?
I'm using MSSQL but in my more familiar MYSQL, I would like this ORM scenario to perform a join like this:
SELECT * FROM dbo_RegisteredUser
LEFT JOIN (dbo_SocialProvider)
ON (dboRegisteredUser.SocialProviderID=dbo_SocialProvider.id)
WHERE dbo_RegisteredUser.UserID=42
Thanks
Jon.
Ahh I figured it out with a bit of debugging. I specified the relationships incorrectly.
In my User model I needed
class User extends Eloquent implements UserInterface, RemindableInterface {
public function socialProvider() {
return $this->belongsTo('SocialProvider','SocialProviderID');
}
Well...relationships use the ID of the model in question....and you're not referencing the actual user anywhere...
You need to have a collumn in the social Provider table that stores the user_id
Then in your functions...
public function SocialProvider() { return $this->hasOne('SocialProvider','user_id'); }
and...
public function user() { return $this->belongsTo('User'); }

Resources