How to treat 'No query results for model' in Laravel - php

We have a DB with 100 users.. for example
And here is the route
Route::get('/users/{user}/edit', 'UserController#edit');
Here is the method
public function edit(User $user)
{
$hi = 'Hello';
return $hi;
}
Ok.. if I do something like this
http://localhost/users/99/edit | WORKS
http://localhost/users/100/edit | WORKS
http://localhost/users/101/edit | PROBLEM
How to solve when the user change the value from URL with an inexistent record..?

In this case I would pass the user id as a parameter rather than explicit binding:
Route::get('/users/{userId}/edit', 'UserController#edit');
When a model is not found you will get a ModelNotFoundException exception thrown, you can catch it and treat this case
Controller:
use Illuminate/Database/Eloquent/ModelNotFoundException;
[...]
public function edit($userId)
{
try{
$user = User::find($userId);
//do some stuff
} catch (ModelNotFoundException $e){
//treat error (log the activity, redirect to a certain page)
//or display a 404 page
//dealer's choice
}
}

In your case the user can't be found, which is a 404:
public function edit(User $user)
{
$user = false; // dummy/example
if ($user) {
return $your_results;
} else {
return view('errors.404');
// assuming you have a folder called 'errors' inside your 'views' folder
// and the file name is `404.blade.php`
// and are using blade files.
}
}
If the user can be found, show whatever you need to, else, show a 404 blade view.

A little late, but it is an useful question.
To suppress the error message you can set the debug mode in the (dot)ENV file to false APP_DEBUG=true. Due to the route model binding, the other solutions above won't help you, because if the model is not found, you won't get into the controller. Unless you don't get the model but only the id or slug etc. and then make a query against the database within the function.

Related

calling a function before redirecting to 404 page on findOrFail

when i delete something from database ... lets say a blog , i might want to redirect the link for that blog to another link instead of 404 page
i have a table called setting_redirects :
setting_redirects : id , deleted_link , redirect_link
i'll store the deleted links and new link in this table
this is how i show a blog
function show( $blog_id ){
$blog = Blog::findOrFail($blog_id);
}
the problem is if it's not found findOrFail will automatically redirect user to 404 page ... i want to be able to checksetting_redirects to see if a redirect is available for that blog .... if so redirect to that new link otherwise go to 404
something like this
function show( $blog_id ){
$blog = Blog::find($blog_id);
if(!$blog)
{
$redirect_available = SettingRedirect::where('deleted_link ' , Request::url() ) ->first();
if($redirect_available )
return redirect( $redirect_available-> redirect_link );
else
abort(404);
}
}
but i want this for all my tables not only blogs and i dont want to write this code in all of my controllers
is there anyway to do this without changing all my controllers ? maybe a middleware before going to 404 page ?
findOrFail throws a ModelNotFoundException. I think you can simply catch and rethrow it.
try {
$blog = Blog::findOrFail($blog_id);
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
$redirect_available = SettingRedirect::where('deleted_link', Request::url())->first();
if ($redirect_available)
return redirect($redirect_available->redirect_link);
throw $e;
}
Alternatively, you can put this logic in the app/Exceptions/Handler.php file inside the register() method.
<?php
namespace App\Exceptions;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;
class Handler extends ExceptionHandler
{
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
// this is to make sure the exception was caused by a findOrFail operation
if ($e->getPrevious() instanceof ModelNotFoundException) {
$redirect_available = SettingRedirect::where('deleted_link', $request->url())->first();
if ($redirect_available) {
return redirect($redirect_available->redirect_link);
}
}
});
}
}
The reason why the first line is $this->renderable(function (NotFoundHttpException $e, $request) { ... }) instead of$this->renderable(function (ModelNotFoundException $e, $request) { ... })
is because Laravel does this weird thing of transforming ModelNotFoundException into NotFoundHttpException before it's available to the exception handler.
I think need to change status of that particular record while deleting action, code on 404, if query executeble then include "mypage.php"; else would show default 404 page

Delete File error

I am trying to delete a file after I delete the product from the database, but it shows me an error, and I cannot find a solution.
Call to a member function delete() on null
public function supprod($id,Request $request)
{
$produits=Produit::find($id);
$produits->delete(); // <-- error on this line
$filename=(string)$document->nom;
Storage::disk('fichier')->delete($filename);
return redirect()->back();
}
Image of the problem
this is check the record this function $produits=Produit::find($id);
This use $produits=Produit::where('id',$id)->first();
You should really let Laravel do the work for you.
Change your action to:
public function supprod(Produit $id, Request $request) {
$filename=(string)$id->nom;
Storage::disk('fichier')->delete($filename);
$id->delete();
return redirect()->back();
}
This will make the framework find the appropriate model or throw a 404 error if it does not exist.
More information in the implicit model binding section of the documentation

Laravel - Creating a Dynamic URL using a Database String

