web middleware for specific routes in laravel 5.2 - php

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.

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 - how can make dynamic content with layout?

I'm wanting to create a website with laravel framework. I had made layout but now, have some zone i don't know how to set content for it. Ex: 2 zone of me are left-menu and cart (please view picture). My left-menu will get content from table: categories and cart will get content from package cart [Cart::content()].
It's on layout and of course, all page will have it. But i don't know how to give content of categories and cart() for it. Please help me
I think that you should to use View Composer.
https://laravel.com/docs/5.6/views#view-composers
Use Blade templates, as found here: https://laravel.com/docs/5.6/blade
Wherever in your page you want to print content, use the {{ $mycontent }} construct. You can also use confitionals and loop structures like #if and #foreach to loop through collections.
Then, in your controllers, you can just call the view and pass it content from your database or wherever you get it by doing something like:
return response()->view(“myView”, [“mycontent” => $content], $httpStatus);
You may opt for afterMiddleware if you want it on every page. Create a section on the master blade page (usually app.blade.php) and fill it in the middleware just like you would in any other controller. You can create a middleware by running php artisan create:middleware Cart. A file will be created at app/Http/Middleware/Cart.php.
Register the middleware in the app/Http/kernel.php file.
You may have to add a Auth::check() condition to avoid errors.

Adding information to a Layout without having to call it on every controller

I have a layout that is used when you are logged in. menu.blade.php.
Then I use it in blade files #extends('admin.layouts.menu')
I want to show some information in the layout, let's say the number of messages near the "message" link in the menu. I could easily do this by adding:
$message_count = Message::where("user_id", Auth::user()->id)->count();
and adding: <div>{{$message_count}}</div> to menu.blade.php
to every single controller and view where the layout is used, but this is clearly not a clean way to do it.
Is there a way to pass information to the view in a single step instead of having to do it in every single controller?
Use view composers.
View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location
Register the view composer within a service provider:
public function boot()
{
View::composer('menu', function ($view) {
$view->with('messagesCount', auth()->user()->messages->count())
});
}
Then each time when the menu view will be rendered, it will have $messagesCount variable with counted messages for an authenticated user.

Mapping route to another route in Laravel

I am developing an application in the Laravel 5.2 which must have a friendly URL-s. This is not problem with the regular way, where the {slug} wildcard is handled by a controller but I want to make it in a different way.
For now I have only two controllers:
ProductsController#show to show details product
CategoriesController#show to show selected category with listing products which are assigned to it
And routes:
Route::get('product/{product}', 'ProductsCategory#show')->name('product.show');
Route::get('category/{category}', 'CategoriesController#show')->name('category.show');
So when I want to echo a just use route('product.show', compact('product'))
Nothing special until I want to handle different URL-s which are fetched from a database. I thought it will be possible to make an another routes which are assigned to existing and when I use a route(...) helper it will be handled automatically. But it is not. So for example I have a new URL:
domain.com/new-url-for-product.html
so by route it should be assigned to the regular 'product.show' route with some ID, which is handled by route model binder for {product} wildcard. The same for route(..) helper it should print friendly URL-s.
I don't know if my strategy is good. How do you handle with the similar problems?
of course route will handle this automatically. have a look into this. I am just giving you example, you have to set this code as per your need.
route('product.show', 'product'=>'new-url-for-product.html');
will generate this
domain.com/new-url-for-product.html
route('product.show', 'product'=>'new-url2-for-product.html');
will generate this URL
domain.com/new-url2-for-product.html
and so on, and then you have to handle all this in your controller method.
eg: your controller method for this route is ProductsCategory#show which is
public function show($product){
if($product == 'new-url-for-product.html'){
//perform this
}
if($product == 'new-url2-for-product.html'){
//perform this
}
}
this just an example, you have to map this according to your need
Edited Here is the example I tested it
Route::get('/product/{product}.html', ['as'=>'product.show', 'uses'=>'ProductsCategory#show']);

PHALCON PHP - Different views based on user profile

I created Phalcon PHP app,
I have 3 different user profiles: (ID: 1) Administrators, (ID: 2) Accountants and (ID: 3) Warehouses.
I want my app able to render views based on those profiles, for example
controllerName/index.1.volt //for Administrators
controllerName/index.2.volt //for Accountants
controllerName/index.3.volt //for Warehouses
but when those files weren't found, my app will fallback to:
controllerName/index.volt
How do I accomplish that?
One approach, although messy, would be to use controlerName/index.volt as the "landing page" then from there check an if-statement to decide what the user's role is. Then from the if-statement use a partial like {{ partial("index.1.volt") }} but you'd need to hard-code this for every controller... yuck...
A good solution which I'd recommend, though, would be to make use of the View's exists method to check if the view exists from within your controller. The idea is you pass this method the string path to the view file you're looking for but omitting the extension. The reason you omit the extension is because Phalcon allows you to use multiple rendering engines so an application using a mixture of .volt and .phtml would work.
Assuming you were using user roles something like this:
define('GUEST_ROLE',0);
define('ADMIN_ROLE',1);
define('ACCOUNTANT_ROLE',2);
define('WAREHOUSE_ROLE',3);
(with the guest role with a value of 0) I would suggest having all your controllers extend a ControllerBase then define the following two methods in your ControllerBase:
public function getUserLevel()
{
if($this->session->has('userLevel'))
{
$userLevel=$this->session->get('userLevel');
return (int)$userLevel;
}else{
return 0;//default to guest
}
}
protected function initialize()
{
$controllerName=$this->dispatcher->getControllerName();
$actionName=$this->dispatcher->getActionName();
$userLevel=$this->getUserLevel();
if($this->view->exists("$controllerName/$actionName.$userLevel"))
{
$this->view->pick("$controllerName/$actionName.$userLevel");
}
//No reason to add an else, Phalcon defaults to "$controllerName/$actionName"
}
Just make sure, that if you ever need to define a custom "initialize" method for a specific controller which extends the ControllerBase, e.g. to add a title prefix to all pages related to the controller, that you call parent::initialize(); otherwise it won't get called. But that's only if you're going to be overriding the method.
Chances are you're already using a ControllerBase and doing similar logic already, if so, you'd need to edit your already existing "initialize" method and merge my code with yours.
Happy coding.

Categories