Laravel firstOrFail functions redirects to wrong route - php

Info: All my routes look like this /locale/something for example /en/home
works fine.
In my controller I'm using the firstOrFail() function.
When the fail part is triggered the function tries to send me to /home.
Which doesn't work because it needs to be /en/home.
So how can I adjust the firstOrFail() function to send me to /locale/home ?
What needs to changed ?

You can treat it in several ways.
Specific approach
You could surround your query with a try-catch wherever you want to redirect to a specific view every time a record isn't found:
class MyCoolController extends Controller {
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Redirect;
//
function myCoolFunction() {
try
{
$object = MyModel::where('column', 'value')->firstOrFail();
}
catch (ModelNotFoundException $e)
{
return Redirect::to('my_view');
// you could also:
// return redirect()->route('home');
}
// the rest of your code..
}
}
The only downside of this is that you need to handle this everywhere you want to use the firstOrFail() method.
The global way
As in the comments suggested, you could define it in the Global Exception Handler:
app/Exceptions/Handler.php
# app/Exceptions/Handler.php
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Redirect;
// some code..
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException && ! $request->expectsJson())
{
return Redirect::to('my_view');
}
return parent::render($request, $exception);
}

Related

MethodNotAllowedHttpException, redirect to 404 Laravel 9

So when a user randomly types a URL on a route that exists, they get an error message:
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: POST.
After doing some searching, all the posts I can find suggest to change the render function inside of App\Exceptions\Handler and change it to this:
public function render($request, Exception $exception)
{
if($exception instanceof \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException){
return abort('404');
}
return parent::render($request, $exception);
}
However, with the newer version of Laravel this no longer exists. One post mentioned to add this in routes\web.php:
Route::fallback( function () {
abort( 404 );
} );
This works fine but I'm not sure if this is the best approach/right place to have it? Is there are any other alternative way?
I have also attempted to change the register function inside of App\Exceptions\Handler to this per the Laravel Doc (https://laravel.com/docs/9.x/errors#rendering-exceptions):
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}
but it does not work
In a newer version on Laravel, you can add
$this->renderable(function (Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException $e) {
// do something
});
this line inside a register method on a class \App\Exceptions\Handler
If you want to handle NotFoundException you should use
$this->renderable(function (Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e) {
// do something
});
You can find more detailed answer on Laravel documentation here:
https://laravel.com/docs/9.x/errors#rendering-exceptions

How to direct random routing (404) to one view page in laravel?

I want to direct random link to only one view except that I've define
e.g : localhost:8000/abxcdss
It will go to a view or home page.
Solution #1 - Via Exception Handler (404)
app/Exceptions/Handler.php
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
//
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
return redirect('/');
}
return parent::render($request, $exception);
}
Solution #2 - Via Route
routes/web.php
// Last line!
Route::any('{any}', function () {
return redirect('/');
});

Change response in terminate function of middleware

I'm trying to change the message from 500 errors if certain conditions are met. I have this middleware in a file called ApiAfter.php:
<?php
namespace App\Http\Middleware;
use App\Helpers\Helper;
use App\Helpers\LogHelper;
use Carbon\Carbon;
use Closure;
use DateTime;
use Exception;
use Illuminate\Http\Response;
class ApiAfter
{
public function handle($request, Closure $next) {
return $next($request);
}
public function terminate($request, $response) {
// move everything in handle function to this
// logging the results of the request
$response = $this->fixFiveHundred($request, $response);
// I do some other stuff here
return $response;
}
private function fixFiveHundred($request, $response) {
if ($response->status() !== 500) return $response;
try {
if (!empty($response->original['message']) && $response->original['message'] === "Server Error") {
if (!empty($response->exception)) {
$newMessage = $response->exception->getMessage();
return response($newMessage, 500); // this is the line of code I'm having trouble with
}
}
} catch(Exception $e) {
return $response;
}
return $response;
}
}
On the line that says return response($newMessage, 500);, I've tried lots of different things but nothing is actually changing the response. Can you not change a response in the Terminate function?
You should avoid handling exception in a custom middleware. Laravel provides a simple class that allows simple customization. Have a look to "render method" of Laravel Error Handling in official docs.
Can you not change a response in the Terminate function?
Short answer: No.
The terminate function in a middleware runs after the response was sent to the browser. Thus, you can't modify the response.
As #Roberto Ferro pointed, the right place to handle custom response for exceptions is using the render in the Exception Handler

How do I force a Laravel 5.6 redirect from a RedirectResponse object?

