Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving associated models in Cakephp 3

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!

like image 437
km27 Avatar asked May 07 '15 13:05

km27


2 Answers

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!

like image 119
km27 Avatar answered Nov 16 '22 02:11

km27


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.

like image 3
Wes King Avatar answered Nov 16 '22 02:11

Wes King