I have one more relationship in the project like this it works but this is not working. I can't see any value on the screen. It return null. I'm not good with Laravel and relationships in database. I think my controller and routes are correct but my relationships are incorrent.
My User model contains the following relationship:
public function follows(){
return $this->hasMany('App\Models\Follow');
}
My Follow model contains the following relationship:
public function user(){
return $this->belongsTo('App\Models\User');
}
My blade is:
#foreach ($user->follows as $followr)
<h6><strong>{{$followr}}</strong></h6>
#endforeach
please refer laravel eloquent documentation here
you can pass foreign key and local key as 2nd and 3rd arguement in hasMany function and pass foreign key and owner key as 2nd and 3rd arguement in belongsTo function
Related
For reference I used this post and Laravel documentation:
Laravel 5.6 getRouteKeyName() not working
https://laravel.com/docs/5.8/routing#explicit-binding
In my routes I have a resources array like this:
Route::resources([
...
'state' => 'StateController',
...
]);
In my controller I am trying to access models by the slug. My state table and model has name and slug columns.
StateController
public function show(State $state)
{
dd($state);
// return view('state.show', compact('state'));
}
public function getRouteKeyName()
{
return 'slug';
}
If I remove the State model typecast it prints out the string indiana for the url: http://codebase.localhost.com/state/indiana But when I put the typecast back in, it gives me a 404. It can't find the model.
I thought getRouteKeyName was supposed to retrieve the model by the string passed.
What gives?
Here are my models fillables
'name', 'order', 'slug'
It's definitely a record in my table.
Looks like the issue is with the getRouteKeyName method being defined in your controller.
From the docs:
you may override the getRouteKeyName method on the Eloquent model
Try moving the getRouteKeyName method into your State model.
I have two routes:
Route::get('subjects/{subject}/{tag?}', 'SubjectController#show');
Route::get('subjects/{subject}/{tag}/{lesson}','LessonController#show');
When I hit the first route, it works properly but when I hit the second route, I get the following error response:
Sorry, the page you are looking for could not be found.
Is this because laravel is trying to treat the /{tag}/{lesson} portion of 2nd route as the value of the parameter of 1st route?
My controller methods are as follows:
//SubjectController.php
public function show($subjectSlug, $tag = null)
{
dd('Inside SubjectController#show');
}
//LessonController.php
public function show(Subject $subject, Tag $tag, Lesson $lesson)
{
dd('Inside LessonController#show');
}
When I visit, say,
localhost:3000/subjects/mysubject-slug/1
It matches the first route and responds accordingly, but when I visit,
localhost:3000/subjects/mysubject-slug/1/mylesson-slug
it shows the page not found error. How can I fix this?
As mentioned in the comments, because of Route Model Binding you can end up with a 404 when the model for the binding can not be retrieved. When using implicit route model binding the primary key will be used to search against by default. This can be changed on the model to use a different field, in this case the slug field.
"If you would like model binding to use a database column other than id when retrieving a given model class, you may override the getRouteKeyName method on the Eloquent model"
Laravel 5.5 Docs - Routing - Route Model Binding - Implicit Binding
public function getRouteKeyName()
{
return 'slug';
}
Try to change your controller to
//LessonController.php
public function show($subject, $tag, $lesson)
{
dd('Inside LessonController#show');
}
And see if it gets hit. If it does, your binding is done incorrectly.
On the side note, I suppose you don't have Route::resource() set up somewhere up in the routes file?
I'm new with Laravel or any MVC.
I created the models, I create the route from web.php, I created the view with Blade, I created the controller and fetched all the data from a single model. Now, I want to make a join or something similar.
I want to get something this:
Name | Section name | Status.
In my user Model I have:
ID | Name | SectionID | Status
In my sections Model I have:
ID | SectionID | Active
Currently I created the controller like this:
public function show($name)
{
if(User::where('name', '=', $name)->exists())
{
return view('profile', ['user'=>User::where('name', $name)->first()]);
}
else
{
return 'User not found';
}
}
And in my Blade I'm getting content like this: {{ $user->name }}
Hmm you have to define the relationship between user and section model.
Please check this thread for how to work with relationships in laravel Laravel Eloquent Relationships.
Then whenever you get the user you can load any relationship that its defined on User model.
In your case you are performing two unecessary database calls, so I would suggest you to check out DRY principle.
Lets say in User has one Section you define the relationship in User model like so:
public function sections(){
return $this->hasOne(Section::class);
}
Then the reverse relationship from section model:
public function user(){
return $this->belongsTo(User::class);
}
So now your method in controller might look something along the lines of this:
public function show($name){
return view('profile', ['user'=>User::with('sections')->where('name', $name)->firstOrFail()]);
}
This way you eagerload the sections that are tied to that user, for more about Laravel eagerloading
Then in the blade you can access any of the attributes from user or section model.
I'm a Laravel user. I am currently trying to use the Laratrust package for web admin purposes. For User and Role implementations I want one user to hold multiple roles and one role many users can have. In my opinion, the most suitable relationship is Many to Many Relationship, am I correct? Then, is the function I made right?
App\User;
public function roles()
{
return $this->belongsToMany('App\Role');
}
App\Role;
public function users()
{
return $this->belongsToMany('App\User');
}
Then how do I display the two interconnected data in one blade file? Thanks.
Define relations as users() and roles(). You can display all roles of the user with something like this:
#foreach ($user->roles as $role)
{{ $role->name }}
#endforeach
I am creating an application using Laravel 5.1 with users, roles and actions.
The table setup is like so:
user
id name
1 John Smith
2 Fred Smith
role
id name
1 Administrator
2 Customer
role_user
user_id role_id
1 1
2 1
action
id name description path
1 dashboard ability to access dashboard /admin
2 editAdmin ability to edit admins /admin/users/administrators/{id}/edit
action_role
action_id role_id
1 1
2 1
The user table holds ALL users on the site, including administrators and customers.
The role table holds all the possible roles a user can have. For example Administrator or Customer.
The role_user table is a pivot table which links role to user.
The action table lists all of the actions possible (i.e. urls or routes) on the app.
The action_role is a pivot table which links action to role.
So to summarise:
Users have roles
Roles have actions
A user can have many roles
A role can have many actions
I want to have a middleware setup which checks on page load if the user has permissions to view the current page. In order to do this, I need to be able to access a users actions, using a call like this:
$user->roles()->actions();
How do I setup my eloquent relationships to support this kind of call?
UPDATE
My relationships are setup like so in my models:
User
/**
* The roles that belong to the user.
*
* #return Object
*/
public function roles()
{
return $this->belongsToMany('App\Models\User\Role')->withTimestamps();
}
Role
/**
* The actions that belong to the role.
*
* #return Object
*/
public function actions()
{
return $this->belongsToMany('App\Models\User\Action');
}
/**
* The users that belong to the role.
*
* #return Object
*/
public function users()
{
return $this->belongsToMany('App\Models\User\User');
}
Action
/**
* The roles that belong to the action.
*
* #return Object
*/
public function roles()
{
return $this->belongsToMany('App\Models\User\Role');
}
Eloquent does not have a HasManyThrough relationship across 2 pivot tables.
1. You can get the actions by lazy eager loading the roles and actions, then extracting them:
$actions = $user->load('roles.actions')->roles->pluck('actions')->collapse()->unique();
2. You can check the action directly in the database, using a whereHas constraint:
$allowed = $user->roles()->whereHas('actions', function ($query) use ($action) {
$query->where('name', $action);
})->exists();
For handling user, user roles and user permissions you can simply use Toddish package.
There are lot of things this package does for you. like:
$user->is('Your Defined Role');
$user->can('add_user');
$user->level(7);
For installation just read it's documentation.
Although you have mentioned that your Eloquent models (and their relationships) are set up, I am assuming that you have the following in your application already:
User model
class User extends Eloquent {
public function roles() {
return $this->belongsToMany('App\Models\Role');
}
}
Role model
class Role extends Eloquent {
public function actions() {
return $this->belongsToMany('App\Models\Action');
}
public function users() {
return $this->belongsToMany('App\Models\User');
}
}
Action model
class Action extends Eloquent{
public function roles(){
return $this->belongsToMany('App\Models\Role');
}
}
Given the above set properly, you can't certainly make a call like the following as Laravel will assume you're making a call to a Query Builder method or whatever [which in fact doesn't exist]:
$userActions = App\Models\User::find(1)->roles()->actions();
Instead, you have to use Laravel's magical whereHas method to query relations where multiple nested relations are involved.
Now, having the user's id and the current allowed action [which indeed can be utilized in your middleware], it can be determined whether the user is allowed to see the page:
$hasAccess = App\Models\User::whereHas('roles.actions', function($q) use ($id, $action){
$q->where('users.id', $id);
$q->where('actions.name', $action);
})->get()->toArray();
if($hasAccess){ // or `count($hasAccess) > 0`. The former will work since any number > 0 evaluates to true in PHP
//user has access
}
Notice the nesting of relationships with ..
Relationship Existence in Laravel:
In Laravel the existence of a relationship between models is determined with has and whereHas methods. The has method is used to only determine if a relationship exists, e.g. by executing App\User::has('roles')->get() you'll always get a list/collection of users which at least have any roles.
More power to this can be added with whereHas with which you can add where clauses to the actual query.
Hi this is how I solved this.
I didn't use an actions table. Just users, roles and the user_role table.
After the solution, I pass my custom middleware a role array to every route I create like:
Route::get('insights',[
'as'=>'insights',
**'middleware'=>['access.level'],**
**'roles'=>['admin','customer'],**
'uses'=>'CustomersController#index'
]);
Files modified:
app\Http\Controllers\Controller.php
Custom middleware: CheckAccessLevel
app\Http\Kernel.php
app\Http\routes.php
Controller.php
In this file, I wanted to eager load the current logged in user with his roles and make it a global variable in all views as {{currentUser}} and in all my controllers as "$this->currentUser". this is the code:
protected $currentUser;
public function __construct() {
if(isset(Auth::user()->username)){
$this->currentUser = User::with('roles')->where(['username' => Auth::user()->username])->first();
// Share this property with all the views and controllers in your application.
view()->share('currentUser', $this->currentUser);
}
}
Custom Middleware: CheckAccessLevel.php
Over here in the middleware, I retrieve the roles array passed to the route and and also the roles assigned to the current user.
After i get these variables, I intersect them to see if there is a match before I pass the user on. check the code below:
//if user is not logged in, show them where to login :)
if (!Auth::check()) {
return redirect()->route('user::login');
}
//Otherwise get the roles for that route
$get_route_action = $request->route()->getAction();
$get_route_roles = $get_route_action['roles'];
//Eager load the user with their list of roles
$eager_user = User::with('roles')->where(['id'=>$request->user()->id])->first();
$user_roles = $eager_user->roles->pluck('role_name')->toArray();
//intersect the users roles with the route roles to see if there is a match
foreach ($user_roles as $user_role){
if(in_array($user_role, $get_route_roles)){
return $next($request);
}
}
Kernel.php
over here, i register my route as "access.level" inside the route middleware group
Route.php
then whenever I create a route, I just pass in the allowed role names to it and voala! It works for me..
I hope this helps.
You need a HasManyThrough relationship here.. Here's something to get you started.
Judging by your given tables I see it has a one-to-many relationship with user and roles. While action and roles also has many-to-many relationship.
But first you need to create models for each entities(user,role,action) to follow the MVC structure. You could easily make these models from the command line using the Laravel's artisan with this command.
php artisan make:model User
Make sure to also add or change columns in the newly created migrations. Assuming you also have your pivot tables set up for the 3 tables, you can now add the relationships.
class User extends Model {
//
protected $table = 'users';
/*
* #var table
*
*/
public function action()
{
return $this->hasManyThrough('App\Action', 'App\Role');
}
}
Now you have a HasManyThrough relationship. you can query like this directly no need to include role.
//this
$actions = User::find(1)->action();
//instead of
$actions = User::find(1)->role()->action();
//this will return all actions available to a user with id of 1
NOTE I also suggest you make your role and user models to be on a one to one relationship.