Laravel: relations on same table - php

In my application I have a users table, in this table there is a field called managedByUsername which is the username of that particular user's manager.
To get your employees specifically you could perform a query as follows:
$employees = User::where('managedByUsername', auth()->user->username)->get()
To get your manager, on the User model you could have the relation;
public function mananager()
{
return $this->belongsTo(User::class, 'username', 'managedByUsername');
}
However, I can't think of how you would do this the other way around?
Perhaps
public function employees()
{
return $this->hasMany(User::class, 'username', 'managedByUsername');
}
But this obviously wouldn't work.
I have also tried the following:
/**
* Get the manager for this user
*
* #return void
*/
public function mananager()
{
return $this->belongsTo(User::class, 'managedByUsername', 'username');
}
/**
* Get the manager for this user
*
* #return void
*/
public function employees()
{
return $this->hasMany(User::class, 'managedByUsername', 'username');
}

The best approach to solve this problem would be to use the id of the user as the foreign key for manager.
So replace managedByUsername field with manager_id.
Then, you can write your Eloquent relations as:
public function mananager()
{
return $this->belongsTo(User::class, 'manager_id');
}
public function employees()
{
return $this->hasMany(User::class, 'manager_id');
}
Hope this helps to solve your problem.

Related

How to deploy relationship in pivot of three model in laravel?

I'm developing a role and permissions based on laravel framework.
I have 3 models :
Weblog
User
Permissions
This is pivot table
user_id , weblog_id , permission_id
Now, a user can have a weblog with permission id 1,2 and another weblog with permission 1,2,3,4
How can I deploy relationships? and how can I check user permissions when managing a weblog. (middleware and ...)
With the fact that Permission are specific to Weblog
Say the pivot table is called permission_user_weblog
class User extends Model
{
public function weblogs()
{
return $this->belongsToMany(Weblog::class, 'permission_user_weblog');
}
public function permissionsFor(int $weblogId)
{
$permissionIds = null;
$this->weblogs()
->where('id', $weblogId)
->with('permissions')
->get()
->each(function($weblog) use(&$permissionIds) {
$permissionIds = $weblog->permissions->pluck('id');
});
return $permissionIds;
}
}
class Weblog extends Model
{
public function users()
{
return $this->belongsToMany(User::class, 'permission_user_weblog');
}
public function permissions()
{
return $this->belongsToMany(Permission::class, 'permission_user_weblog');
}
}
class Permission extends Model
{
public function weblogs()
{
return $this->belongsToMany(Weblog::class, 'permission_user_weblog');
}
}
Then you can check anywhere for whether logged in user has specific permission for a specific weblog
public function update(Request $request, $weblogId)
{
$user = auth()->user();
$permissions = $user->permissionsFor($weblogId);
//Check whether the logged in user has permission identified by id 1 or 4 for weblog
$can = !! $permissions->intersect([1,4])->count();
//Do rest of processing
}
your Weblog,User,Permission has ManyToMany Relation, its a kind of odd but if you want to have this kind of relation its not a problem.
just consider each pair a ManyToMany. and every one of those can have a hasMany to Pivot (i named it Access) too (based on your needs).
User model:
class User extends Model{
/**
* retrive weblogs
*
* #return BelongsToMany weblogs
*/
public function weblogs()
{
return $this->belongsToMany(App\WebLog::class,'accesses_table')
->withPivot("permission_id")
->using(App\Access::class);
}
/**
* retrive permissions
*
* #return BelongsToMany permissions
*/
public function permissions()
{
return $this->belongsToMany(App\Permission::class,'accesses_table')
->withPivot("weblog_id")
->using(App\Access::class);
}
/**
* retrive access
*
* #return hasMany [description]
*/
public function accesses()
{
return $this->hasMany(App\Access::class, "user_id");
}
}
Weblog model:
class Weblog extends Model{
/**
* retrive users
*
* #return BelongsToMany users
*/
public function users()
{
return $this->belongsToMany(App\User::class,'accesses_table')
->withPivot("permission_id")
->using(App\Access::class);
}
/**
* retrive permissions
*
* #return BelongsToMany permissions
*/
public function permissions()
{
return $this->belongsToMany(App\Permission::class,'accesses_table')
->withPivot("user_id")
->using(App\Access::class);
}
/**
* retrive access
*
* #return hasMany [description]
*/
public function accesses()
{
return $this->hasMany(App\Access::class, "weblog_id");
}
}
Permission model:
class Permission extends Model{
/**
* retrieve users
*
* #return BelongsToMany users
*/
public function users()
{
return $this->belongsToMany(App\User::class,'accesses_table')
->withPivot("weblog_id")
->using(App\Access::class);
}
/**
* retrieve weblogs
*
* #return BelongsToMany weblogs
*/
public function weblogs()
{
return $this->belongsToMany(App\Weblog::class,'accesses_table')
->withPivot("user_id")
->using(App\Access::class);
}
/**
* retrive access
*
* #return hasMany [description]
*/
public function accesses()
{
return $this->hasMany(App\Access::class, "permission_id");
}
}
and you can have a model for your pivot, which i named it Access :
Illuminate\Database\Eloquent\Relations\Pivot;
class Access extends Pivot
{
public $incrementing = true;
public function user()
{
return $this->belongsTo(App\User::class);
}
public function weblog()
{
return $this->belongsTo(App\Weblog::class);
}
public function permission()
{
return $this->belongsTo(App\Permission::class);
}
}

