JSON Responses with JResponseJson

From Joomla! Documentation

Overview

A new class, JResponseJson, was added to Joomla! 3 that is able to simplify Ajax requests. As of Joomla! 3.8, the core code was namespaced and aliases were added. (See the /libraries/classmap.php file at or near line 131.) The JResponseJson class was replaced by the JsonResponse class in the Joomla/CMS/Response namespace.

With that class it is now possible to prepare responses to Ajax requests in a standardized and easy manner.

Have a look at the revised file on Github or in your own site's files at /libraries/src/Response/JsonResponse.php.

The JsonResponse class will mostly be used in controllers of components where you get the following advantages:

  • Using the flag success the JavaScript code (whether Mootools JRequest.JSON or jQuery.getJSON) can check whether the task was successful or not and react accordingly. (If the request itself fails, the error event of the JavaScript API can be used.)
  • Then the actual response data, if any, can be retrieved from data in the JSON object, and
  • Optionally a main response message from message.
  • Additionally, all gathered messages in the JApplication message queue are automatically sent back in messages. This can be turned off.

Another advantage is that no $app->close(); is necessary if the Ajax request is done with format=json. The existing API handles the rest. Just echo the response object at the end of the task.

How to Use

Most Common Cases

Here is an example controller file:

class MyController extends JControllerLegacy
{
  public function execute()
  {
    try
    {
      $anyParam = JFactory::getApplication()->input->get('anyparam');

      $result = $this->getModel('example')->createSomething($anyParam);

      echo new JsonResponse($result);
    }
    catch(Exception $e)
    {
      echo new JsonResponse($e);
    }
  }
}

In the default case, the return value of something that has been calculated by the model is simply pushed into a new JsonResponse object and written to the output. This will automatically create a JSON-encoded string as follows:

{"success":true,"message":null,"messages":null,"data":{"myfirstcustomparam":1,"mysecondcustomparam":42, ...}}

In the data field, you can send any array, object or value you want and the success flag is automatically set to true.

If any exception occurred in the model, this exception is simply passed directly to a new JsonResponse object which would create the following output:

{"success":false,"message":"This is the message of the exception","messages":null,"data":null}

Since it is an exception the success flag is automatically set to false and the message of the exception becomes the main response message.

Additional Options

If no exception is passed as the first argument of the JsonResponse constructor, you can specify an arbitrary main response message by passing a string as the second argument:

echo new JsonResponse($result, JText::_('COM_COMPONENT_MY_TASK_SUCCESS'));

which creates, for example:

{"success":true,"message":"The request was successful.","messages":null,"data":{"myfirstcustomparam":1,"mysecondcustomparam":42, ...}}

You can also set the error flag to false with the help of the third argument ($error):

echo new JsonResponse($result, JText::_('COM_COMPONENT_MY_TASK_ERROR'), true);

which creates, for example:

{"success":false,"message":"There was an error.","messages":null,"data":{"myfirstcustomparam":1,"mysecondcustomparam":42, ...}}

In this way you can also send some data back.

Regardless of having an error response or a success response, JsonResponse sends all messages back to the client that have been gathered in the application object:

$app = JFactory::getApplication();
// Some code ($result = ...)
$app->enqueueMessage('This part was successful');
// Some more code
$app->enqueueMessage('Here was a small warning'. 'warning');

echo new JsonResponse($result, 'Main response message');

This results in:

{"success":true,"message":"Main response message","messages":"<<all the encoded messages of $app>>","data":{"myfirstcustomparam":1,"mysecondcustomparam":42, ...}}

You can see the advantage of that below in the JavaScript section.

If you don't want to send the messages back (rather they stay in the session), set the fourth argument ($ignoreMessages) of the constructor to true.

Corresponding JavaScript Code

Here is some sample JavaScript that can be used on the client side together with JsonResponse on the server side. The example is written with Mootools code. Something similar can also be done with jQuery or any other JavaScript library for Ajax requests.

var req = new Request.JSON({
	method: 'post',
	url: 'index.php?option=com_component&task=mycontroller.execute&format=json',
	onSuccess: function(r)
	{
		if (!r.success && r.message)
		{
			// Success flag is set to 'false' and main response message given
			// so you can alert it or insert it into some HTML element
			alert(r.message);
		}

		if (r.messages)
		{
			// All the enqueued messages of the $app object can simple be
			// rendered by the respective helper function of Joomla!
			// They will automatically be displayed at the messages section of the template
			Joomla.renderMessages(r.messages);
		}

		if (r.data)
		{
			// Here you can access all the data of your response
			alert(r.data.myfirstcustomparam);
			alert(r.data.mysecondcustomparam);
		}
	}.bind(this),
	onFailure: function(xhr)
	{
		// Reaching this point means that the Ajax request itself was not successful
		// So JsonResponse was never called
		alert('Ajax error');
	}.bind(this),
	onError: function(text, error)
	{
		// Reaching this point means that the Ajax request was answered by the server, but
		// the response was not valid JSON. (This happens sometimes if there were PHP errors,
		// warnings or notices during the development process of a new Ajax request.)
		alert(error + "\n\n" + text);
	}.bind(this)
});
req.post('anyparam=myvalue');

Best Practice

As seen above, controller code can be kept simple using JsonResponse. The model is calculating the data which can be passed to JsonResponse afterwards. (You can still modify it in the controller.) If you are developing an MVC component, save such a controller in a file called mycontroller.json.php and place it inside your controllers folder. That controller is automatically executed if your request URL contains format=json.

Note that there is no need for closing the application ($app->close();) because the architecture of Joomla! handles that for you.

class MyController extends JControllerLegacy
{
  public function execute()
  {
    try
    {
      $anyParam = JFactory::getApplication()->input->get('anyparam');

      $count = $this->getModel('example')->countSomething($anyParam);

      echo new JsonResponse($count);
    }
    catch(Exception $e)
    {
      echo new JsonResponse($e);
    }
  }
}

Additional Note

If you want to support users without JavaScript by doing the same task with a normal request and a final redirect back to the page, there is not much you have do additionally. Just create another controller (with a name such as mycontroller.php) and code like this:

class MyController extends JControllerLegacy
{
  public function execute()
  {
    $app = JFactory::getApplication();
    $app->setRedirect(JRoute::_('index.php?option=com_mycomponent&view=myview', false));

    try
    {
      $anyParam = JFactory::getApplication()->input->get('anyparam');

      $count = $this->getModel('example')->countSomething($anyParam);

      $app->setMessage(JText::plural('COM_COMPONENT_COUNT_TASK', $count));
    }
    catch(Exception $e)
    {
      $app->setMessage(JText::sprintf('COM_COMPONENT_COUNT_TASK_ERROR', $e->getMessage()), 'error');
    }
  }
}

No changes are necessary in the model!

Depending on whether you do the request with format=json (Ajax-Request) or via normal request (without the format parameter), the correct controller is executed automatically and the response is prepared accordingly.