Joomla - Controller task that returns JSON data
Asked Answered
S

7

9

I have the task run in my controller. I want it to return JSON data. As it stands, I am getting my JSON data wrapped inside the template HTML. How do I tell Joomla to just return JSON data from the controller? This is the function I have:

public function run  ( ) {

    JFactory::getDocument()->setMimeEncoding( 'application/json' );

    JResponse::setHeader('Content-Disposition','attachment;filename="progress-report-results.json"');

    JRequest::setVar('tmpl','component');

    $data = array(
        'foo' => 'bar'
    );

    echo json_encode( $data );

}

And this returns:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb" lang="en-gb" dir="ltr">
...
</head>
<body class="contentpane">

<div id="system-message-container">
</div>
    {"foo":"bar"}
</body>
</html>

I would like to get:

{"foo":"bar"}
Schell answered 24/5, 2013 at 16:40 Comment(0)
U
17

You don't need to build a special JSON view (view.json.php; or controller progressreports.json.php) to achieve that. The only thing you have to do is to echo the JSON string and close the application.

public function run( )
{
    JFactory::getDocument()->setMimeEncoding( 'application/json' );
    JResponse::setHeader('Content-Disposition','attachment;filename="progress-report-results.json"');

    $data = array(
        'foo' => 'bar'
    );
    echo json_encode( $data );
    JFactory::getApplication()->close(); // or jexit();
}

You only need a separate view (or controller), if you want to provide the same function with both HTML and JSON output (chosen by the caller).

Unspeakable answered 25/5, 2013 at 17:42 Comment(1)
This answer is from 2013 and did not work for me. Instead I used inf3rno's solution, which works fineConstituency
T
5

Joomla 4

You have to add the parameter &format=json to your URL. This tells the system that you are waiting for a json response. The system will render JsonDocument and will send correct browser headers as response.

index.php?option=com_foo&task=report.run&format=json
class Report extends BaseController {

   public function run() {
       $data = [
         'foo' => 'bar'
       ];

       $response = new ResponseJson($data);

       echo $response;
   }
}

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

If you close the application you will miss lot of stuff during the process of rendering. Many of the events will not be triggered. Furthermore, you will have to send headers for content type manually.

Your code should look like this one. This approach is not recommend.

class Report extends BaseController {

   public function run() {

       $this->app->mimeType = 'application/json';
       $this->app->setHeader('Content-Type', $this->app->mimeType . '; charset=' . $this->app->charSet);
       $this->app->sendHeaders();

       $data = [
         'foo' => 'bar'
       ];

       $response = new ResponseJson($data);

       echo $response;

       $this->app->close();
   }
}
Titania answered 19/12, 2020 at 19:48 Comment(0)
S
4

Quick method in existing controller

You need to use jexit() to return json data without any Joomla output.

public function run  ( ) {

    $data = array(
        'foo' => 'bar'
    );

    echo json_encode( $data );
    jexit();

}

Joomla 3.x Method

The better alternative is to create JSON controller and use JResponseJson to output JSON. The file name should have JSON suffix. For example, if your controller is an item, then your file name can be item.json.php and you can add controller code something like below.

public function run  ( ) {
  $data = array(
    'foo' => 'bar'
  );
  echo new JResponseJson($data);
}

The output will be json like below.

{"success":true,"message":null,"messages":null,"data":["foo": "bar"]}

Your ajax URL should have json parameter to trigger this json controller.

index.php?option=com_mycomponent&task=item.run&format=json

Use JResponseJson to inform errors and messages as well. Read complete documentation at the below location.

https://docs.joomla.org/JSON_Responses_with_JResponseJson

Skeen answered 24/5, 2013 at 19:31 Comment(0)
S
2

Got the answer.

I needed to make a new controller. In my case the original controller was called, progressreports.php - I made a new controller called progressreports.raw.php.

Then when calling the url, add format=raw. i.e.

index.php?option=com_foo&task=progressreports.run&format=raw
Schell answered 24/5, 2013 at 16:55 Comment(1)
There's a few ways to do it. What you've done is one way and Nagarjun pointed out something you should ensure you do for JSON requests. You can also use a progressreports.json.php controller and change your request to format=json. At the end of the day, so long as it's working for you, then you're good to go.Libel
B
1

Depending on what you are doing you may want to make a json document, whatever.json.php instead of whatever.html.php.

Bullfinch answered 25/5, 2013 at 0:49 Comment(0)
P
1

I checked the accepted answer, it did not work with joomla 3.4.3. If somebody has the same problem, here is the solution for lower joomla versions:

$data = array(
    'foo' => 'bar'
);
header('content-type: application/json; charset=utf-8');
echo json_encode($data);
JFactory::getApplication()->close();

The content-disposition header is needed only if you want to offer the file for download.

off: I just want to end this project and drink somethink to forget it. Even starting from scratch and writing a new CMS would be less painful. :S

Price answered 4/3, 2017 at 3:40 Comment(0)
H
0

I do not like to much JResponseJson and also I like to enqueue messages for the next request, so I made the following method on the main controller of my component:

public function returnJson($data)
{
    $app = Factory::getApplication();

    // Persist messages if they exist. (Same as CMSApplication->redirect)
    $session = \JFactory::getSession();
    $session->set('application.queue', $app->getMessageQueue());

    // Send json mime type.
    $app->mimeType = 'application/json';
    $app->setHeader('Content-Type', $app->mimeType . '; charset=' . $app->charSet);
    $app->sendHeaders();

    echo json_encode($data);

    $app->close();
}

In any task of the controller or sub-controller you could now do the following:

public function sampleTask()
{
    $app = Factory::getApplication();
    $app->enqueueMessage("Done (for the next user's request)", 'success');
    $this->returnJson("json data");
}
Hoarhound answered 27/5, 2018 at 15:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.