0

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 did a test this way too, but the subcategory doesn't show any value:

<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>

Log JS: enter image description here

I appreciate any comment or example!

E. Biagi
  • 452
  • 3
  • 12
  • That's not a CakePHP-specific problem, hence why the documentation may not cover it. It's a JavaScript problem (perhaps Ajax, perhaps not, solutions exist both ways). CakePHP can be used to build the solution to that JavaScript problem, but you need to start with the answer to that. – Greg Schmidt Jun 11 '20 at 18:36
  • Exactly. Here is a solution that I tried to implement. But without success! I made an update to the code. I appreciate if you can see how it turned out. There was a route problem that is in the javascript. I think it's a start! -> https://sandbox.dereuromark.de/sandbox/ajax-examples/chained-dropdowns – E. Biagi Jun 11 '20 at 19:26
  • That's a very standard namespacing error. Either add a `use` declaration at the top of that file to tell it which `Router` you want, or give the full class path where you reference it. – Greg Schmidt Jun 11 '20 at 20:57
  • https://stackoverflow.com/questions/12769982/reference-what-does-this-error-mean-in-php – Greg Schmidt Jun 11 '20 at 20:57
  • Thank you. I made the correction, but the category and subcatheology filter does not occur. I made an update to the code. – E. Biagi Jun 12 '20 at 01:05
  • Your `var subCategories` is a JSON-encoded string, not a JavaScript array, so I don't think that your `var subCategoriesObject` will contain what you think it does. To debug JavaScript, get comfortable with `console.log()`, use it at every step in your function to see where its output agrees with what you're expecting, and where it goes wrong. – Greg Schmidt Jun 12 '20 at 01:36

1 Answers1

1

You don't have to fetch as list, you can work your way trough by just fetching categories with its subcategories and then build your HTML/JS from there.

Like this:

$categories = $this->Products->Categories->find('all')
->contain(['Subcategories']);

in HTML/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;
   }
}

?>

So you have now an array with all subcategories and the category id is used as a key.

<?php

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

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

Now Js

<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>

I haven't tested the code or anything but I have done this multiple times, you get the idea.

gagnav
  • 48
  • 7
  • Thanks for the comment, friend. I updated the code, but it doesn't return an error and it doesn't filter the subcategory from the category. – E. Biagi Jun 12 '20 at 01:06
  • Isn't `subCategories` now going to be a string? How do you array-reference that? Is there JSON magic that I'm not aware of? – Greg Schmidt Jun 12 '20 at 01:38
  • Yes it is, but it doesn't take a scientist to figure-out and remove the string quotations. As I have highlighted I have not tested it, so some minor syntax errors or some misspellings are possible. – gagnav Jun 12 '20 at 08:07
  • Remove string delimiters from var subCategories = "= json_encode($subcategories_list); ?>"; it should be var subCategories = = json_encode($subcategories_list); ?>; otherwise you get a string instead of an array so you cannot reference it it. – gagnav Jun 12 '20 at 08:25
  • Also remove the $subcategories_list from the subcategory dropdown, it should be empty as I have in the example: echo $this->Form->select('subcategory', ['options'=>[], 'empty'=>'...', 'id'=>'subcategory']); otherwise if you supply the $subcategories_list it will appear as it appears because you are supplying a multidimensional array for options of the select using form helper so it will be group in optgroup in the HTML output. So the second dropdown only should be populated by the JavaScript code when user selects the first drop down. – gagnav Jun 12 '20 at 08:38
  • Also I have edited my code so it will match your case, in the form helper selects you should have category_id and subcategory_id as field names not category and subcategory as you have now, your patchEntity method won't recognize the fields that way. – gagnav Jun 12 '20 at 09:27
  • Friend, I made the corrections but do not load the subcategories correctly. The categories are all listed. I also tried $this-> Form-> select ('subcategory', $subcategories_list,['id' => 'subcategory']); but without success. – E. Biagi Jun 12 '20 at 17:10
  • I updated the code. I also did a test with manual values, but it doesn't work. In all situations, do not load the subcategories. Only the category. – E. Biagi Jun 12 '20 at 19:04
  • I don't see that you have defined a hasMany relationship for subcategories in your CategoriesTable.php table class. Define that after that test if it works. If it doesn't, while changing the category drop down open your browsers inspector console what kind of JS errors do you get? – gagnav Jun 12 '20 at 21:11
  • The hasMany relationship is defined. Updated the js log. – E. Biagi Jun 12 '20 at 23:23
  • Sorted out. It was a jquery import problem. Something basic! But even then the subcategories do not appear! – E. Biagi Jun 13 '20 at 01:16