Laravel two 404 styles - php

I have a main site and an admin control panel.
I want to have different 404 pages for each version.
How should I do this? I currently have the following code in my app/Exceptions/Handles.php file:
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if($exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException)
{
$view = $request->is('admin/*') ? 'acp.errors.404' : 'errors.404' ;
return response()->view($view, [], 404);
}
return parent::render($request, $exception);
}
But I use the package spatie/laravel-permission and get the following error;
Trying to get property 'role' of non-object (View: F:\Development\RPR\site\resources\views\layouts\acp.blade.php) (View: F:\Development\RPR\site\resources\views\layouts\acp.blade.php)
I use in acp.blade.php auth()->user()->role, to get the user role, which just works fine without any exception. How should I fix this?

Here are two ways to accomplish different 404 views depending on the route. Both will allow you to have these error pages:
/resources/views/acp/errors/404.blade.php
/resources/views/errors/404.blade.php
The directories will be checked in order until a view is found, which means you can selectively add custom error views and fall through to the default when none exist. If the route did not match, then it will not look for a custom error page.
Option 1
Override registerErrorViewPaths() inside app/Exceptions/Handler.php:
/**
* Register the error template hint paths.
*
* #return void
*/
protected function registerErrorViewPaths()
{
parent::registerErrorViewPaths();
if (request()->is('admin/*')) {
View::prependNamespace(
'errors',
realpath(base_path('resources/views/acp/errors'))
);
}
}
Option 2
Create a ViewServiceProvider:
php artisan make:provider ViewServiceProvider
Register your provider in config/app.php:
'providers' => [
// ...
App\Providers\ViewServiceProvider::class,
],
Edit the boot method of your provider:
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
if (request()->is('admin/*')) {
View::prependNamespace(
'errors',
realpath(base_path('resources/views/acp/errors'))
);
}
}
For the second part of the question, auth()->user() is only available when the session middleware has run. If the 404 was caused by the route not existing, then the request does not go through the web middleware and unfortunately sessions and auth information will not be available. However, if the 404 was caused by a ModelNotFoundException triggered inside a controller, then the web middleware probably did run and you can access the user.
Inside your error view you have to check if the user is signed in:
#guest
<p>Hello, guest</p>
#else
<p>Hello, {{ auth()->user()->name }}</p>
#endguest
If this is not good enough for your use case, then you might want to try Route::fallback(), which allows you to define a controller for serving 404 pages and does run web middleware.

Related

Laravel 5.7.6 resource route with auth

I'm starting my first Laravel project (first MVC / OOPHP project infact) and could use some help with routes.
I followed the guide at https://medium.com/employbl/easily-build-administrator-login-into-a-laravel-5-app-8a942e4fef37 to add a check if user is admin when loading a page. It works for normal view routes, e.g.
Route::get('/admin/something', 'AdminController#admin_something')
->middleware('is_admin')
->name('admin');
But I now have a resource route and get an error when I add the two -> lines to the route. So this works with no auth:
Route::resource('thingies', 'ThingyController');
But with this:
Route::resource('thingies', 'ThingyController')
->middleware('is_admin')
->name('admin');
I get the error Symfony \ Component \ Debug \ Exception \ FatalThrowableError (E_RECOVERABLE_ERROR)
Too few arguments to function Illuminate\Routing\PendingResourceRegistration::name(), 1 passed in /var/www/routes/web.php on line 24 and exactly 2 expected
What do I need to do differently to add this auth to a resource route?
The is_admin() function from the tutorial:
const ADMIN_TYPE = 'admin';
const DEFAULT_TYPE = 'default';
public function isAdmin() {
return $this->type === self::ADMIN_TYPE;
}
And the middleware:
namespace App\Http\Middleware;
use Closure;
class IsAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(auth()->user()->isAdmin()) {
return $next($request);
}
return redirect('home');
}
}
You can't name your route "admin" with ->name('admin'); at the end of your resource route because it concerns all CRUD routes in one statement and Laravel build-in system has already named them.
You're on the good way, just delete the last line like so, it should works :
Route::resource('thingies', 'ThingyController')
->middleware('is_admin');
You cannot give a 'name' to a resource route. but you can give names to each method in the resource controller separately.
to do so name() function required 2 parameters.
method name
name for that method route.
,
Route::resource('thingies', 'ThingyController')
->middleware('is_admin')
->name('create', 'admin.create');

Laravel always returning 302 on PUT

