Thursday, November 1, 2018

Laravel : Better Way To Deal with Trying to Get Property Of Non Object

Hello Everyone,

After a gap of almost 3 months I am again writing a blog. In laravel project we sometimes face issues like we try to access model but it's not available due to various reason like
  • id we passed is wrong
  • we manually deleted records from database
  • wrong value saved in database.
  • wrong model you are trying to access with wrong value.
When we get this error, Laravel throws an ErrorException with message "Trying to get property of non object."

However, end user does not understand this error so we have to show them proper message on what's missing. We can not display generic error page and for each line of code we can not put the check so better to check it at single place. Laravel gives this functionality in Exception Handler. Here the trick is to get model which throws this error and get name of Model and display proper message. 

Laravel has class ModelNotFoundException which is thrown when you use findOrFail function. However we developers sometime do not use this function and use find function. 

When find function is used, it throws ErrorException which does not contain Model information. However we can modify Laravel source code for this. 

Open following file 

vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php

Here find public function find($id, $columns = ['*'])

and modify it as following.

        if (is_array($id) || $id instanceof Arrayable) {
            return $this->findMany($id, $columns);
        }
        $object = $this->whereKey($id)->first($columns);
        if($object == null){
            throw (new ModelNotFoundException)->setModel(
                get_class($this->model), $id
            );
        }else{
            return $this->whereKey($id)->first($columns);
        }

PLEASE NOTE : I DO NOT RECOMMEND TO MODIFY SOURCE FILE SO BETTER WAY IS TO USE FINDORFAIL FUNCTION.

Now if you use find or findOrFail function it will throw ModelNotFoundException. Now open app/Exception/Handler.php file and find out function public function render($request, Exception $exception)

Add following code at start of the function.

       if(get_class($exception) == 'Illuminate\Database\Eloquent\ModelNotFoundException'){
            $modelName = explode("\\",$exception->getModel());
            $modelName = $modelName[count($modelName)-1];
            $message = "You are trying to access ".$modelName." which does not exist.";
                return Response::make(view('Frontend.error')->with(array(
                    "message"=>$message
            )));
        }

That's it and now it will display proper message like

You are trying to access User which does not exist.
You are trying to access Post which does not exist.

Hope this helps you.