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
Related
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);
}
}
Normally we can simplify finding User by id logic in controller by injecting the User Class in parameter. Like this:
class UserController extends Controller
{
public function show(User $id)
{
return $user;
}
}
But now I must treat the Id to find like this:
<?php
namespace App\Http\Controllers;
class UserController extends Controller
{
public function show(User $id)
{
$preformattedId = '98'.$id;
$user = User::find($preformattedId );
return $user;
}
}
My basic question is: how I can achieved that same trick to my preformatted id in below code like the above code?
Note: I have to use the Id this way because i work with legacy database that actually adding that '98' prefix in every Id, despite that we only use characters after that prefix.
You can use Inversion of Control by using explicit binding on your router.
In your RouteServiceProvider
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return User::find('98'.$value);
});
}
Or in your User model
/**
* Retrieve the model for a bound value.
*
* #param mixed $value
* #param string|null $field
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
return $this->find('98'.$value);
}
https://laravel.com/docs/7.x/routing#explicit-binding
You can share your route file ?
But if your file is
Route::get('user/{id}', 'UserController#show');
When you use
class UserController extends Controller
{
public function show(User $id)
{
// you don't need use find function, it is make automatic by laravel
$user = $id;
return $user;
}
}
if you want to get id, just remove User type inside show parameter
class UserController extends Controller
{
public function show($id)
{
$preformattedId = '98'.$id;
$user = User::find($preformattedId );
return $user;
}
}
I'm trying to implement multiple controllers which listens to one route /account.
There are two controllers and only one should be executed on that URL where the choice lies within user's role.
namespace AppBundle\Controller;
use AppBundle\Entity\Role;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/account")
*/
abstract class DashboardController extends Controller
{
protected $userRoles;
public function __construct()
{
$this->userRoles = $this->getUser()->getRoles();
}
/**
* Get all user roles
*/
public function getRoles()
{
return $this->userRoles;
}
/**
* Get user account type
*
* #return Role
*/
public function getAccountType(): Role
{
$accountType = new Role();
foreach ($this->userRoles as $role) {
if(Role::ROLE_STUDENT == $role->getName()) {
$accountType = $role;
} else if(Role::ROLE_SCHOOL_REPRESENTATIVE == $role->getName()) {
$accountType = $role;
} else if(Role::ROLE_EMPLOYER == $role->getName()) {
$accountType = $role;
} else if(Role::ROLE_ADMIN == $role->getName()) {
$accountType = $role;
}
}
return $accountType;
}
}
namespace AppBundle\Controller;
class CompanyDashboardController extends DashboardController
{
public function __construct()
{
parent::__construct();
}
/**
* #Route("/", name="dashboard_company_home", methods={"GET"})
* #return Response
*/
public function index()
{
return $this->render('dashboard/company/index.html.twig');
}
}
namespace AppBundle\Controller;
class AdminDashboardController extends DashboardController
{
public function __construct()
{
parent::__construct();
}
/**
* #Route("/", name="dashboard_admin_home", methods={"GET"})
* #return Response
*/
public function index()
{
return $this->render('dashboard/admin/index.html.twig');
}
}
That's what I've got so far.
You can't do this with "route" declarations, since the route listener is executed with higher priority than the security listener. Both happen during the KernelEvents::REQUEST event, but routing comes before firewall.
When the route to controller mapping is being resolved, you do not have yet user information (which is why you can't simply attach another a listener and inject the user information on the Request object, so it's available to use in the route declaration for expression matching, for example).
Basically, one route, one controller. If you want to have diverging logic for these users, you'll have to apply it after you get into the controller.
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.
I have a test site where I've got a recipes, ingredients, and ingredient_uses tables. Each recipe "uses" ingredients in different quantities and prepared differently (eg. chopped, sliced, minced, grated), so the ingredient_uses table tracks the recipe_id and the ingredient_id with the other variable info.
The relationships look like this:
Recipe model:
public function ingredients()
{
return $this->hasManyThrough('App\Ingredient', 'App\IngredientUse');
}
public function ingredientUses()
{
return $this->hasMany('App\IngredientUse');
}
Ingredient_Use model:
public function ingredient()
{
return $this->hasOne('App\Ingredient');
}
public function recipe()
{
return $this->belongsTo('App\Recipe');
}
Ingredient model:
public function recipe()
{
return $this->belongsToMany('App\Ingredient');
}
I thought I wanted a hasManyThrough relationship from the recipe to the ingredient table, using the ingredient_uses as the middle table. But the sql is wrong:
SELECT `ingredients`.*, `ingredient_uses`.`recipe_id`
FROM `ingredients`
INNER JOIN `ingredient_uses`
ON `ingredient_uses`.`id` = `ingredients`.`ingredient_use_id`
WHERE `ingredient_uses`.`recipe_id` = 1
This is what I think I want:
SELECT `ingredients`.*, `ingredient_uses`.`recipe_id`
FROM `ingredients`
INNER JOIN `ingredient_uses`
ON `ingredient_uses`.`ingredient_id` = `ingredients`.`id`
WHERE `ingredient_uses`.`recipe_id` = 1
Is there a more appropriate relationship that I should be using?
First thing - you don't need Ingredient_Use model. Model examples:
class Ingredient extends Model
{
/**
* #var string
*/
protected $table = 'ingredients';
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function recipes()
{
return $this->belongsToMany(Recipe::class, 'recipes_ingredients', 'ingredient_id');
}
}
class Recipe extends Model
{
/**
* #var string
*/
protected $table = 'recipes';
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function ingredients()
{
return $this->belongsToMany(Ingredient::class, 'recipes_ingredients', 'recipe_id')->withPivot('additional', 'fields');
}
}
Access to additional fields example
$recipe->pivot->additional;
in Ingredient model
class Ingredient extends Model
{
public function recipe(){
return $this->belongsToMany(Recipe::class, 'ingredient_uses', 'ingredient_id', 'recipe_id');
}
}
other models will be empty thats mean there would not be any function inside Recipe model and Ingredient model .
u can see documentation about this
https://laravel.com/docs/5.3/eloquent-relationships#many-to-many