Problems with dynamic drop-down lists in cakephp 3

Friends, I have 3 tables: products, categories and subcategories. I am trying to register products and through two dependent drop-down lists on the form (categories and subcategories). That is, when selecting the category, the subcategories are loaded in the other list, but I’m having difficulties with that even consulting the documentation! I am grateful if anyone can point a way! I’ll summarize the fields!

Here is my table:


products: id, name, category_id, subcategory_id

categories: id, name

subcategories: id, name, category_id


I’m registering the new product like this:

 public function add()
    {
        $product = $this->Products->newEntity();
        if ($this->request->is('post')) {
            $product = $this->Products->patchEntity($product, $this->request->getData());
            if ($this->Products->save($product)) {
                $this->Flash->success(__('The produto has been saved.'));
                return $this->redirect('/admin/products/');
            }
            $this->Flash->error(__('The produto could not be saved. Please, try again.'));
        }
        $categories = $this->Products->Categories->find('all')->contain(['Subcategories']);    
        $subcategories = $this->Products->Subcategories->find('list', ['limit' => 200]);
        $this->set(compact('product', 'categories', 'subcategories'));
    }

ProductTable.php

.
.
$this->belongsTo('Categories', [
            'foreignKey' => 'category_id',
            'joinType' => 'INNER',
        ]);
        $this->belongsTo('Subcategories', [
            'foreignKey' => 'subcategory_id',
            'joinType' => 'INNER',
        ]);
.
.

CategoriesTable.php

.
$this->hasMany('Product', [
      'foreignKey' => 'category_id',
]);
.

SubcategoriesTable.php

.
 $this->belongsTo('Categories', [
            'foreignKey' => 'category_id',
            'joinType' => 'INNER',
]);
$this->hasMany('Product', [
            'foreignKey' => 'subcategory_id',
]);
.

Product => add.ctp

<php
$categories_list = [];
$subcategories_list = [];

foreach($categories as $category){

   $categories_list[$category->id] = $category->name; 

   foreach($category->subcategories as $subcategory){
     $subcategories_list[$category->id][$subcategory->id] = $subcategory->name;
   }
}

?>


<div class="produtos form large-9 medium-8 columns content">
    <?= $this->Form->create($product, ['class' => 'ajax_page']) ?>
    <fieldset>
        <legend><?= __('Add Produto') ?></legend>
        <?php

          echo $this->Form->select('category_id', ['options'=>$categories_list,'id'=>'category']);

         echo $this->Form->select('subcategory_id', ['options'=>[], 'id'=>'subcategory']);
?>
  
  </fieldset>
    <?= $this->Form->button(__('Submit')) ?>
    <?= $this->Form->end() ?>
</div>
<script>

var subCategories = <?= json_encode($subcategories_list);  ?>;

$(document).ready(function(){
         $('#category').change(function(){

  var categoryId = $(this).val();

  var subCategoriesObject = subCategories[categoryId];

  $('#subcategory option:gt(0)').remove();
  var subCategoriesSelect = $('#subcategory');

  $.each(subCategoriesObject, function(key,value) {
     subCategoriesSelect.append($("<option></option>").attr("value", key).text(value));
 });

});
});

</script>

With this update, there is no error, but it does not load the name of the subcategories.

Category:enter image description here

Subcategory: enter image description here

I appreciate any comment or example!

The error seems to be saying the path to the class Router can no be determined.

You probably need a use statement on the template file or a fully qualified reference to the class:

recommended

//add.ctp
use Cake\Routing\Router;

//more code here