Laravel - Show all project from company that user is in

I have a system where I want to show all the projects from a company. That specific company must be the company that the logged-in user is in. This sounds relatively simple, but I can't figure out how with my current database setup because neither projects nor the user has a project_ID, this because I'm using an intermediate table. I've built my database structure like below
Users
company_id
Companies
id
Projects
company_id
Project_User
user_id
project_id
With the above setup, I made all the connections with every model except a model for Project_User. These models are listed below.
User model
class User extends Authenticatable
{
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function role()
{
return $this->belongsTo(Role::class, 'role_id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function job()
{
return $this->belongsTo(Job::class, 'job_id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function company()
{
return $this->belongsTo(Company::class, 'company_id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function projects()
{
return $this->belongsToMany(Project::class);
}
public function isAdmin()
{
}
public function isCompanyOwner() {
if(Auth::check())
return(Auth::user()->job_id == 1);
return false;
}
}
Project model
class Project extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users()
{
return $this->belongsToMany(User::class);
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function company()
{
return $this->belongsTo(Company::class);
}
}
Company model
class Company extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function users()
{
return $this->hasMany(User::class);
}
/*
*
*/
public function projects()
{
return $this->hasMany(Project::class);
}
}
How can I show all projects from the company that the logged user is in on my home screen? With this current DB setup?
The current Controller gets ALL projects and none particular from a specific company.
Controller
public function index()
{
$projects = Project::all();
return view('Project.show', compact('projects'));
}
Without getting technical, this would work:
$request->user()->company->projects;
Get the company for the current user then get the projects for that company.

Spatie User Permissions - Belongs To Many Companies, solution feedback

I have a situation where a user can belong to many teams/companies and within that team/company they can have different roles and permissions depending on which one they are signed into. I have come up with the following solution and would love some feedback!
Note: Currently I am only using the model_has_roles table with Spatie permissions and always use $user->can('Permission') to check permissions.
Our company model has the following relationships and method
class Company extends Model
{
public function owner(): HasOne
{
return $this->hasOne(User::class, 'id', 'user_id');
}
public function users(): BelongsToMany
{
return $this->belongsToMany(
User::class, 'company_users', 'company_id', 'user_id'
)->using(CompanyUser::class);
}
public function addTeamMember(User $user)
{
$this->users()->detach($user);
$this->users()->attach($user);
}
}
We modify the pivot model to have the Spatie HasRoles trait. This allows us to assign a role to the CompanyUser as opposed to the Auth User. You also need to specify the default guard or Spatie permissions squarks.
class CompanyUser extends Pivot
{
use HasRoles;
protected $guard_name = 'web';
}
On the user model, I have created the HasCompanies Trait. This provides the relationships and provides a method for assigning the roles to the new company user. Additionally, it overwrites the gate can() method.
A user can belong to many companies, but can only have one active company at a time (i.e. the one they are viewing). We define this with the current_company_id column.
It is also important to ensure the pivot table ID is pulled across (which it will not be as standard) as this is now what we are using in the Spatie model_has_roles table.
trait HasCompanies
{
public function companies(): HasMany
{
return $this->hasMany(Company::class);
}
public function currentCompany(): HasOne
{
return $this->hasOne(Company::class, 'id', 'current_company_id');
}
public function teams(): BelongsToMany
{
return $this->belongsToMany(
Company::class, 'company_users', 'user_id', 'company_id'
)->using(CompanyUser::class)->withPivot('id');
}
public function switchCompanies(Company $company): void
{
$this->current_company_id = $company->id;
$this->save();
}
private function companyWithPivot(Company $company)
{
return $this->teams()->where('companies.id', $company->id)->first();
}
public function assignRolesForCompany(Company $company, ...$roles)
{
if($company = $this->companyWithPivot($company)){
/** #var CompanyUser $companyUser */
$companyUser = $company->pivot;
$companyUser->assignRole($roles);
return;
}
throw new Exception('Roles could not be assigned to company user');
}
public function hasRoleForCurrentCompany(string $roles, Company $company = null, string $guard = null): bool
{
if(! $company){
if(! $company = $this->currentCompany){
throw new Exception('Cannot check role for current company because it has not been set');
}
}
if($company = $this->companyWithPivot($company)){
/** #var CompanyUser $companyUser */
$companyUser = $company->pivot;
return $companyUser->hasRole($roles, $guard);
}
return false;
}
public function can($ability, $arguments = []): bool
{
if(isset($this->current_company_id)){
/** #var CompanyUser $companyUser */
$companyUser = $this->teams()->where('companies.id', $this->current_company_id)->first()->pivot;
if($companyUser->hasPermissionTo($ability)){
return true;
}
// Still run through the gate as this will check for gate bypass
return app(Gate::class)->forUser($this)->check('N/A', []);
}
return app(Gate::class)->forUser($this)->check($ability, $arguments);
}
}
Now we can do something like this:
Create the role & permission
/** #var Role $ownerRoll */
$ownerRoll = Role::create(['name' => 'Owner']);
/** #var Permission $permission */
$permission = Permission::create([
'name' => 'Create Company',
'guard_name' => 'web',
]);
$ownerRoll->givePermissionTo($permission);
Create a new company with an owning user and then switch this company to that owner's active company.
public function store(CompanyStoreRequest $request)
{
DB::transaction(function () use($request) {
/** #var User $owner */
$owner = User::findOrFail($request->user_id);
/** #var Company $company */
$company = $owner->companies()->create($request->validated());
$company->addTeamMember($owner);
$owner->assignRolesForCompany($company, 'Owner');
$owner->switchCompanies($company);
});
return redirect()->back();
}
So this all works, my main concerns are that:
We are overwriting the can method. There may be other authorization methods/gate functions that are not caught.
We have 2 sets of model_permissions. The Auth user and the company user. I think I need to build in some checks to ensure that only the correct kinds of users can be assigned to the roles. At this stage, all administrator users would have permissions assigned to their auth user, while any users who own a company should only have permissions on the company user model

How to use one model for multiple tables in laravel 5.6

I am looking for solutions, but can't really understand. I'm new in Laravel and I want a simple instruction on how to use one model for multiple tables like CodeIgniter as follows:
Controller myController:
public function shipBuilding()
{
$data = $this->input->post();
$response = $this->MyModel->shipbuildingSave($data);
}
public function contact()
{
$data = $this->input->post();
$response = $this->MyModel->contactSave($data);
}
Model MyModel:
public function shipbuildingSave($data){
$this->db->insert('tbl_shipbuilding', $data);
return $this->db->insert_id();
}
public function contactSave($data){
$this->db->insert('tbl_contact', $data);
return $this->db->insert_id();
}
This is not how models work in Laravel. each model should be a representation of one single table.
You could, however, change the table name on booting the model up:
class Flight extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'example';
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
// Set the $this->table depending on some logic.
}
}
But again, this is probably not recommended for your case.

Laravel 5.3 add custom attribute to model

Within my model I wanting to add a role attribute that is a value based on what relationships the return user model has, so my user model has various relationships on it like below,
/*
* User - Supers
* 1:1
*/
public function super() {
return $this->hasOne('App\Super');
}
/*
* User - Teachers
* 1:1
*/
public function staff() {
return $this->hasOne('App\Teacher');
}
/**
* User - Students
* 1:1
*/
public function student() {
return $this->hasOne('App\Student');
}
What I am wanting to do is check if the user has a student or super relationship and set a role attribute based on that.
I thought I would be able to something like this,
public function getRoleAttribute() {
if($this->student()->user_id) {
return "Student";
}
//if($this->super)
}
but sadly not, the exception that gets return is this,
Undefined property: Illuminate\Database\Eloquent\Relations\HasOne::$user_id
does anyone have any idea how I can achieve this?
Ok, I misunderstood the question at first.
A better way is to create a relation detection function and call it inside the model's constructor, then assign it to a new attribute.
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
protected $table = 'Users';
private $role;
public __construct() {
$this->setRole();
}
private function setRole() {
if (count($this->super)){
$this->role = 'super';
}elseif (count($this->staff)) {
$this->role = 'staff';
} elseif (count($this->student)) {
$this->role = 'student';
} else {
$this->role = 'none';
throw new \exception('no relation');
}
}
public function super() {
return $this->hasOne('App\Super');
}
/*
* User - Teachers
* 1:1
*/
public function staff() {
return $this->hasOne('App\Teacher');
}
/**
* User - Students
* 1:1
*/
public function student() {
return $this->hasOne('App\Student');
}
}
I based part of my answer on Laravel check if related model exists
edit: Laravel has a built-in-way to set attributes https://github.com/illuminate/database/blob/v4.2.17/Eloquent/Model.php#L2551

Categories