I'm pretty new to Laravel and am struggeling with the following problem at the moment:
I want to create a User-Group-Management. So I want to add Users to a Group and add Rights to these Groups, to have an authorization. My aim is to have a User::hasRight('exampleRight') function, which i can easily call. For that I wanted to have a function chaining inside of the User-Class. I have this function to create a Connection to the UserGroup-Table (which connects an User-ID to a Group-ID):
public function role()
{
return $this->hasOne('UserGroup');
}
The next function shall return an Array of rights. My plan was to write something like
public function rights()
{
$rights = Groups::find($this->role()->groups_id)->rights;
return $rights;
}
The GroupsModel of course has this function to get the Rights:
public function rights()
{
return $this->hasMany('Rights');
}
But obviously $this->role()->groups_id doesn't give me the groups_id but instead throws the Error Undefined property: Illuminate\Database\Eloquent\Relations\HasOne::$groups_id. When i leave the ->groups_id out, and do add it instead in the controller like:
(Attention Controller, no model!)
public function getUserRole()
{
return User::find(10)->rights->groups_id;
}
it gives me the right answer. Can somebody tell me, whats the error? I don't really get whats wrong...
Thanks you in advance!
Regards
In this particular method you need to remove the parenthesis:
public function rights()
{
$rights = Groups::find($this->role->groups_id)->rights;
return $rights;
}
When you do role() you're actually telling Laravel to return the relation object, so you can do things like
$this->role()->where('x', '=', 'y')->get();
Why do you have an intermediary table between User and Group? Simply give the User a group_id. Here is how I have built this in the past.
User belongsTo Group
Group belongsToMany Right
// Inside User Model
public function can($name)
{
foreach ($this->group->rights as $right)
{
if ($right->name == $name)
{
return true;
}
}
return false;
}
// Elsewhere
if (!$user->can('edit_blogs'))
{
return 'FORBIDDEN';
}
Look into eager loading to mitigate performance issues.
Look into filters to apply these access restrictions at the controller or route level.
Related
I have an app where I create entries based on who is signed in. If I use the find($id) method it returns json response. The function is like this:
public function edit($id)
{
$chore = Chore::find($id);
return response()->json($chore);
}
Now if I where to edit the id value I might be able to access other user's data which isn't secure at all.
So I added and extra column user_id that checks who is signed in:
public function edit($id)
{
$chore = Chore::find($id)
->where('user_id', Auth::id());
return response()->json($chore);
}
But of course laravel can't make it easy so it doesn't work. Adding ->get() returns an array instead of a json response.
First of all how is find($id) ever secure in any app that uses authentication and secondly how do I add another condition under the find($id) clause? I need data returned in JSON otherwise I will need to rewrite all my front-end which isn't ideal at this point.
I also tried:
public function edit($id)
{
$chore = Chore::where('id', $id)
->where('user_id', Auth::id());
return response()->json($chore);
}
but no luck
You just need to call the where method before the find method
$chore = Chore::where('user_id', Auth::id())
->find($id);
As an alternative, If you've set up relationships properly on user model
// In User.php
public function chores()
{
return $this->hasMany(Chore::class, 'user_id', 'id');
}
then you could also do
$chore = Auth::user()->chores()->find($id);
While this seems like extra work, it's more convenient and easier to maintain.
If Chore is in a one-to-one relationship with your User model, then you can create a relationship in your User.php model.
public function chore() {
return $this->hasOne(Chore::class);
}
Then in your controller, you could simply call auth()->user()->chore.
The Eloquent's find() method finds only one record by the primary key. If you use additional validation it's perfectly safe. You could use route model binding to simplify the code.
web.php
Route::get('/chore/{chore}/', 'ChoreController#edit');
Then in your controller
public function edit(Chore $chore)
{
if (! $chore->user_id === auth()->id()) {
// throw error or redirect, or whetever
}
return response()->json($chore);
}
To simplify the controller a little bit more, you could create a form request and inject it into controller's method as a regular request. (https://laravel.com/docs/7.x/validation#authorizing-form-requests)
Then you could move the validation into your form request. It should look something like this:
public function authorize() {
return $this->route('chore')->user_id === $this->user()->id
}
6 projects
and there is user model and this is a method in user model
public function setPasswordAttribute($password)
{
$this->attributes['password'] = bcrypt($password);
}
Everything is working fine when I am using get() in laravel like this
User::get();
It returns all users.
My question is, is there a way to write method in the model that can return users with conditions, not all users,
like this
public function setRoleToGetData()
{
$user = Auth::user()->getSex // getSex is method in the model
if($user == 1)
return users in the whole program like this
User::where('user_sex','=',1)->get();
else
// return the reverse
}
i dont want to write where('user_sex','=',1) in every time i want to get users
thanks
You are probably looking for scopes.
https://laravel.com/docs/5.6/eloquent#local-scopes
From the docs:
Local scopes allow you to define common sets of constraints that you may easily re-use throughout your application
Example:
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
Then use as
SomeModel::popular()->get();
Trying to load different views and methods depending on which view the user is browsing.
Views:
public function edit()
{
if("SOMETHING")return View::make('store_edit');
If("SOMETHING")return View::make('product_edit');
}
Methods:
public function destroy($id)
{
if(SOMETHING){
$store = Store::find($id);
$store->delete();
return redirect('/store');
}
if(SOMETHING){
$product = Product::find($id);
$product->delete();
return redirect('/product');
}
}
What can be used in the if() statements depending on which view is browsed in order to delete the right item and not having to rewrite the functions for each table.
There isn't a simple way to get information about which view was displayed in a previous request, and that's probably not what you want. You should create separate controllers/routes for both "products" and "store". Then you can do away with that view logic altogether.
To somewhat answer your question, you can access information about the current route with the Route facade.
$route = Route::current();
$name = Route::currentRouteName();
$action = Route::currentRouteAction();
Read Laravel routing:
https://laravel.com/docs/5.6/routing#route-parameters
Route
Route::get('something/{param}', 'SomeController#edit');
Controller
...
public function edit($param) {
if ($param === $expectedParam) {...} else {...}
}
Also you can make $param optional:
Route
Route::get('something/{param?}', 'SomeController#edit');
Controller (don't forget to give a default value)
...
public function edit($param = null) {
if ($param === $expectedParam) {...} else {...}
}
Please do not use same controller for diferent models.
if has diferent view and diferent functions this HAVE to has diferent controllers
Again, Laravel is a beautiful framework, dont do this!
I am trying to find a row with condition and that is...
A user has many profile pictures but there is one picture that is is_main
So this is what I wrote
public function profile_picture()
{
return $this->hasMany('App\User_profile_picture');
}
public function active_picture()
{
return $this->profile_picture()->find($this->is_main);
}
Now when I access it through
$picture = Auth::user()->active_picture;
It says
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
What is that I have to do to make it work?
Your code should be
public function profile_picture()
{
return $this->hasMany('App\User_profile_picture');
}
You are missing the return statement
If you want to use a Model method as a property, it has to return a relationship. Otherwise you need to call it as a method with the () operator. Like explained here.
So the solution to your question would be:
$picture = Auth::user()->active_picture();
edit: TIL you can also set a custom eloquent accessor:
public function getActivePictureAttribute()
{
return $this->profile_picture()->find($this->is_main);
}
$picture = Auth::user()->active_picture;
Yeah, you have to write the get...Attribute in camelCase, and can then use the attribute in snake_case/kebab-case or camelCase. (See the eloquent $snakeAttributes boolean variable.)
I think you can try this:
public function profile_picture()
{
return $this->hasMany('App\User_profile_picture');
}
public function active_picture()
{
return $this->profile_picture()->find($this->is_main);
}
Hope this work for you !!!
You must use class:
public function profile_picture()
{
return $this->hasMany(App\User_profile_picture::class);
}
I have a pivot table of users_operators.
I want to grab the operator_id of the user.
This is how i do this now, but its seems like verbose way.
if (Auth::user()->type === 'operator') {
$user = Auth::user();
// There is a better way to do this?
$operator_id = $user->operator[0]['pivot']['operator_id'];
Session::put('operatorId', $operator_id);
}
class Operator extends \Eloquent
{
public function users() {
return $this->belongsToMany('User');
}
}
class User extends \Eloquent
{
public function operator() {
return $this->belongsToMany('Operator');
}
}
I'm, battling insomnia and not functioning at 100%, but you should be able to get away with $user->operator->id based on what I'm interpreting your models to be (it looks like you had a typo when you copied them into the question).
If that doesn't work, you might want to check out the "Dynamic Properties" section in the Eloquent docs for more info, if you haven't already.