Laravel Gates and Policies - php

The following Gate definitions are working fine:
Gate::define('create_role','App\Policies\RolePolicy#create_role');
Gate::define('view_role','App\Policies\RolePolicy#view_role');
Gate::define('edit_role','App\Policies\RolePolicy#edit_role');
Gate::define('delete_role','App\Policies\RolePolicy#delete_role');
but when I changed it to,
Gate::resource('roles', 'App\Policies\RolePolicy', [
'create_role' => 'create_role',
'view_role' => 'view_role',
'edit_role' => 'edit_role',
'delete_role' => 'delete_role',
]);
for reducing LOC and optimizing code. It is not working.
Following are the lines of code in my blade file,
#can('create_role')
Add Role
#endcan
#can('view_role')
View Roles
#endcan
Similarly for edit and delete.
The behavior observed is as such that it is only receiving false from the policy methods. I even tried out sending only true from it, but makes no difference.

Are you trying to create extra gates?. Given you passed that extra parameter in the array.
The array after gate definition is used to create extra gate definitions.
For more info go through the docs,
link.
When using resourceful policies.
eg:
Gate::resource('roles', 'App\Policies\RolePolicy');
#can('roles.view')
// html here
#endcan
#can('roles.create')
//html here
#endcan

Related

How to prevent users from checking not existing urls, how to hide routes in Laravel 8.*