or something like this

    ...
    $.ajax({
        dataType:'json',
        type: "POST",
        url: '<?php echo Cake\Routing\Router::url(array("controller" => "Products","prefix" => "admin", "action" => "add")); ?>' ,
        data: dataString,
        ...

Thank you. The error has been resolved. I made an update, but it does not filter the subcategory from the category. I believe that a detail is missing that I cannot see. Thanks if you can look at the code!

Possibly not the whole problem but:

Your two selects are not constructed properly.

You provide one string and one array argument:

echo $this->Form->select(
   'category', 
   ['options'=>$categories_list,'id'=>'category']
);
echo $this->Form->select(
   'subcategory', 
   ['options'=>$subcategories_list, 'id'=>'subcategory']
);

However, you should provide one string and two array arguments like this:

echo $this->Form->select(
   'category', 
   $categories_list, 
   ['id'=>'category']
);
echo $this->Form->select(
   'subcategory', 
   $subcategories_list, 
   ['id'=>'subcategory']
);

The way your code is currently written will prevent the select elements from getting the id attributes you are expecting so the javascript can’t bind the change function.

https://book.cakephp.org/3/en/views/helpers/form.html#creating-select-pickers

I did it the way you mentioned, but it loads all subcategories, regardless of the category you select. I put ‘options’ => [], but it also doesn’t load data.

This is the code I put in a template file to test. I don’t have your databases so I hand coded simple arrays when needed. This works form me

<script>
    $(document).ready(function () {
        var subCategories = {
            '0': [
                'cat0.0', 'cat0.1', 'cat0.2',
            ],
            '1': [
                'cat1.0', 'cat1.1', 'cat1.2',
            ],
            '2': [
                'cat2.0', 'cat2.1', 'cat2.2',
            ],
        };
        $('#category').change(function () {

            var categoryId = $(this).val();
            var subCategoriesObject = subCategories[categoryId];

            $('#subcategory option:gt(0)').remove();
            var subCategoriesSelect = $('#subcategory');

            $.each(subCategoriesObject, function (key, value) {
                subCategoriesSelect.append($("<option></option>").attr("value", key).text(value));
            });

        });
    });

</script>

<?php
echo $this->Form->create();
echo $this->Form->select(
    'category',
    [0  => 'cat0', 1 => 'cat1', 2 => 'cat2'],
    ['id' => 'category']);

echo $this->Form->select(
    'subcategory',
    [],
    ['id' => 'subcategory']
);
echo $this->Form->submit();
echo $this->Form->end();
?>

I do get a 1 entry hold-over that can be tweaked out of the code by changing the selects to:

    'category',
    ['cat0', 'cat1', 'cat2'],
    ['id' => 'category', 'empty' => 'Choose one']);

echo $this->Form->select(
    'subcategory',
    [],
    ['id' => 'subcategory', 'empty' => 'Choose one']
);

Likewise, subcategories do not carry values. They only appear in the category.

<?php
$categories_list = [];
$subcategories_list = [];

foreach($categories as $category){

   $categories_list[$category->id] = $category->name; 

   foreach($category->subcategories as $subcategory){
     $subcategories_list[$category->id][$subcategory->id] = $subcategory->name;
   }
}
?>             

<?= $this->Form->select('category_id', ['cat0', 'cat1', 'cat2'],['id' => 'categories', 'empty' => 'Choose one']);?>

<?= $this->Form->select('subcategory_id',[],['id' => 'subcategories', 'empty' => 'Choose one']);?>

<script>
    $(document).ready(function () {
        var subCategories = {
            '0': [
                'cat0.0', 'cat0.1', 'cat0.2',
            ],
            '1': [
                'cat1.0', 'cat1.1', 'cat1.2',
            ],
            '2': [
                'cat2.0', 'cat2.1', 'cat2.2',
            ],
        };
        $('#category').change(function () {

            var categoryId = $(this).val();
            var subCategoriesObject = subCategories[categoryId];

            $('#subcategory option:gt(0)').remove();
            var subCategoriesSelect = $('#subcategory');

            $.each(subCategoriesObject, function (key, value) {
                subCategoriesSelect.append($("<option></option>").attr("value", key).text(value));
            });

        });
    });

</script>

I don’t understand. Is there a question in your last post?

The code works like this for me:

Screen Shot 2020-06-12 at 12.20.50 PM Initial view

Screen Shot 2020-06-12 at 12.20.58 PM Initial values of Categories

Screen Shot 2020-06-12 at 12.21.14 PM Initial value of Subcategories

Screen Shot 2020-06-12 at 12.21.24 PM Subcategories after choosing Category 0

Screen Shot 2020-06-12 at 12.21.36 PM Subcategories after choosing Category 1

It’s very weird. I left the code clean. I removed everything that could be interfering. I believe it is something in js.

<!DOCTYPE html>
<html>
<body>

<?php
echo $this->Form->create();
echo $this->Form->select('category_id',[0  => 'cat0', 1 => 'cat1', 2 => 'cat2'],['id' => 'category']);

echo $this->Form->select('subcategory_id',[],['id' => 'subcategory']);

echo $this->Form->submit();
echo $this->Form->end();
?>

<script>
  $(document).ready(function () {
    var subCategories = {
      '0': [
      'cat0.0', 'cat0.1', 'cat0.2',
      ],
      '1': [
      'cat1.0', 'cat1.1', 'cat1.2',
      ],
      '2': [
      'cat2.0', 'cat2.1', 'cat2.2',
      ],
    };
    $('#category').change(function () {

      var categoryId = $(this).val();
      var subCategoriesObject = subCategories[categoryId];

      $('#subcategory option:gt(0)').remove();
      var subCategoriesSelect = $('#subcategory');

      $.each(subCategoriesObject, function (key, value) {
        subCategoriesSelect.append($("<option></option>").attr("value", key).text(value));
      });

    });
  });

</script>


</body>
</html>

Log JS:


Submenu options simply do not appear. I will review the code more carefully!

$ is not defined generally means you’re trying to use jQuery without actually having loaded jQuery.

I loaded jquery.

The error persists in this line:

.
$(document).ready(function () {
.
.

Sorted out. It was a jquery import problem. Something basic! I managed to make it work. I’ll try to make the adjustments and load the initial form because the subcategories still don’t appear!