0

I have seen this: https://waltherlalk.com/blog/dynamic-form-input-fields and have been active in this: Dynamically add form field rows - cakePHP. I have reached the stage where the setup is as per the original tutorial with changes made as per the Stackoverflow post from monsur.hoq.

The form is working fine but, upon saving, it only saves the 'student' part of the data: nothing is sent to grades. The add part of my controller currently looks like this:

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

All code is as per bake or the tutorial shaped by the monsur.hoq post.

If anyone could help me to arrive at a working Cakephp3 example of the Walther Lalk tutorial I'd be very grateful.

The debugging toolbar shows the following SQL being produced on submitting the form:

INSERT INTO students (name, created, modified) 
VALUES 
  (
    'Test Two', '2016-09-13 16:04:07', 
    '2016-09-13 16:04:07'
  )    

All that serves to do is confirm the problem. Debugging in PHP Storm on form submission reveals the following:

$_POST = {array} [3]
 _method = "POST"
 name = "Test Four"
 Grade = {array} [1]
  0 = {array} [3]
   id = ""
   subject = "Maths"
   grade = "3"    

The add.ctp is as follows:

    <nav class="large-3 medium-4 columns" id="actions-sidebar">
    <ul class="side-nav">
        <li class="heading"><?= __('Actions') ?></li>
        <li><?= $this->Html->link(__('List Students'), ['action' => 'index']) ?></li>
        <li><?= $this->Html->link(__('List Grades'), ['controller' => 'Grades', 'action' => 'index']) ?></li>
        <li><?= $this->Html->link(__('New Grade'), ['controller' => 'Grades', 'action' => 'add']) ?></li>
    </ul>
</nav>
<div class="students form large-9 medium-8 columns content">
    <?= $this->Form->create($student) ?>
    <fieldset>
        <legend><?= __('Add Student') ?></legend>
        <?php
            echo $this->Form->input('name');
        ?>
    </fieldset>
    <fieldset>
        <legend><?php echo __('Grades');?></legend>
        <table id="grade-table">
            <thead>
            <tr>
                <th>Subject</th>
                <th>Grade achieved</th>
                <th>&nbsp;</th>
            </tr>
            </thead>
            <tbody></tbody>
            <tfoot>
            <tr>
                <td colspan="2"></td>
                <td class="actions">
                    <a href="#" class="add">Add grade</a>
                </td>
            </tr>
            </tfoot>
        </table>
    </fieldset>
    <script id="grade-template" type="text/x-underscore-template">
        <?php echo $this->element('grades');?>
    </script>
    <?= $this->Form->button(__('Submit')) ?>
    <?= $this->Form->end() ?>
</div>
<script>
    $(document).ready(function() {
        //I changed undescore default template settings
        _.templateSettings = {
            interpolate: /\{\{(.+?)\}\}/g
        }

        var
            gradeTable = $('#grade-table'),
            gradeBody = gradeTable.find('tbody'),
            gradeTemplate = _.template($('#grade-template').remove().text()),
            numberRows = gradeTable.find('tbody > tr').length;

        gradeTable
            .on('click', 'a.add', function(e) {
                e.preventDefault();

                $(gradeTemplate({key: numberRows++}))
                    .hide()
                    .appendTo(gradeBody)
                    .fadeIn('fast');
            })
            .on('click', 'a.remove', function(e) {
                e.preventDefault();

                $(this)
                    .closest('tr')
                    .fadeOut('fast', function() {
                        $(this).remove();
                    });
            });

        if (numberRows === 0) {
            gradeTable.find('a.add').click();
        }
    });
</script>
Community
  • 1
  • 1
pmelon
  • 187
  • 2
  • 11
  • add ```debug($student);```, ```debug($this->request->data);``` before patchEntity and ```debug($student);``` after patchEntity, post results here in your question; remove 'associated' from save options – Salines Sep 13 '16 at 19:32
  • I'll get that done tomorrow as soon as I can - I have to call it quits for today. Thanks for your help! – pmelon Sep 13 '16 at 19:53

1 Answers1

0

Change from CakePHP 2 to CakePHP 3 fields name conventions,

Grade.{$key}.grade to grades.{$key}.grade

Create View/Elements/grades.ctp file with the following contents. https://waltherlalk.com/blog/dynamic-form-input-fields

<?php
$key = isset($key) ? $key : '<%= key %>';
?>
<tr>
    <td>
        <?= $this->Form->hidden('grades.{$key}.id') ?>
        <?= $this->Form->text('grades.{$key}.subject'); ?>
    </td>   
    <td>
        <?= $this->Form->select("grades.{$key}.grade",
          [
            'A+',
            'A',
            'B+',
            'B',
            'C+',
            'C',
            'D',
            'E',
            'F'
          ],
          [
            'empty' => '-- Select grade --'
          ]); ?>
    </td>
    <td class="actions">
        <a href="#" class="remove">Remove grade</a>
    </td>
</tr>
Salines
  • 5,674
  • 3
  • 25
  • 50
  • Thanks for that - it didn't help, sorry to say. I'm still convinced it must be controller-related but cannot find where needs work. – pmelon Sep 13 '16 at 19:18