users and roles tables relationship through middleware parameter laravel 5.1 - laravel

I'm developing a user management website using laravel 5.1.
Users are not allowed to register but they are able to see their profiles. Registration can be done only by the admin, so i have two types of users (Admin and normal users).
I'm following this tutorial:
https://www.youtube.com/watch?v=8AQLUZ0X9ME
everything was ok until I reached to the User Model
User table:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password', 60);
$table->rememberToken();
$table->softDeletes();
$table->integer('role_id')->unsigned();
$table->timestamps();
});
Role table:
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->increments('id');
$table->string('role_name');
//
});
}
Role Model:
class Role extends Model
{
protected $table = 'roles';
protected $fillable = ['role_name'];
public function users()
{
return $this->hasMany('App\User');
}
User model:
public function role(){
return $this->belongsTo('App\Role','role_id');
}
public function hasRole($title){
$user_role = $this->role();
if(!is_null($user_role)){
$user_role = $user_role->role_name; //here is the problem with role_name
}
return ($user_role===$title)?true:false;
}
In PHPStorm the role_name is highlighted in yellow and it says
Field 'role_name' not found in class
\Illuminate\Database\Eloquent\Relations\BelongsTo less... (Ctrl+F1)
Referenced field is not found in subject class. Note: Check is not
performed on objects of type "stdClass" or derived.
I created 3 middlewares Update, Create, and Delete and they all have the same handle function:
public function handle($request, Closure $next, $Admin)
{
$User = $request->user();
return ($User->hasRole($Admin))?$next($request):response(view('errors.401'),401);
}
Routes file:
Route::get('/users','UserController#index');
Route::post('/users/create',['middleware'=>'create:Admin','uses'=>'UserController#store']);
Route::patch('/users/{id}',['middleware'=>'update:Admin','uses'=>'UserController#update']);
Route::delete('/users/{id}',['middleware'=>'delete:Admin','uses'=>'UserController#destroy']);
whenever i open up the create page i got this error:
"ErrorException in C:\wamp\www\laravelU\project - Copy5\app\User.php
line 42: Undefined property:
Illuminate\Database\Eloquent\Collection::$role_name"
I have been dealing with this code for 3 nights i need your help.
if there is any easier way to achieve my goal without using packages ?

The reason you are getting an error when trying $user_role->role_name is because you're technically looking for the property role_name in the BelongsTo class.
When trying to access a relationship of an eloquent model you can either use it as a dynamic property i.e.
$user_role = $this->role;
or
$user_role = $this->role()->get();
So you can change your hasRole method to something like:
public function hasRole($title){
if(is_null($this->role)){
return false;
}
return $this->role->role_name === $title;
}
The reason your application is throwing a MethodNotAllowedHttpException is because you are trying to access a POST route with a GET request. (anytime you navigate to a page using a web browser it will be a GET request).
What you should do is change the Route::post('create' ... to Route::get('create'... ad add something like Route::post('store' to submit to.
Have a look at https://laravel.com/docs/5.1/controllers#restful-resource-controllers
Secondly, you don't need to have 3 different middleware classes if they are just checking if the user is an admin.
Lastly, have a look at https://laravel.com/docs/5.1/validation#form-request-validation (including the Authorizing section)
Hope this helps!

Thanks Ross !
here is my routes after editing them:
Route::get('/users','UserController#index');
Route::get('/users/create',['middleware'=>'create:Admin','uses'=>'UserController#create']);
Route::post('/users/create',['middleware'=>'create:Admin','uses'=>'UserController#store']);
Route::patch('/users/{id}',['middleware'=>'update:Admin','uses'=>'UserController#update']);
Route::delete('/users/{id}',['middleware'=>'delete:Admin','uses'=>'UserController#destroy']);
I get this error when i open up the browser and trying to access the create page :
"ErrorException in C:\wamp\www\laravelU\project - Copy5\app\User.php
line 42: Undefined property:
Illuminate\Database\Eloquent\Collection::$role_name"
in the future, i will add one more role like managers who wont be able to delete users. so i feel it is easier using multiple middlewares but $role_name is pissing me off !

Related

Let show method of resource controller to show soft-deleted model using policy

Steps to reproduce the issue I'm facing
1- Create the Item:
php artisan make:model Item --all
2- Create the resource in web.php:
Route::resource('items', ItemController::class);
3- Then, in the ItemController's constructor, link the ItemPolicy:
public function __construct()
{
$this->authorizeResource(Item::class);
}
4- Return true in all of the ItemPolicy's methods (so you get authorized)
public function view(User $user, Item $item)
{
return true;
}
5- Add SoftDeletes trait in Item model:
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Item extends Model
{
use HasFactory, SoftDeletes;
}
6- Add SoftDeletes in Item migration. Run it.
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->softDeletes();
});
}
7- Write a dd in show method in ItemController to check out if you are actually entering inside the method:
public function show(Item $item)
{
dd('You are inside the show function');
}
8- Create a item in the db and go to GET /items/1. You should see the dd message.
Route::get('/testCreate', function () {
$item = Item::create();
return redirect("/items/$item->id");
});
9- Delete the item. Now, in GET /items/1, the message doesn't appear anymore (instead, I get 404).
Route::get('/testDelete', function () {
$item = Item::firstOrFail();
$item->delete();
return redirect("/items/$item->id");
});
The question
How to enter the show method in GET /items/1 despite the model is soft-deleted?
Notes
Make sure you are logged in
I already checked out this question but I'm not able to make it to work
I also tried to change the show method in the controller to this way ($id instead of Item $item), but anyways I get 404. I'm not entering the method, policy is in the middle and doesn't let me in:
public function show($id)
{
dd($id);
// dd($item);
}
It was as that easy as adding the withTrashed() function at the end of the route resource route defined in routes/web.php file.
Before:
Route::resource('items', ItemController::class);
After:
Route::resource('items', ItemController::class)->withTrashed(['show']);
Related docs here & here.
Update
In an older project I had, despite of doing the explained above, I was getting this error:
Method Illuminate\Routing\PendingResourceRegistration::withTrashed does not exist.
I solved it by updating the project's composer pagackes:
composer update
Now works fine.

Laravel : How to add conditional clause on a model having Many to Many model relationship?

I have this method on my User Model.
public function reviews()
{
return $this->belongsToMany(Review::class, 'review_user')->withTimestamps();
}
and its inverse here at Review Model.
public function reviewers()
{
return $this->belongsToMany(User::class, 'review_user')->withTimestamps();
}
They are connected via a pivot table review_user with following structure.
$table->id();
// User ID
$table->unsignedBigInteger('user_id')->nullable();
$table->foreign('user_id')
->references('id')
->on('users');
// Review ID
$table->unsignedBigInteger('review_id')->nullable();
$table->foreign('review_id')
->references('id')
->on('reviews');
$table->timestamps();
The review model has a boolean column status.
What I want is a users all reviews which has a status of true?
I have been able to achieve users all reviews using Auth::user()->reviews()->get();
How can I add a where clause, which can filter by status column ?
You can achieve this by using scope or Defining same Relationship with Extra Where Clause
Using Relation:
public function Activereviews()
{
return $this->belongsToMany(Review::class, 'review_user')
->whereStatus(true)
->withTimestamps();
}
OR Using Scope:
public function scopeActivereviews()
{
return $this->reviews()
->whereStatus(true);
}
Now you can do:
Auth::user()->ActiveReviews()->get();
Hope it helps.
Thanks
Try this code like that, does works
$reviews = auth()->user()->whereHas('reviews', function ($query) {
return $query->where('status', '=', true);
})->get();
And you check the official documentation
like option use documentation
Like all other relationship types, you may call the roles method to continue chaining query constraints onto the relationship:
$roles = App\User::find(1)->roles()->orderBy('name')->get();

BadMethodCallException Call to undefined method on model method that references itself using many to many relations

I'm building a system where a user can have many teachers (Same User model) and teachers can have many users. When I hit the controller I get:
BadMethodCallException with message 'Call to undefined method App/User::teachers()
Code
Controller
public function requestTeacher()
{
$teacher = User::where(['hashed_id' => 'auserhash123', 'is_teacher' => 1])->first();
$user = auth()->user()->first();
$teacher->notify(new teacherRequest($user));
$user->teachers()->attach($teacher->id);
}
User Model
public function teachers()
{
return $this->belongsToMany('App\User','teacher_approval','user_id','teacher_id');
}
public function clients()
{
return $this->belongsToMany('App\User','teacher_approval','teacher_id','user_id');
}
teacher_approval table
Schema::create('teacher_approval', function (Blueprint $table) {
$table->unsignedInteger('user_id');
$table->unsignedInteger('teacher_id');
$table->unsignedInteger('status')->default(0);
$table->timestamp('decission_date')->nullable();
$table->foreign('user_id')->references('id')->on('users')
->onDelete('cascade');
$table->foreign('teacher_id')->references('id')->on('users')
->onDelete('cascade');
});
Edit:
I wonder if this is because the User is extending SparkUser (I'm using laravel Spark) class User extends SparkUser { }

Laravel eloquent attach auto generate random ID

I have an eloquent many to many relationship and I want to use attach() to easily create role_permissions data but the problem is I'm using an UUID for my ID and it throws an error Field 'id' doesn't have a default value. Any way of hijacking the attach() method? so I can set my UUID?
My migration
Schema::create('role_permissions', function (Blueprint $table) {
$table->increments('count')->unique();
$table->string('id')->unique();
$table->string('role_id');
$table->string('permission_id');
$table->timestamps();
});
My model
class Role extends Model
{
//
public $incrementing = false;
public function users()
{
return $this->belongsToMany('App\User', 'user_roles', 'role_id', 'user_id');
}
public function permissions()
{
return $this->belongsToMany('App\Permission', 'role_permissions', 'role_id', 'permission_id');
}
}
My attach code
$role->permissions()->attach($permission_ids);
I know the problem here is that my id is not an incrementing number it's an unique string. My question is how do I "Inject" that unique string to the attach() method? Thank you guys.
The error
Field 'id' doesn't have a default value
refers to the fact that your database does not know how to fill the id field when it's not specified.
Either you edit the schema adding a nullable:
Schema::create('role_permissions', function (Blueprint $table) {
$table->increments('count')->unique();
$table->string('id')->unique()->nullable(); // Bad idea
$table->string('role_id');
$table->string('permission_id');
$table->timestamps();
});
or injecting it via attach:
$role->permissions()->attach($permission_ids, ["id" => null]);
More info on Laravel official doc
Update
For the future developers who encounter this problem you can also set anything inside the attach array, for example:
$role->permissions()->attach($permission_ids, ["id" => Uuid::generate()]);
Update 2
There's also a more clean way to handle this to be honest. I will try to explain it.
You can handle the Pivot events inside the event service provider by simply hooking into the bootmethod:
Here's a snippet
/App/Providers/EventServiceProvider.php
public function boot()
{
Pivot::creating(function($pivot) {
if ($pivot->getTable() == 'role_permissions') {
$pivot->id = Uuid::generate();
}
});
}
Be aware I do not know if this is actually possible on your laravel version. Mine (5.4.*) works as intended
Okay managed to fixed it, what I did with the help of #Claudio Ludovico Panneta's tip.
foreach($permission_ids as $permission_id)
{
$role->permissions()->attach($permission_id, ["id" => Uuid::generate()]);
}

How to fix error Base table or view not found: 1146 Table laravel relationship table?

I am a new of laravel I try to create relationship many to many between table,My problem when I am insert data in to database I got errors
QueryException in Connection.php line 713: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'learn.category_posts' doesn't exist (SQL: insert into category_posts (category_id, posts_id) values (4, ))
can anyone help me pls . and here below is my migrate and code:
2016_08_04_131009_create_table_posts.php
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->text('title');
$table->text('body');
$table->timestamps();
});
}
2016_08_04_131053_create_table_categories.php
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
2016_08_04_131413_create_table_category_posts.php
public function up()
{
Schema::create('category_post', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->integer('post_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories')->onUpdate('cascade')->onDelete('cascade');
$table->foreign('post_id')->references('id')->on('posts')->onUpdate('cascade')->onDelete('cascade');
$table->timestamps();
});
}
and my model Posts.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Posts extends Model
{
protected $table = 'posts';
public function categories()
{
return $this->belongsToMany('App\Category');
}
}
Category.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $table = 'categories';
public function posts()
{
return $this->belongsToMany('App\Posts');
}
}
My PostsController.php
public function create()
{
$categories = Category::all();
return view('create',compact('categories'));
}
public function store(Request $request)
{
$post = new Posts;
$post->title = $request->title;
$post->body = $request->body;
$post->categories()->attach($request->categories_id);
return redirect()->route('posts.index');
}
My View create.blade.php
{!!Form::open(array('route' => 'store', 'method' => 'POST'))!!}
{{Form::text('title')}}<br>
{{Form::textarea('body')}}<br>
<select name="categories_id" multiple>
#foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->name }}</option>
#endforeach
</select>
<br>
{{Form::submit('submit')}}
{!!Form::close()!!}
It seems Laravel is trying to use category_posts table (because of many-to-many relationship). But you don't have this table, because you've created category_post table. Change name of the table to category_posts.
Laravel tries to guess the name of the table, you have to specify it directly so that it does not give you that error..
Try this:
class NameModel extends Model {
public $table = 'name_exact_of_the_table';
I hope that helps!
Schema::table is to modify an existing table, use Schema::create to create new.
The main problem for causing your table unable to migrate, is that you have running query on your "AppServiceProvider.php" try to check your serviceprovider and disable code for the meantime, and run php artisan migrate
You can add this in Post Model,
public function categories()
{
return $this->belongsToMany('App\Category','category_post','post_id','category_id');
}
category_post indicate the table you want to use.
post_id indicate the column where you want to store the posts id.
category_id indicate the column where you want to store the categories id.
For solving your Base Table or view not found error you can do As #Alexey Mezenin said that change table name category_post to category_posts,
but if you don't want to change the name like in my case i am using inventory table so i don't want to suffix it by s so i will provide table name in model as protected $table = 'Table_name_as_you_want' and then there is no need to change table name:
Change your Model of the module in which you are getting error for example:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Inventory extends Model
{
protected $table = 'inventory';
protected $fillable = [
'supply', 'order',
];
}
you have to provide table name in model then it will not give error.
The simplest thing to do is, change the default table name assigned for the model. Simply put following code,
protected $table = 'category_posts'; instead of protected $table = 'posts'; then it'll do the trick.
However, if you refer Laravel documentation you'll find the answer. Here what it says,
By convention, the "snake case", plural name of the class(model) will be used as the table name unless another name is explicitly specified
Better to you use artisan command to make model and the migration file at the same time, use the following command,
php artisan make:model Test --migration
This will create a model class and a migration class in your Laravel project. Let's say it created following files,
Test.php
2018_06_22_142912_create_tests_table.php
If you look at the code in those two files you'll see,
2018_06_22_142912_create_tests_table.php files' up function,
public function up()
{
Schema::create('tests', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
Here it automatically generated code with the table name of 'tests' which is the plural name of that class which is in Test.php file.
You should change/add in your PostController: (and change PostsController to PostController)
public function create()
{
$categories = Category::all();
return view('create',compact('categories'));
}
public function store(Request $request)
{
$post = new Posts;
$post->title = $request->get('title'); // CHANGE THIS
$post->body = $request->get('body'); // CHANGE THIS
$post->save(); // ADD THIS
$post->categories()->attach($request->get('categories_id')); // CHANGE THIS
return redirect()->route('posts.index'); // PS ON THIS ONE
}
PS: using route() means you have named your route as such
Route::get('example', 'ExampleController#getExample')->name('getExample');
UPDATE
The comments above are also right, change your 'Posts' Model to 'Post'
try checking in the app if you are using the tables before it's created
such as appServiceProvider.php
you might be calling the table without being created it, if you are, comment it then run php artisan migrate.
This problem occur due to wrong spell or undefined database name. Make sure your database name, table name and all column name is same as from phpmyadmin
If you're facing this error but your issue is different and you're tired of searching for a long time then this might help you.
If you have changed your database and updated .env file and still facing same issue then you should check C:\xampp\htdocs{your-project-name}\bootstrap\cache\config.php file and replace or remove the old database name and other changed items.
Just run the command:
php artisan migrate:refresh --seed
If you are getting the error "SQLSTATE[42S02]: Base table or view not found" and you have no problem in your code, check if you have wrote the first letter of the table in capital letter.
Inspections >> inspections
capital case and small case letters matters on AWS environments.

Resources