First question was solved with findOrFail method
Is there any way to prevent users from checking non-existing routes?
Example
I've got route to http://127.0.0.1:8000/event/9
but event with id 8 does not exist, if user would go to that id there is a massage:
Attempt to read property "photo_patch" on null (View: C:\xampp\htdocs\Laravel1\resources\views\frontend\eventView.blade.php)
Or any other error from db that record does not exist.
Second question
How to turn on preety URLs in laravel
So my page with display http://127.0.0.1:8000 not http://127.0.0.1:8000/events something...
I know that its somewere in config files but I cant find it.
Example class and route that uses it:
-----------------------------Class----------------
public function eventView($id)
{
$notDisplay = Auth::user();
$eventView = Event::findOrFail($id);
if(!$notDisplay){
$eventView->displayed = $eventView->displayed +1;
$eventView->save();
}
return view('frontend/eventView', ['eventView' => $eventView]);
}
----------------Route-----------------
Route::get('event/' . '{id}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
First off, use the container!
Laravel's service container is very powerful and your controller resolve use-case is one of the most common places you should be using it. The url argument and controller argument MUST match for this to work.
Your route:
Route::get('event/' . '{event}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
Your Controller:
public function eventView(Event $event)
{
return view('frontend/eventView', ['event' => $event]);
}
When leveraging Laravel's dependency injection and container, you get your findOrFail() for free. You should also remove your auth check, and handle that with route middleware.
In terms of "prettifying" urls, Laravel's route model binding feature allows you to control what property of a model is used to for container resolution. For example, let's imagine your event has a unique slug you'd like to use instead of the auto-increment id:
Route::get('event/' . '{event:slug}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
Laravel's routing functionality offers a fallback feature that would allow you to fine-tune where the user is redirected if the route model binding failed.
https://laravel.com/docs/8.x/routing#fallback-routes
With regard to preventing an unauthorized individual from editing someone else's event. The first place I would put protections in place would be at the time of persistence (when saving to the database). While you can do this in every place in your codebase where persistence occurs, Laravel's Observer feature could be a great fit. That way, you can be confident that no matter what code is added to your app, the ownership check will always be run before making any changes to events.
https://laravel.com/docs/8.x/eloquent#observers
The second place that I would put protections in place would be with a route middleware on any routes that can mutate the event. That way, you can redirect the user away from an event they don't own before they even have a chance to attempt to edit it.
https://laravel.com/docs/8.x/middleware#assigning-middleware-to-routes

Laravel create controller method to update 1 column from 1 row

I am making a website for a dog shelter, and I want to make a button which would mark the dog as adopted. In other words, I am trying to update 1 column from 1 row. It will always be set from false to true.
I have tried making my own method in my existing controller, adding a route and creating a form, but I get errors that some other fields are also required, which are the same required fields when I would create a new dog (name, sex, DoB etc).
Code of the method in my controller:
public function markAsAdopted(Request $request, $id){
$pet = Pet::table('pets')->where('id', $id)->update(['is_adopted' => true]);
return redirect("/pets/$pet->id")->with('success', 'Successfully marked as adopted');
}
Code of the route:
Route::patch('/pets/{id}', 'PetsController#markAsAdopted');
Code of the form:
{!! Form::open(['action' => ['PetsController#markAsAdopted', $pet->id], 'method' => 'POST', 'class' => 'float-right ml-1 mr-1']) !!}
{{Form::hidden('_method', 'PATCH')}}
{{Form::submit('Mark as adopted', ['class' => 'btn btn-success'])}}
{!! Form::close() !!}
The form is on the page /pets/{id}
I have created all the other routes in this controller with the --resource flag when creating the controller, so the skeleton of CRUD was there.
Did I miss something? OR is there a better way of doing this?
Laravel is routing the markAsAdopted call to your update() (or possibly create() depending on how you have your Routes set) instead of the markAsAdopted() method. This is, in turn, calling your PetsRequest class (or whatever you've called your verification class).
It is most likely coming in as a POST and possibly ignoring the PATCH method, thus routed to update(). You can do a couple of things - move the patch method higher in Routes file and test by changing to POST. If this works, you know this is the problem. This is just a test - you will break the other pets POST coming in below it in the Routes file, so remember to change it back :)
I typically skip the PATCH thing altogether though, as I've run into similar issues. I normally just make a POST route with a special name (e.g. Route::post('/petsMarkAsAdopted/{id}', 'PetsController#markAsAdopted');) and open the form with the url to that special route.
If you really want to keep the PATCH, do the higher placement test above, and you can use blade to set the method after you open your form, like so:
#method('PATCH')
Edit: As noted in the comments, the above fixes the routing, but there is a different problem in your controller method now that we can access it. Take out the table(), and just call the model to get the pet your need. I've separated out the call for clarity:
$pet = Pet::where('id', $id)->first();
$pet->update(['is_adopted' => true]);

web middleware for specific routes in laravel 5.2

In Laravel 5.4 the web middleware is included on all routes by default. But I want to show those routes(menu's) assign to users. For example
I have some routes(menu's) as follows
user\list
user\add
user\show_form
list\forms
if user 'ABC' assign only two routes(menu's) like
`user\list`
`user\add`
so when user 'ABC' is logged that in menu shows only two routes(menu's) those assign. When user create that time I assign routes(menu's) and this stored in permission table. Now my question is how can I handle this using middleware.
Is this possible handle via middleware. please suggest me
Thanks in advance
Since you are pulling from the database, what I would do is skip the middleware idea all together and approach this by querying the user's routes and then displaying them on the page. I would accomplish this by using a service provider.
Let's assume you have your project set up with a user-navigation.blade.php file which contains your user's nav elements. Maybe something like:
<ul class="side-nav">
<li>
List
</li>
<li>
Add
</li>
</ul>
And you are bringing that file into your other blade template with #include('user-navigation').
What we want to do is do a query for the current user's routes ANY time that view (user-navigation) is displayed to the user. We can do this easily with a service provider.
Run the following command in your terminal: php artisan make:provider UserNavigationServiceProvider. Next, we need to tell Laravel to actually use this service provider. Open config/app.php and scroll down to the area where it says 'Application Service Providers' and add this: App\Providers\UserNavigationServiceProvider::class,. Now edit the file found in app\Providers\UserNavigationServiceProvider.php
If you're using Laravel's default Authentication, bring it in at the top of your file: use Auth;. We also need to bring in the model for your permissions table. So put use App\Permission; at the top of this file as well.
Now, in the boot() method, make it look like this:
public function boot()
{
$this->getUserNavigation();
}
Next, we're going to create the getUserNavigation() method. Just below the register() method, add this:
private function getUserNavigation()
{
view()->composer('user-navigation', function($view)
{
$userID = Auth::id();
$userNavigation = ! is_null($userID) ? Provider::where('user_id', $userID)->get() : null;
$view->with([ 'userNavigation' => $userNavigation ]);
});
}
So lets break down what we're doing in this new method. First, we're saying we want to target the view by the name of user-navigation. Any time this view is loaded we're going to perform the logic in this closure. Next, we use the default Laravel Auth way to obtain the current user's ID, then we run a query on the permissions table using Eloquent. NOTE: I am assuming you have a column in your permissions table that is user_id. This query gives us a collection of all the records owned by that user in the permissions table. Now we're binding that result to the variable $userNavigation and passing it to user-navigation as $userNavigation. NOTE: Because you are including user-navigation.blade.php in another file, that file will also have access to this $userNavigation variable.
Now, in user-navigation.blade.php lets write the logic. First we check to see if that variable is null. You can optionally skip this if you know a non-logged in user will never access this view. Then simply loop through it and display the results.
#if ( $userNavigation )
<ul class="side-nav">
#foreach( $userNavigation as $navItem )
<li>
{{ $navItem->url_name }}
</li>
#endforeach
</ul>
#endif
In the above example I am assuming you have a database column called url where you are storing the link and a column called url_name where you are storing the text for the anchor link. If not, you might consider adding those.
And that's it. You can use a service provider to give your view file(s) access to certain data any time they are used.
Hope this helps.

Laravel NotFoundHttpException on route

I have been struggling with something very simple here nevertheless the vagueness around Laravel's routing system that complicates its easy routing approach. I have gone through questions listed here but nothing seems to help me so here it goes.
I have formerly defined a route to my controller on an action named "create". This action is suppose to accept a post data from the form and persist it. This create method has one parameter which defaults to null for a project id if its add else we pass an id e.g domain/projects/add/22 to edit and domain/projects/add to create a new one.
Below is the skeleton of the function:
public function create( $id = null ){ ... }
I then defined a route for this which is:
Route::post( 'projects/add', 'ProjectsController#create' );
Inside my form I have {{ Form::open(array('url' => 'projects/add', 'method' => 'post')) }} .
I keep on getting errors related to routing, Http or method not found exceptions. I tried to follow every suggestion on the net but cannot for the life of me find my way.
Please help me point to the right direction, thanks.
try with below sample code
routs.php►
Route::post('projects/add/{id?}',array('as'=>'project_create','uses'=>'ProjectsController#create'));
ProjectsController.php►
class ProjectsController extends BaseController{
public function create( $id = null ){
...
}
projects.blade.php► (for used blade templating your views should have blade.php extention )
<html>....
<form action="{{ route('project_create') }}"method="post">
....
</form>
</html>
Thank you guys for all your responses. After being swamped with work I finally came back to my project and wanted to try out some of your suggestions but I failed.
I did find a tutorial that I tried to follow and basically was able to have my routes working.
Inside the routes I added (see below):
Route::get('/projects/list', [
'as' => 'post.list',
'uses' => 'ProjectsController#listProjects'
]);
Inside my controller I just created a function that is named listProjects(). As for getting my form displayed I followed the same pattern except point to newProject() method in my controller.
As much as I was not keen on this approach I ended up creating another function just for saving my POSTed form data after a new project form has been filled out and submitted. I still used the same url as projects/add except pointing it to a different function in may controller named saveProject().
About the view I just added the as part of the same save route and it worked. below is a link to the tutorial I followed and taking a look at the code.
http://www.codeheaps.com/php-programming/creating-blog-using-laravel-4-part-1/

Why is passing of second variable on href much flexible in Laravel?

I am using Laravel Framework 4.1.30.
My Original Problem was...
I got a working route that when I type "localhost/user/alvin" it works but when I click a drop-down bar, it does not work.
drop-down bar from template was: (li tag removed)
{{ Auth::user()->username }}
route is:
Route::get('/user/{username}', array(
'as' => 'profile-user',
'uses' => 'ProfileController#user'));
View of URL when I click the dropdown bar is:
"localhost/user/%7Busername%7D"
I got 3 working answers for this from other communities.
First was a simple:
{{ Auth::user()->username }}
Second was:
{{ Auth::user()->username }}
Last was:
<a href="{{ URL::route('profile-user',['username'=>Auth::user()->username]) }}">
{{ Auth::user()->username}}</a>
Originally I thought the simplest (First Solution) form would be the wisest choice but as per most developer they choose the Third answer because it allows some flexibility to some extent.
I want to implement a good Quality Assured code as much as possible.
My question... Why would the 3rd solution be much more flexible compared to a much simpler code?
You can even use a more flexible, shorter modification of the third one:
<a href="{{ route('profile-user', Auth::user()->username) }}">
When you are using named routes, you do not put actual URLs, nor controller method names into your Views. You only provide a route name to your Views, and all other things are defined in your routes, and can be changed, if needed, without making breaking anything else. It would be a waste not to use this feature.
It is also more flexible, by not specifying the parameter name, so it can be changed in the routes too, without breaking the Views. This way they will be just taken in the order they were inputted. They are also read by a controller method in the order, i.e. public function index($parameter1, $parameter2) not by an associative array.
The route() is an alias to URL::route().

Categories