Cascading dropdowns - the proper way in CakPHP 4.0

Does have anybody any example with properly coded cascading dorpdowns menus? I tried to search in the internet but I am only confused from different approaches. And yes, I do not want to use echo json_encode($states) in my controller code …

Thank you for any hint or examples.

Menus? Or form elements?

Thank you for answer. Just form elements …

So you’re looking for an Ajax solution where you make a selection in the first field, then the second one gets filled in with valid options based on that, and then the third one with valid options based on the first two, that sort of thing?

Which piece, specifically, are you having problems with? It’s a lot of pieces that have to come together to make this work, only a couple of which are in any way CakePHP-specific. There should be lots of tutorials out there about how to do this in general.

Yes, I am searching exactly for what you write. In fact, it is working on my site but the problem is, that I use

echo json_encode($workplaces);

in my controller. Somewhere at discourse you wrote that we should use

return $this->response
->withType(‘application/json’)
->withStringBody(json_encode($workplaces));

and never echo output in controller. Nevertheless, when I use this, my jquery ajax script does not work.

Here are few screenshots for easier orientation in this topic:

This is working page:

This is controller that works:

And this is my jquery code:

And when I change my controller to:

chained dropdowns at my page does not work:

This is corresponding Chrome debug tools output:

Could you help me to find what I am doing wrong? I found also working solution solution here - CakePHP Sandbox App: AjaxExamples but I do not understand it. It is too complicated for occasional programmer like me.

I am just seeking for CakePHP proper way. Once more thank you for your help.

I wonder if the addition of the application/json type header means that the browser is automatically recognizing that it’s a JSON payload and parsing it for you? Not at a good place right now to check that out, but it should be pretty easy for you. In your jQuery code, instead of trying to parse the result, just console.log(result);, and show us what that looks like in both of your controller scenarios.

When I use

echo json_encode($workplaces);

the console output contains the required data:

When I use

return $this->response
->withType(‘application/json’)
->withStringBody(json_encode($workplaces));

the console output gives error and 8 MB of structured data:

Trusty speaking, I do not know what does the error men, if in one case my code works and in other not.

This is what I did with my script:

Anybody who could help me?

Look at the response content in the network tab, not in your console. Also check your application’s error logs, you may be receiving an error page.

ps, the quotes around application/json look like ticks, which would be invalid and could certainly trigger an error.

Thank you for help but unfortunately, no difference. Still the same problem … Any other hint?

I am not really sure if this helps you but usually if you want to fetch JSON data from CakePHP you can follow these steps to leverage the JsonView provided by CakePHP:

  1. Tell CakePHP that you want JSON data back via adding dataType: 'json', to your jQuery $.ajax() config. So like
$.ajax( {
    type: 'GET',
    dataType: 'json',
    url: submit_url,
    headers: {
        'X-CSRF-Token': $( '[name="_csrfToken"]' ).val()
    },
   ... rest of the JSON ...
} );

With that CakePHP automatically enables the JsonView instead of the default AppView to not render HTML but instead JSON.

  1. Set the variables inside your controller which should be part of the JSON.
    public function ajaxView(?int $id = null): void
    {
        $someArray = ['hello', 'someothervalue'];
        $myBoolean = false;
        $someInteger = 1234;
        $this->set(compact('someArray', 'myBoolean', 'someInteger'));
        $this->viewBuilder()
            ->setOption('serialize', ['someArray', 'myBoolean', 'someInteger']);
    }

The important part here are the last 2 lines with $this->set() to set variables into the view (aka make them available to the template) as well as $this->viewBuilder()->setOption(); to tell CakePHP to serialize those variables inside the JSON.

This will result in a JSON being returned which looks like this:

{
    "someArray": [
        "hello",
        "someothervalue"
    ],
    "myBoolean": false,
    "someInteger": 1234
}

No need to

  • manually return a response object
  • manually encode json data because this automatically happens in the JsonView class (inside CakePHP)

Hopefully this helps you leverage the power of CakePHP even more :smile:

I gave you three hints to look at, what do you mean by “no difference”?

It means that I changed the quotes but it seems that the output is still not recognized by the jQuery script:

The response is nevertheless as expected, contains all date I need but the script does not load them into secondary dropdown.

Controller with echo of json data

echo json_encode($workplaces);

works well.

I will try to check my jQuery code one more time. The error must be within it.

This is my controller code with the first option hidden behind comments.

Hi all. Once more thank you very much @Zuluru, @ndm and @KevinPfeifer for your help. I do not why but the option with Serialize Key proposed by @KevinPfeifer and described in CakePHP Cookbook did not work. Response on ajax request was empty (at least according to Network tab Preview, subject_id was sent).

Nevertheless, I got working the version with

return $this->response
->withType(‘application/json’)
->withStringBody(json_encode($wplist));

The solution was finally simple and the error was in jQuery code. One has to use function JSON.stringify() before parsing the data with JSON.parse().

Here is final solution for anybody who could encounter same problem as me:

Controller code:

View template code and script:

Once more thank you all!

You do not have to use any of those JSON functions, result will already be a JavaScript object!

Your JS error was caused by passing that object to JSON.parse(), where it was implicitly being converted into its string representation, which is [object Object], and of course that’s not a valid JSON string that could be parsed.

Yes, you are right. It is really working!!! Thank you! This is new working code.