I am with a bit of a stuggle here. I managed to create the dynamic URL using the following code:
Home Page Controller
$satellites = DB::table('satellites')->get();
return view('pages/home', ['satellites' => $satellites]);
Blade File
#foreach($satellites as $satellite)
<li>{{$satellite->satname}}</li>
#endforeach
web.php
Route::get('{norad_cat_id}', 'Satellite#show');
Controller
public function show($norad_cat_id)
{
return view('pages/satellite');
}
The URL generated is: mysite.com/12345 (where 12345 is the norad_cat_id).
This code manages to create the dynamic URLs using the norad_cat_id from the database - which is what I want. The problem is that I can replace the URL with anything and it still creates a page (ie. replace the 12345 with something not from the database and a page is still created).
What I want is only for a URL to be generated only with the norad_cat_id and if there is no matching norad_cat_id in the database, display a 404 page.
In the show method add a fetch from database if there is no record just abort
public function show($norad_cat_id)
{
$satellite = DB::table('satellites')->where('norad_cat_id', $norad_cat_id)->first();
if( ! satellite){
return abort(404);
}
return view('pages/satellite');
}
PS: abort will automatically redirect to your resources/views/errors/404.blade.php
You can do this multiple ways
Create a regex for norad_cat_id
The example shows nummeric ([0-9]+)
Route::get('{norad_cat_id}', 'Satellite#show')->where(['norad_cat_id' => '[0-9]+']);
Use findOrFail() and on fail show the 404.
try
{
$user = Satellites::findOrFail($id);
return view('norad_cats');
}
// catch(Exception $e) catch any exception
catch(ModelNotFoundException $e)
{
return view('404');
}
You can throw 404 in your controlller (for example). Just check if records exists in database - if not then return error.
Example:
public function show($cat_id)
{
$sattelites = DB::table('sattelites')->where('norad_cat_id', $cat_id)->get();
if ($satellites === NULL)
{
\App::abort(404);
}
return view('pages/sattelite', [
'satellites' => $satellites
]);
}
I think you get the idea.
Here's an example using query builder which is what I assume you're using:
public function show($norad_cat_id)
{
$norad_cat_data = DB::table('satellites')->where('norad_cat_id', $norad_cat_id)->get();
if (!is_null($norad_cat_id)
{
return view('pages/satellite');
}
\App::abort(404);
}

Error when trying to return Redirect in Laravel

When I submit a form in Laravel, the following controller method handles it:
public function update($id)
{
//handle input
return View::make('generic.success', ["message" => 'Data submitted successfully!']);
}
This works fine. However, instead of returning a view like above I'd like to return a redirect, because when I return the view directly, reloading the page resubmits the form.
So I tried to do this:
public function update($id)
{
//handle input
return Redirect::to('/success', ['message' => 'Data submitted successfully!']);
}
In my routes file I defined the success route:
Route::get('success', 'NotificationsController#success');
And set up a notification controller to display the view:
class NotificationsController extends BaseController {
public function success($message)
{
return View::make('generic.success', ["message" => $message]);
}
When I run the above code, I get the following error from Laravel:
InvalidArgumentException
The HTTP status code "1" is not valid.
I have no idea what this is supposed to tell me, and neither does Google apparently.
Can someone shed some light on this issue?
P.S.
Incidentally, being new to Laravel, I've noticed so far that Laravel's error reporting is very user-unfriendly, in that instead of telling me I have an issue with my router, or controller, or permissions, it displays these generic errors with no humane explanation of their cause. Is there a better way to troubleshoot problems in Laravel than relying on this?
For example, in the above incident, the error report points to this line of code...
public function setStatusCode($code, $text = null)
{
$this->statusCode = $code = (int) $code;
if ($this->isInvalid()) {
throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
}
...which is completely useless, as all it does is show me the code that printed the error itself.
The second parameter of the redirector's to() method is the HTTP status code that will be returned by the response, not data that will be passed along. Passing data when redirecting to GET routes can be done either via the query string or the session. The recommended solution here is to pass data via the current session using the with() method which passes that data for the next request. So in your case this would be the approach needed:
public function update($id)
{
return Redirect::to('/success')->with('message', 'Data submitted successfully!');
}
Then in your success method you can have this:
public function success($message)
{
return View::make('generic.success', ["message" => Session::get('message')]);
}
When in doubt always try checking the documentation first. The solution to this is explicitly stated in the Laravel Response Redirects Documentation.
Thanks a lot -Bogdan I found in the documentation that you post answer to my problem. In my case the solution was redirect to an action in a controller, like this...
return
\Redirect::action(
'PqrController#solicitud',
array($id)
)
->with(
'message',
'¡El estado de la solicitud ha sido actualizado correctamente!'
)
;
I redirect to a method in a controller, with one parameter array($id) and I put too in the session a message using ->with('message','Mensaje')

Checking if there is not record with that id

I've just started to learn about laravel and I want to use this framework with It's advantages. I'm asking this question to learn the right way to this with laravel.
It is printing the post from posts table which has the same id with $id.
<?php
class PostsController extends BaseController{
public function singlePost($id)
{
$thePost = Posts::find($id);
return View::make('singlePost')->with('thePost', $thePost);
}
}
Normally I do check if there is a post that's id equal to $id and if that is, return view so forth. Isn't there better way to do this with laravel like you can do with route filters.
Shortly,
How to know if there is post with that id?
How to throw exception if there is not?
...
Route Model Binding might be an option however a more universal solution is findOrFail
findOrFail will either return the model or throw a ModelNotFoundException which will display as a 404 page.
$thePost = Posts::findOrFail($id);
return View::make('singlePost')->with('thePost', $thePost);
To just check for existence you can use find and then compare with null:
$thePost = Posts::find($id);
if($thePost != null){
// post exists
}
Or simpler, just a truthy value:
$thePost = Posts::find($id);
if($thePost){
// post exists
}
See "Route Model Binding" in the documentation.
Route::model('post', 'Post', function() {
// do something if Post is not found
throw new NotFoundHttpException;
});
Route::get('post/{post}', function(Post $post) {
return View::make('singlePost')->with('thePost', $post);
});
You could also just replace find() with findOrFail() in your code, which would throw an exception of a post wasn't found with that ID.

Categories