I created a Laravel REST API. My routes are auto-generated with Route::apiResource().
Authentication is set up using Passport and is working as intended.
Now I tried to implement an Admin role with a Middleware that I attach to store, update and delete in my Controller:
$this->middleware('admin-only', ['only' => ['store', 'update', 'delete']]);
I want my application to respond with 403 Insufficient permissions if the User is not an Admin.
For GET it is working as intended, but for PUT requests Laravel is always returning a Response with 302 Found.
AdminOnly Middleware
namespace App\Http\Middleware;
use Illuminate\Http\Response;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminOnly {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next) {
$user = Auth::user();
if (!$user->admin) {
return Response::create('Insufficient permissions', 403);
}
return $next($request);
}
}
I found my Error by throwing an Exception in constructor of Symfony\Component\HttpFoundation\RedirectResponse class (thanks to #jedrzej.kurylo) and examining the produced stack trace in laravels errorlog (storage/logs/laravel.log).
The AdminOnly middleware was never called, because I forgot to onclude the id in the request (/person instead of /person/{id}). This caused my default route to trigger and redirect to the application base.

Laravel automaticly redirect on authentication exception

im working with laravel 5.5 and tried to protect an api route. I assigned the 'auth' middleware to this route, but when i tested it, i get an InvalidArgumentException aka 'Route [login] is not defined'. Im not reffering to this route in any way, laravel automaticaly tried to redirect to this route. I found the following code line in file 'laravel\framework\src\Illuminate\Foundation\Exceptions\Handler.php':
/*
* Convert an authentication exception into a response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Auth\AuthenticationException $exception
* #return \Illuminate\Http\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest(route('login'));
}
So im wondering, whats the best way to catch this exception globally on every route?
From my understanding, its because you added an auth middleware to the route. So whenever the route is accessed without an authentication, the route is redirected to the name login route which is the login page by default.
You can add an unauthenticated method to your app/Exceptions/Handler.php with the following code. So if the request is in API or expecting JSON response, it will give a 401 status code with JSON response. If the route is accessed using web routes, it will be redirected to the default page. In the below example, am redirecting it to a login page.
Note: This method existed in the app/Exceptions/Handler.php till laravel 5.4.
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
return redirect()->guest(('login'));
}
Don't forget to import use Illuminate\Auth\AuthenticationException;
Edit: Explanation for the error Route [login] is not defined. This is because laravel is trying to redirect to a named route. This named route is available if you are using the laravel auth scaffolding. If you are manually creating the functions, even if the route login exists, it should be named as login. For more information, see https://laravel.com/docs/5.5/routing#named-routes

Laravel 5 Routing Calling Wrong View

I've got a simple Laravel 5 site that I am working on (learning Laravel)
I have a link on my 'users' view to add a 'new user':
Create New User
My user Routes look like the following:
Route::get('/users', 'UserController#index');
Route::get('/user/{id}', 'UserController#edit');
Route::get('/user/create', 'UserController#create');
Route::get('/user/update', 'UserController#update');
Route::get('/user/delete/{id}', 'UserController#delete');
My UsersController has the following:
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
$users = user::all();
return view('user.index',compact('users'));
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
$userroles = userrole::all();
return view('user.create', compact('userroles'));
}
When I click the link - I get the following error:
Trying to get property of non-object (View: C:\xampp\htdocs\mysite\resources\views\user\edit.blade.php)
I cannot figure out why it is trying to load edit.blade.php
I have tried just putting the following in my create view and it still doesn't render.
#extends('app')
#section('content')
Test
#endsection
I'm not sure why the routing is being so bizarre.
Any ideas?
Put the create (and update) route before the edit route in your routes file. Laravel 1st stumbles on the edit route and treats the create as id.
You can test it by doing a dd($id) in your edit method in the controller.

Laravel 5 custom 404 controller

I am making laravel app that has a CMS with pages stored in the database. A page record in the database has an id, title, content and url slug.
I want it so that if someone visits the url slug then it will load a page to display the content of that page on. So I think I need to override the 404 handler so that say the user goes to mysite.com/about before it 404's it checks the database to see if there is a record with about stored as the url slug. If there is then it will load the page to show the content, or if not then it will 404. I'm 90% of the way there with the following solution:
I have modified the render method in App\Exceptions\handler.php to be the following:
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $e
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
$page = Page::where('url', '=', Request::segment(1))->first();
if(!is_null($page)) {
return response(view('page', [ 'page' => $page ]));
}
}
return parent::render($request, $e);
}
This works however there is a problem, all of my menus are stored in the database as well and I have a made FrontendController that gets all of the menus from the database to be loaded into the views. All of my controllers extend FrontendController so that this is done on every page load and I don't have to repeat any code. So is there a way that I can route all 404s through a controller instead so that it can extend my FrontendController? Or am I just going to have to repeat all of the logic that the front end controller does in the exceptions handler file?
Solved it! Thanks to Styphon for giving me the idea, I just defined the following route as my very last route:
Route::get('/{url}', 'FrontendController#page');

Categories