6
votes

I have a form that collects data about an Article, and I want to save that data, as well as for a model called Abstract, where an Article hasMany Abstracts. My models look like this:

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class AbstractsTable extends Table
{
public function initialize(array $config)
{
    $this->belongsTo('Articles');
}

public function validationDefault(Validator $validator)
{
    $validator
        ->notEmpty('body');
    return $validator;
}
}

And

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('Timestamp');
        $this->hasMany('Abstracts');
    }

public function validationDefault(Validator $validator)
    {
        $validator  ->notEmpty('category')
        return $validator;
    }
}

My input form has a field named 'abstracts.body', and in my ArticlesController I have this function:

  public function add()
  {
          $data = $this->request->data;
          $article = $this->Articles->newEntity($data, [
                            'associated' => ['Abstracts']
                    ]);
         if ($this->request->is('post')) {
                  $article->user_id = $this->Auth->user('id');
                  $data['abstracts']['user_id'] = $article->user_id;
                  $data['abstracts']['approved'] = 0;
                  $article = $this->Articles->patchEntity($article, $data, [ 
                                'associated' => ['Abstracts']
                          ]);

            if ($this->Articles->save($article, [ 'validate' => false,
                                                   'associated' => ['Abstracts']
                                                 ]) )
                 {
                         $this->Flash->success(__('Your article has been saved.'));
                         return $this->redirect(['action' => 'index']);
                 }
          $this->Flash->error(__('Unable to add your article.'));
          }
          $this->set('article', $article);
  }

My Abstracts table is pretty straightforward:

CREATE TABLE 'abstracts' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'article_id' INTEGER , 'user_id' INTEGER , 'body' TEXT, 'approved' BOOLEAN )

From debugging I can see that I have the correct 'abstracts' array within my $data (in add()), but it doesn't appear to ever try to save it to the database. Can someone please point out my error? Thanks!

2
show a debug((string)$article) before calling save, please - José Lorenzo Rodríguez
Saved my day :) The patchEntity() with the associated bit. - miCRoSCoPiC_eaRthLinG

2 Answers

4
votes

Got it.

I started going wrong here:

My input form has a field named 'abstracts.body'

Because it's a hasMany relationship, I need to have that input be 'abstracts.0.body'

Then the rest of LeWestopher's answer will work-- adding an index to the fields I want to fill in from the Controller, so $data[abstracts][0]['user_id'] => ... and so on. Thanks!

3
votes

You're post processing your $data['abstracts'] array incorrectly resulting in the association not saving. $data['abstracts'] is expected to be an array of Abstracts. Your issue lies here:

$data['abstracts']['user_id'] = $article->user_id;
$data['abstracts']['approved'] = 0;

You should be able to fix this pretty easily by changing this to:

foreach($data['abstracts'] as $index => $abstract) {
    $abstract['user_id'] = $article->user_id;
    $abstract['approved'] = 0;
    $data['abstracts'][$index] = $abstract;
}

This should correctly iterate over your array of abstracts, set the user_id and approved keys appropriately and then it should save correctly.

CakePHP 3.x Documentation on Saving Associations

EDIT: Very interesting issue indeed. Try it without using patchEntity, and use newEntity by itself instead:

public function add()
{

    if ($this->request->is('post')) {
        $data = $this->request->data;

        // Post process abstracts objects
        foreach($data['abstracts'] as $index => $abstract) {
            $abstract['user_id'] = $article->user_id;
            $abstract['approved'] = 0;
            $data['abstracts'][$index] = $abstract;
        }

        // Build newEntity
        $article = $this->Articles->newEntity($data, [
            'associated' => ['Abstracts']
        ]);

        // Save our entity with associations
        if ($this->Articles->save($article, [ 
            'validate' => false,
            'associated' => ['Abstracts']
        ])) {
            $this->Flash->success(__('Your article has been saved.'));
            return $this->redirect(['action' => 'index']);
        }

        // On save fail
        $this->Flash->error(__('Unable to add your article.'));
        $this->set('article', $article);
    }     
}    

EDIT 2: Your issue looks like it's definitely in your form helper. Your current form helper input creates an $data array that looks like this:

$data = [
    'abstracts' => [
        'body' => 'example text'
    ],
    'category' => 'Science'
];

Which SHOULD look like:

$data = [
    'abstracts' => [
        ['body' => 'example text'],
        ['body' => 'Im your second abstract'],
        ['body' => 'Abstract three!']
    ],
    'category' => 'Science'
];

The issue lies in:

abstracts.body

Which should read as (in array dot notation):

// abstracts.0.body
echo $this->Form->input('abstracts.0.body', [
    'label' => 'summary of article', 
    'maxlength' =>'440', 
    'rows' => '7'
]);

I believe that should be the last issue you run into.