I just started learning Laravel 5 and came across a problem i could't solve:
How to tell that a certain user can only see certain things.
For example if i view a profile which is not my own, the "Edit Profile" button should not be visible. But if i look at my own profile this button should be visible.
What i already got is to authorize certain requests. For example to authorize a user to actually update a profile:
public function updateProfile(Profile $profile, UpdateProfile $request){
//update the given profile
}
So UpdateProfile is here a Request Class which has a authorize() and a rule() method and in the authorize() Method i check if the logged User is updating his own profile.
So i thought maybe i can use the authorize() method on its own, but i am not really sure how.
Now of course i could always check sth like:
if($user -> userID == Auth::user() -> userID)
But what if i need to check sth more complex, for example when i write a post and want to show a delete button for that post i want to check:
Is the user admin, if not is the user writer of that post, if any of this is true, show delete button.
So my question would be, where would i check sth like this in laravel 5?
You could write a userCanEdit method on your Post class. Something like this:
function userCanEdit(User $user)
{
return $user->isAdmin() || $this->user_id == $user->id;
}
And then just call it on your view:
#if ($post->userCanEdit(Auth::user()))
Edit
#endif
The advantage of this is that you keep your view clean and centralize the business logic in a single, reusable, method. If the definitions for a user who can edit a post ever change, that is the only place you'll have to worry about.
So my question would be, where would i check sth like this in laravel 5?
In your views. For example, let's assume that you are loading a blog content like this:
// controller
public function showPost(Post $post){
return view('views.post', ['post' => $post]);
}
In the view, we want that only the author make changes to it.
// post view
<h2>{{ $post->getTitle() }}</h2>
#if ($post->getAuthor()->getId() === Auth::user()->getId())
Edit
#endif
As you can see above, if the author is the same user as the authenticated, then she/he can see an editing link for this post.
Related
I have simple URL /players/USERNAME to view user profile. I want to insert some thing only in user profile page. Cuz user profile got another links.
Example:
#if (Request::is('players/{{ $user->slug }}'))
#include('users::stats')
#endif
But dont get anything. Any ideas what's wrong? Thanks!
I don't think Request::is is necessary in your blade view. You should have a route defined in your web.php file to capture that pattern meaning you already know the end user is on players/{username}.
web.php
Route::get('/players/{slug}', [
PlayerController::class, 'show'
])->name('players.show');
PlayerController.php
public function show($slug)
{
$user = User::where('username', $slug')->firstOrFail();
return view('players.show', compact('user'));
}
Blade view (players/show.blade.php)
// some stuff
#include('users.stats')
// some other stuff
Note that I've named the methods and files based on what you've provided, you'll need to change them to be whatever they are in your project.
I've User model which has HasMany relation with Post model. When I include a field for HasMany in User resource of Nova, I see there is Create post button. How do I remove/hide that button?
You could achieve this with Policies.
According to the documentation:
If a policy exists but is missing a method for a particular action, the user will not be allowed to perform that action. So, if you have defined a policy, don't forget to define all of its relevant authorization methods.
So in your case, if you want to hide the button completely, just create a policy for your resource (PostPolicy) and don't implement the create method.
You need to 2 things here.
In your Post resource
public static function authorizable()
{
return true;
}
Now create policy for Post and return true for all methods except create, for create return false and in AuthServiceProvider.php
put
protected $policies = [
Post::class => PostPolicy::class,
];
And you are done.
this question is answered in laravel nova official documentation
in my case i have user model and order model, user Hasmany order i added
public function addOrder()
{
return false;
}
on user policy now create role button is gone on user detail page
this is a screenshot of user detail page
If you're like me, the last thing you want to do is set a policy blocking creation of the sub-resource referenced by the HasMany rule by setting a policy. The reason is that setting this addX() policy to false on the "Has" side of the HasMany not only blocks the creation of the sub-resource from the resource detail view, it also produces permission errors when creating the sub-resource from its page view, specifically that creation of the resource with references to the "parent" or "Has" is forbidden by the policy. Which when you think about how broad the permission statement of addClassName() is, isn't actually surprising.
Thus my solution ended up having to be butt ugly CSS. Just why is this the only way to do page dependant hiding of the create button. This should be a HasMany::make("")->canCreate(false) declaration in the Nova/*.php view file.
Anyway here's the CSS, hopefully, it helps someone.
div[dusk="parent-class-detail-component"] div[dusk="has-many-child-class-index-component"] a[dusk='create-button'] {
display: none;
}
In case someone is still looking for the solution, you can authorise attaching/detaching resources in your policies:
https://nova.laravel.com/docs/2.0/resources/authorization.html#authorizing-attaching-detaching
So in this case, you have a UserPolicy to which you add a function:
attachPost(User $user, User $model, Post $post)
{
return false;
}
The $user variable is the user that is signed in, the $model variable is the user page that is viewed.
I'm trying to make a "Profile settings" section in an application.
The thing is, I learned how to do this the "Admin" way, the route would be /users/{user}/edit, the would call the edit method on the controller and it would return the edit view. There I would have a form which the user would patch to the route users/{user} and it would call the update method on the controller.
But I don't want anyone editing other users, so I'd like to know if there's a way to limit this route to the current user only.
Thanks in advance.
Since version 5.1 Laravel has Policies which are exactly what you need.
You can create a new policy by typing in command:
php artisan make:policy UserPolicy
In your UserPolicy class you can include the following method:
public function updateProfile(User $user, User $updatedUser) {
return $user->id === $updatedUser->id;
}
Please note: The first parameter $user is resolved automatically behind the scenes and is the currently logged in user. When checking the policy through the Gate facade in your application you need to pass only the second parameter $updatedUser.
Then you need to register your policy in the AuthServiceProvider:
use Acme\User;
use Acme\Policies\UserPolicy;
...
class AuthServiceProvider extends ServiceProvider {
protected $policies = [
User::class => UserPolicy::class
]
Now when you have your policy registered you can check it across your app using the Gate facade like so:
if(Gate::allows('updateProfile', $user)) {
// Your logic goes here
}
Or the other approach with I like more using the denies method and include it at the beginning of my controller methods and return http error:
public function edit($id) {
if(Gate::denies('updateProfile', $user)) {
abort(403, 'You do not have permissions to access this page!');
}
// The check is passed and your can include your logic
}
You can also check for permissions in your blade files using can and cannot like so:
#can('updateProfile', $user)
// Show something only to the user that can edit the $user's profile
#endcan
For more info check the docs.
you should not need to pass in the user id as there user is already logged in and there for should be able to edit themselves, thus only targetting the logged in user.
So you can use the routes /user/editand /user/updateetc
Just get the current user details like
Auth::user()->id
or something else like
$user = Auth::user();
Thus only the logged in user (themselves) can be edited.
In the view there should be a button or link, on click pass the ID to the desired route that's it.
Example:
For Grabbing the current logged in User id you should do like
$user = Auth::user()->id;
And directly in the route you can get it like
Edit
Now when someone clicks on the Edit Button/Link, the route will look like route/currentuserid.
I'm having my first interaction with the core laravel code so I want to be careful not to break anything.
In my project, my users also correspond to person records (via user->person_id), so I have a get_person_from_user() function that takes the \Auth::user() (conveniently accessible anywhere) and returns the person object, so I can grab the person record for the authenticated user from any controller and pass it to a view.
The problem: there's a piece of data from the person record that I'd like to include in a nav partial in my default blade view (which gets extended by a bunch of different views), so it's the one case where I'm not going through a controller first. I'm unclear on how I can make the logged in user's person record available here. Any suggestions?
I think I need to add some step after the user logs in, to save the person record (globally? in the session?) so it's generally accessible. The login stuff happens in AuthenticatesUsers.php, and reading around it sounds like I'll want to add an override of postLogin to my AuthController.
But I tried copying that function from AuthenticatesUsers.php into my AuthController (not adding anything else to it yet), and AuthController gives me a new error when I try to log in:
ReflectionException in RouteDependencyResolverTrait.php line 81:
Class App\Http\Controllers\Auth\Request does not exist
Any advice on a good way to go about accessing the person object for the authenticated user, when I don't have a controller to pass it along?
You can setup the correct relationship on the User model to Person model.
public function person()
{
return $this->belongsTo(Person::class);
}
Then you can do:
Auth::user()->person;
For having a variable available to a particular view you can use a View Composer. (You can create and register a Service Provider and add this to the register method.) Potentially something like this:
view()->composer('someview', function ($view) {
if ($user = Auth::user()) {
$somevar = $user->person->somevar;
} else {
$somevar = null; // or some default
}
$view->with('somevar', $somevar);
});
If this view is also used in a scenario where someone doesn't have to be authed you will want to check if Auth::user() is null before trying to use the relationship.
Laravel Docs - Views - View Composers
I suggest you to use Eloquent relation
User.php
public function person()
{
return $this->belongsTo('NAMESPACE_TO_YOUR_MODEL\Person'); //also you can specify FK, more info in docs
}
then you can access Auth facade in your view
Auth::user()->person
I am having a small issue. There are two user roles and one is a normal member and one is an admin. The member can delete a blog and they will not be able to see the blog after they delete (soft delete) it while the admin can still see the blog, even if it's soft deleted.
Example code:
// Route file
Route::get('/blog/{blog}', 'BlogController#show');
// BlogController
public function show(App\Blog $blog) {
// It never gets to here if the blog has been soft deleted...
// Automatically throws an 404 exception
}
I want the admin to be able to visit the blog even if it's soft deleted but it doesn't really work. I am trying to edit the route service provider but I haven't gotten any luck as it doesn't let me use the Auth::user() function to get the logged in user so I can check if they have permission.
My RouteServiceProvider
$router->bind('post', function($post) {
if (Auth::user()->isAdmin()
return Post::withTrashed()->where('id', $post)->firstOrFail();
});
This does not work as it doesn't know what Auth::user() is. I have imported Auth facade but still doesn't work.
Edit: It gives me a null value when I dump and die Auth::user().
Any help is highly appreciated.
As this question pop ups when googling for this, in newer Laravel versions you can do this:
Route::get('posts/{post}', [PostController::class, 'show'])->withTrashed();
See: Implicit Soft Deleted Models
I just found out that getting the current logged in user is not possible in Route Service Provider because it loads before all session service provider.
Instead I simply did:
//Route Service Provider
$router->bind('post', function($post)
return Post::withTrashed()->where('id', $post)->firstOrFail();
});
// Controller
public function show(Post $post) {
// If the post has been trashed and the user is not admin, he cannot see it
if (!Auth::user()->isAdmin() && $post->trashed())
abort(404);
// Proceed with normal request because the post has not been deleted.
}