Despite my very rusty coding skills, I’ve been working to re-write an old CakePHP 2 application in CakePHP 4. I’m almost done – hurrah! – and while it’s not the prettiest code, it works. The only outstanding issue I’m having is writing the code for cascading dropdowns. Many years ago, I followed this brilliant tutorial, helped by the fact that the author (a real, bonafide developer – unlike me!) shared my frustration in trying to follow others available at the time.
Unfortunately, that doesn’t work anymore with CakePHP 4. Nor do the five others I would have linked to, except new forum users can only include two links in their posts. I’m aware that there’s an AJAX plugin but it’s much too complicated for simpletons like me, and the documentation and example code is also out-of-date for CakePHP 4.
I decided the best option might be to try and ‘upgrade’ the code in this very, very simple example that could be entirely idiot-proof, if only it worked. As such, I’ve spent the last week or so trying to do exactly that – but I’ve hit a wall. My code is currently as follows:
In my Countries controller:
public function initialize(): void {
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function select() {
$countries = $this->Countries->find('list');
$states = array();
foreach ($countries as $country_id => $name) {
$states = $this->Countries->States->findByCountryId($country_id);
break;
}
$this->set(compact('countries', 'states'));
}
public function stateslist() {
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException();
}
$id = $this->request->getQuery('id');
if (!$id) {
throw new NotFoundException();
}
$states = $this->Countries->States->find('list', array(
'conditions' => array('country_id' => $id)
));
$this->set(compact('states'));
}
And in my select.php (Countries) template:
<?= $this->Form->create() ?>
<fieldset>
<legend><?php echo __('Countries and States');?></legend>
<?php
$url = $this->Url->build(['controller' => 'countries', 'action' => 'states_list', 'ext' => 'json']);
$empty = count((array)$states) > 0 ? __('Please Select') : array('0' => __('No States Available'));
echo $this->Form->control('country_id', array('id' => 'countries', 'rel' => $url, 'empty' => 'Choose Country'));
echo $this->Form->control('state_id', array('id' => 'states', 'empty' => $empty));
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
<script type="text/JavaScript">
$(document).ready(function(){
//$("#states").attr("disabled","disabled");
$("#countries").change(function(){
//$("#states").attr("disabled","disabled");
$("#states").html("<option>Please wait...</option>");
var id = $("#countries option:selected").attr('value');
$.get("/countries/stateslist", {id:id}, function(data){
$("#states").removeAttr("disabled");
$("#states").html(data);
});
});
});
</script>
From what I can tell, the issue appears to be that the request isn’t being processed by the controller. I think it might be because I need to enable routing for the JSON extension, because right now, the request is being passed to the controller, but the controller throws an error because it can’t find a stateslist.php template (whereas, I think, it shouldn’t need one – it should process the AJAX as it is). Of course, there could be (and probably are) other issues with my hacked together code, but I’m tackling one problem at a time here.
Regrettably, I am at a total loss on how to do this in CakePHP 4. There is plenty of documentation on doing this in previous versions, and many answered questions on StackOverflow etc. (that often contradict one another, as so many things have been deprecated/replaced/upgraded over Cake versions). The bigger issue is that the CakePHP 4 cookbook also doesn’t appear to have been updated, because some the examples I try to follow (e.g. on the Routing and Views pages) no longer seem to be applicable.
With that in mind: is this my (only) problem, and could anyone please point me in the direction of how to fix it? A quick Google search shows that cascading/dependent dropdowns in CakePHP seem to have been a huge challenge for many people over the years. It would be ideal if I can get this code to become an idiot-proof example for others using CakePHP 4.