user1555112
user1555112

Reputation: 1987

Cakephp3: How can I return json data?

I am having a ajax post call to a cakePhp Controller:

$.ajax({
                type: "POST",
                url: 'locations/add',
                data: {
                  abbreviation: $(jqInputs[0]).val(),
                  description: $(jqInputs[1]).val()
                },
                success: function (response) {
                    if(response.status === "success") {
                        // do something with response.message or whatever other data on success
                        console.log('success');
                    } else if(response.status === "error") {
                        // do something with response.message or whatever other data on error
                        console.log('error');
                    }
                }
            });

When I try this I get the following error message:

Controller actions can only return Cake\Network\Response or null.

Within the AppController I have this

$this->loadComponent('RequestHandler');

enabled.

the Controller function looks like this:

public function add()
{
    $this->autoRender = false; // avoid to render view

    $location = $this->Locations->newEntity();
    if ($this->request->is('post')) {
        $location = $this->Locations->patchEntity($location, $this->request->data);
        if ($this->Locations->save($location)) {
            //$this->Flash->success(__('The location has been saved.'));
            //return $this->redirect(['action' => 'index']);
            return json_encode(array('result' => 'success'));
        } else {
            //$this->Flash->error(__('The location could not be saved. Please, try again.'));
            return json_encode(array('result' => 'error'));
        }
    }
    $this->set(compact('location'));
    $this->set('_serialize', ['location']);
}

What do I miss here? Is there any additional settings needed?

Upvotes: 14

Views: 31395

Answers (8)

sh6210
sh6210

Reputation: 4540

Though I'm not a CakePHP Guru, in my case i'm using cake > 4 and I need some results by ajax call. For this, from my controller i wrote,

echo json_encode(Dashboard::recentDealers()); die;

and in my JS file i just need to parse the data using

JSON.parse(data)

The ajax call like

 $.get('/recent-dealers', function (data, status) {
   console.log (JSON.parse(data)); });
});

Upvotes: 0

Hamfri
Hamfri

Reputation: 2219

As of cakePHP 4.x.x the following should work assuming that your controller and routes are set as shown below: controller: <your_project_name>/src/Controller/StudentsController.php

public function index()
    {
        $students = $this->Students->find('all');
        $this->set(compact('students'));
        $this->viewBuilder()->setOption('serialize',['students']);
    }

Routes: <your_project_name>/config/routes.php

<?php

use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;

/** @var \Cake\Routing\RouteBuilder $routes */
$routes->setRouteClass(DashedRoute::class);

$routes->scope('/', function (RouteBuilder $builder) {
 
    $builder->setExtensions(['json']);
    $builder->resources('Students');
    $builder->fallbacks();
});

Run bin/cake server and visit http://localhost:8765/students.json using postman/insomnia or just the normal browser. See further documentation for setting up Restful controllers and Restful Routing

Don't forget to set the method to GET on postman and insomnia.

Upvotes: 0

ᴍᴇʜᴏᴠ
ᴍᴇʜᴏᴠ

Reputation: 5276

Most answers I've seen here are either outdated, overloaded with unnecessary information, or rely on withBody(), which feels workaround-ish and not a CakePHP way.

Here's what worked for me instead:

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

$this->set([
    'my_response' => $my_results,
    '_serialize' => 'my_response',
]);
$this->RequestHandler->renderAs($this, 'json');

More info on RequestHandler. Seemingly it's not getting deprecated anytime soon.

UPDATE: CakePHP 4

$this->set(['my_response' => $my_results]);
$this->viewBuilder()->setOption('serialize', true);
$this->RequestHandler->renderAs($this, 'json');

More info

Upvotes: 21

beta-developper
beta-developper

Reputation: 1774

Instead of returning the json_encode result, set the response body with that result and return it back.

public function add()
{
    $this->autoRender = false; // avoid to render view

    $location = $this->Locations->newEntity();
    if ($this->request->is('post')) {
        $location = $this->Locations->patchEntity($location, $this->request->data);
        if ($this->Locations->save($location)) {
            //$this->Flash->success(__('The location has been saved.'));
            //return $this->redirect(['action' => 'index']);
            $resultJ = json_encode(array('result' => 'success'));
            $this->response->type('json');
            $this->response->body($resultJ);
            return $this->response;
        } else {
            //$this->Flash->error(__('The location could not be saved. Please, try again.'));
            $resultJ = json_encode(array('result' => 'error', 'errors' => $location->errors()));

            $this->response->type('json');
            $this->response->body($resultJ);
            return $this->response;
        }
    }
    $this->set(compact('location'));
    $this->set('_serialize', ['location']);
}

Edit (credit to @Warren Sergent)

Since CakePHP 3.4, we should use

return $this->response->withType("application/json")->withStringBody(json_encode($result));

Instead of :

$this->response->type('json');
$this->response->body($resultJ);
return $this->response;

CakePHP Documentation

Upvotes: 22

user6811107
user6811107

Reputation:

In the latest version of CakePHP $this->response->type() and $this->response->body() are deprecated.

Instead you should use $this->response->withType() and $this->response->withStringBody()

E.g:

(this was pinched from the accepted answer)

if ($this->request->is('post')) {
    $location = $this->Locations->patchEntity($location, $this->request->data);
    if ($this->Locations->save($location)) {
        //$this->Flash->success(__('The location has been saved.'));
        //return $this->redirect(['action' => 'index']);
        $resultJ = json_encode(array('result' => 'success'));

        $this->response = $this->response
            ->withType('application/json') // Here
            ->withStringBody($resultJ)     // and here

        return $this->response;
    }
}

Relevant Documentation

Upvotes: 2

AndreyP
AndreyP

Reputation: 2688

RequestHandler is not required to send json. In controller's action:

$this->viewBuilder()->setClassName('Json');
$result = ['result' => $success ? 'success' : 'error'];
$this->set($result);
$this->set('_serialize', array_keys($result));

Upvotes: 0

Faisal
Faisal

Reputation: 4765

When you return JSON data you need to define the data type and response body information like below:

$cardInformation = json_encode($cardData);
$this->response->type('json');
$this->response->body($cardInformation);
return $this->response;

In you case just change this return json_encode(array('result' => 'success')); line with below code:

$responseResult = json_encode(array('result' => 'success'));
$this->response->type('json');
$this->response->body($responseResult);
return $this->response;

Upvotes: 0

teran
teran

Reputation: 3234

there are few things to return JSON response:

  1. load RequestHandler component
  2. set rendering mode as json
  3. set content type
  4. set required data
  5. define _serialize value

for example you can move first 3 steps to some method in parent controller class:

protected function setJsonResponse(){
    $this->loadComponent('RequestHandler');
    $this->RequestHandler->renderAs($this, 'json');
    $this->response->type('application/json');
}

later in your controller you should call that method, and set required data;

if ($this->request->is('post')) {
    $location = $this->Locations->patchEntity($location, $this->request->data);

    $success = $this->Locations->save($location);

    $result = [ 'result' => $success ? 'success' : 'error' ];

    $this->setJsonResponse();
    $this->set(['result' => $result, '_serialize' => 'result']);
}

also it looks like you should also check for request->is('ajax); I'm not sure about returning json in case of GET request, so setJsonResponse method is called within if-post block;

in your ajax-call success handler you should check result field value:

success: function (response) {
             if(response.result == "success") {
                 console.log('success');
             } 
             else if(response.result === "error") {
                    console.log('error');
             }
         }

Upvotes: 8

Related Questions