Same check for all policy functions - laravel

Almost for all models, the policy functions look like this example:
public function index(User $user)
{
return $user->hasPermission('view-post');
}
The hasPermission exists in the User model:
public function hasPermission($permission){
$roleId = Auth()->user()->role_id;
$id = $this->select('p.id')->join('role_permissions AS rp', 'rp.role_id', 'users.role_id')->join('permissions AS p', 'p.id', 'rp.permission_id')->where('rp.role_id', roleId)->where('permission', $permission)->get()->toArray();
return $id ? true : false;
}
Then in postController:
public function index(User $user){
if ($user->cannot('index', Post::class)) {
return redirect('/');
}
}
Is there a way to avoid repeating the policy example at the top for every model and every policy function?
I want to be able to use the method in both controller and view to check against permissions

Related

How would I edit the Laravel Nova indexQuery for a Resource to only show records through multiple relationships?

firstly thank you in advance.
I have the following Models , User , Location, Listing, Offer.
The relationships are:
User Model:
public function location()
{
return $this->hasOne(Location::class);
}
Location Model:
public function listings()
{
return $this->hasMany(Listing::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
Listing Model:
public function offers()
{
return $this->hasMany(Offer::class);
}
public function location()
{
return $this->belongsTo(Location::class);
}
Offer Model:
public function listing()
{
return $this->belongsTo(Listing::class);
}
In my Offer Resourse I would like to show only the offers that belongTo the listings that in turn belongsTo the location that in turn belongTo the authenticated user. I have tried the following.
public static function indexQuery(NovaRequest $request, $query)
{
$user = Auth::user();
if ($user->is_admin === 1){
return $query;
}elseif($user->is_admin === 0) {
return $user->location->listings->offers;
}
}
But get an error Property [offers] does not exist on this collection instance. Any assistance will be greatly appreciated.
I would try something like this.
More information here on querying relationship existence:
https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence
public static function indexQuery(NovaRequest $request, $query) {
$user = Auth::user();
if (!$user->is_admin) {
$query = $query->whereHas('listings.location.user', function ($builder) use ($user) {
return $builder->where('id', $user->id);
});
}
return $query;
}
The reason you are getting the "Property does not exist" error is due to you not pulling it from database.
Two alternative solutions include
Querying them separately: ...->listings()->offers()
Eager loading: ->with(['location.listings.offers']);

Trying to get property 'designation_name' of non-object in Laravel 8

I want to show the user role from my user role table, but I can't.
User.php
public function role()
{
return $this->belongsTo('App\Models\Designation');
}
UserController
public function index()
{
$user = User::all();
return view('dashboard2', compact('user'));
}
View
<h1>{{ Auth::user()->role->designation_name}}</h1>
You actually should have shared more details about the relationships. But I will give an answer based on an assumption. It seems like there is a one-to-one relationship betwen User and Role as well as Role and Designation. So you want to reach designation from user. Based on that:
//User.php
public function role()
{
return $this->hasOne(Role::class);
}
//Role.php
public function user()
{
return $this->belongsTo(User::class);
}
public function designation()
{
return $this->hasOne(Designation::class);
}
//Designation.php
public function role()
{
return $this->belongsTo(Role::class);
}
// Controller
public function index()
{
// If you need only the auth user's data, you don't
// need adding relationships into the query.
$users = User::with('role.desgination')->all();
return view('dashboard2', compact('users'));
}
// View
// For auth user:
// I haven't used this way before,
// but technically it should work.
auth()->role()->designation()->name;
// For users collection:
// Make sure you added the query to the controller.
#foreach($users as $user)
$user->role->desgination->name
#endforeach

How to call a function in one controller to the other controller in laravel

I want to call a function in another controller. when i call this gives me an error.
Call to undefined method Illuminate\Database\Query\Builder::defaultBuckets()
I dont know why it gives me this error. I don't know i am calling this function rightly in another controller. Here is my code. Please Help.
Here is my function i created in my BucketController:
public function defaultBuckets()
{
$buckets = Bucket::where('bucket_type', 'default')->get();
}
And here is my Profile controller function Where i call this function:
public function show(User $user)
{
$authUser = JWTAuth::parseToken()->toUser();
if (! $user->isBlocking($authUser) && ! $user->isBlockedBy($authUser)) {
if($authUser->id == $user->id){
$profile = $user->where('id', $user->id)->defaultBuckets()->with([
'posts', 'likes', 'followers', 'following'])->first();
} else{
$profile = $user->where('id', $user->id)->with([
'posts' => function ($query) {
$query->where('post_type', 'public');
},
'buckets' => function ($query) {
$query->where('bucket_type', 'public');
},
'likes' => function ($query) {
$query->where('post_type', 'public');
},
'followers', 'following'])->first();
}
return response()->json(['profile'=> $profile], 200);
}
return response()->json(['message'=> 'Your are not able to open profile of this user'], 200);
}
I Think there is mistake. You said you have this function in your BucketController
public function defaultBuckets()
{
$buckets = Bucket::where('bucket_type', 'default')->get();
}
and then you are firing the function from user model in your ProfileController
$profile = $user->where('id', $user->id)->defaultBuckets()->with([
'posts', 'likes', 'followers', 'following'])->first();
That is the reason it says that there is no function named "defaultBuckets".
You have to put this function in your User model and everything will work fine.
Also don't forget to return the buckets as well like this:
To return all buckets
public function defaultBuckets()
{
$buckets = Bucket::where('bucket_type', 'default')->get();
return $buckets; // all buckets
}
To return a user's buckets only
public function defaultBuckets()
{
return $this->hasMany(Bucket::class)->where('bucket_type', 'default');
}
Make sure to accept the relationship from user in bucket model like this:
public function user(){
return $this->hasOne(User::class, 'bucket_id' , 'user_id');
}
You can replace column names (bucket_id,user_id) according to your database.
Let me know if this fixes your problem

Laravel Nova metrics filtering

I have a model called Property which has an 'active' flag. I want a metric at the top of my resource which shows a count of active Properties.
My calculate method is exactly as in the doc but this shows all Properties rather than active ones:
public function calculate(Request $request)
{
return $this->count($request, Property::class);
}
How can I add a filter?
I've tried a where clause:
public function calculate(Request $request)
{
return $this->count($request, Property::class)->where('active','=',1);
}
And a query scope:
public function calculate(Request $request)
{
return $this->count($request, Property::class)->active();
}
I thought I might be able to use the Nova filter I set up on the resource list page but that didn't seem to work either. I'm sure it's really easy but I haven't worked it out. Thanks for your help!
Your can use every type of Eloquent\Builder instance in the $model param.
Instead of:
public function calculate(Request $request)
{
return $this->count($request, Property::class);
}
Set a Scope on your Model
App\Property.php
...
public function scopeActive($query)
{
return $query->where('active', 1);
}
public function scopeInactive($query)
{
return $query->where('active', 0);
}
And use this scope as the $model param in your calculate method, because the call of the scope returns a Eloquent\Builder Instance
public function calculate(Request $request)
{
return $this->count($request, Property::active());
// return $this->count($request, Property::inactive());
}
Edit
Of course you can make the Eloquent Builder call inline:
public function calculate(Request $request)
{
return $this->count($request, Property::where('active', 1));
}

Is it better to query a database from the controller or with a model function in laravel?

Currently I am using the following code in my controller to query the database. This code checks whether a user has set their username yet.
$user = User::where('email', $userdata['email'])->first();
if(empty($user->username)){
echo 'Set username here...';
} else {
echo 'My home page!';
}
My question is, is it better to make a function in the User model to do this, or keep it as it is. So for example, the first line would be removed and in the if statement it would call the model function which would give true or false.
My initial thought is this should be moved to a model function as MVC structured projects should have 'fat models' and 'skinny controllers'. This is 'business logic' so should be in the model. If so, could you give an example on how I would move this to a model and call the function from the controller.
You should definitely move all data related code to the model. You've asked for an example. I'd create this method in a model:
public function findByEmail($email)
{
return $this->where('email', $email)->first();
}
In the controller:
use App\User;
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function showSomething($userdata)
{
return view('some.view', [
'user' => $this->user->findByEmail($userdata['email'])
]);
}
In a view:
{{ empty($user->username) ? 'Hello anonymous' : $user->username }}
In this example, it looks like moving query in the model is not very good idea, but when your application will grow you'll see this is the only good way to work with data. It's MVC. Also, you should keep validation logic in the Request classes, business logic in their own classes etc.
You could write in your User.php model a function like :
public function hasUsername()
{
if($this->username)
{
return true;
}
return false;
}
and in your controller you could say:
$user = User::where('email', $userdata['email'])->first();
if($user->hasUsername())
{
// do somethin
}
Model
public static function checkIfUsernameExists($email) {
$user = User::where('email', $email)->first();
if (empty($user->username)) {
return true;
} else {
return false;
}
}
Controller
if(User::checkIfUsernameExists($userdata['email'])){
echo 'Set username heree...';
} else {
echo 'My home page!';
}

Resources