I have a custom non-Eloquent class called Item. In the constructor, the Item attempts to authenticate with my app. I then have an Auth() method to check whether the authentication was successful, and if not to fire a redirect.
public function Auth() {
if (isset($this->authenticated)) {
return $this;
} else {
return redirect('/auth');
}
}
I use the Auth method to access Item statically via a Item Facade. The intended outcome is that if the Item is authenticated, we proceed to the Index with that Item as a variable. If not the redirect would be triggered.
public function index() {
$item = Item::Auth();
return view('index',$item);
}
However, if not authenticated, all that happens is a Laravel RedirectResponse object is passed to the index view. How can I force that redirect to actually fire?
Solutions I have thought of but don't like
if ($item instanceOf RedirectResponse)... in the controller, but this feels clunky
$item = new Item; if ($item->authenticated)... this is fine for the controller but I want to trigger the redirect from multiple parts of the app so there would be a lot of code re-use which doesn't feel efficient.
Thanks for any help. Either on the Facade front or firing the Redirect object.
You can handle this case by using an AuthenticationException:
public function Auth() {
if (isset($this->authenticated)) {
return $this;
}
throw new AuthenticationException();
}
The authentication exception will generate either a redirect to login or a 401 error on JSON routes. You can of course throw a different exception and handle it as a redirect in your App\Exceptions\Handler class
For example:
public function Auth() {
if (isset($this->authenticated)) {
return $this;
}
throw new ItemNotAuthorizedException(); //Custom class you need to create
}
In Handler.php:
public function render($request, Exception $exception)
{
if ($exception instanceof ItemNotAuthorizedException) {
return redirect("/auth");
}
return parent::render($request, $exception);
}
Note: Laravel 5.6 might also allow exceptions to implement their own render function but you should refer to the documentation for that.

Redirection in laravel without return statement

i have this blogsController, the create function is as follows.
public function create() {
if($this->reqLogin()) return $this->reqLogin();
return View::make('blogs.create');
}
In BaseController, i have this function which checks if user is logged in.
public function reqLogin(){
if(!Auth::check()){
Session::flash('message', 'You need to login');
return Redirect::to("login");
}
}
This code is working fine , but it is not what is need i want my create function as follows.
public function create() {
$this->reqLogin();
return View::make('blogs.create');
}
Can i do so?
Apart from that, can i set authantication rules , like we do in Yii framework, at the top of controller.
Beside organizing your code to fit better Laravel's architecture, there's a little trick you can use when returning a response is not possible and a redirect is absolutely needed.
The trick is to call \App::abort() and pass the approriate code and headers. This will work in most of the circumstances (excluding, notably, blade views and __toString() methods.
Here's a simple function that will work everywhere, no matter what, while still keeping your shutdown logic intact.
/**
* Redirect the user no matter what. No need to use a return
* statement. Also avoids the trap put in place by the Blade Compiler.
*
* #param string $url
* #param int $code http code for the redirect (should be 302 or 301)
*/
function redirect_now($url, $code = 302)
{
try {
\App::abort($code, '', ['Location' => $url]);
} catch (\Exception $exception) {
// the blade compiler catches exceptions and rethrows them
// as ErrorExceptions :(
//
// also the __toString() magic method cannot throw exceptions
// in that case also we need to manually call the exception
// handler
$previousErrorHandler = set_exception_handler(function () {
});
restore_error_handler();
call_user_func($previousErrorHandler, $exception);
die;
}
}
Usage in PHP:
redirect_now('/');
Usage in Blade:
{{ redirect_now('/') }}
You should put the check into a filter, then only let the user get to the controller if they are logged in in the first place.
Filter
Route::filter('auth', function($route, $request, $response)
{
if(!Auth::check()) {
Session::flash('message', 'You need to login');
return Redirect::to("login");
}
});
Route
Route::get('blogs/create', array('before' => 'auth', 'uses' => 'BlogsController#create'));
Controller
public function create() {
return View::make('blogs.create');
}
We can do like this,
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect('/to/another/route/')->with('status', 'An error occurred.'));
It's not a best practice to use this method, but to solve your question, you can use this gist.
Create a helper function like:
if(!function_exists('abortTo')) {
function abortTo($to = '/') {
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect($to));
}
}
then use it in your code:
public function reqLogin(){
if(!Auth::check()){
abortTo(route('login'));
}
}
public function create() {
$this->reqLogin();
return View::make('blogs.create');
}

Categories