Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CakePHP saveAssociated not saving HasMany Through Model Data

I'm trying to get my associated models in CakePHP 2.3 to save properly, but I'm having issues. I'm storing posts, and I want to know what links are in those posts. For each of those links, I'd like to store anchor text if it is available. My database is set up as in the following diagram.

Database Diagram
(source: derekperkins.com)

Anchor Model

class Anchor extends AppModel {

        public $hasMany = array(
            'PostsUrl' => array(
                'className' => 'PostsUrl',
                'foreignKey' => 'anchor_id',
                'dependent' => false
            )
        );

        public function save($data = NULL, $validate = true, $fieldList = array()) {
            $id = Anchor::find('first', array(
                'fields' => array('id'),
                'recursive' => -1,
                'conditions' => array('anchor' => $data['anchor'])
            ));
            if( $id )
                $data['id'] = $id['Anchor']['id'];
            return parent::save($data, $validate, $fieldList);
        }
    }

URL Model

class Url extends AppModel {

    public $hasMany = array(
        'PostsUrl' => array(
            'className' => 'PostsUrl',
            'foreignKey' => 'url_id',
            'dependent' => false
        )
    );

    public function save($data = NULL, $validate = true, $fieldList = array()) {
        $id = Url::find('first', array(
            'fields' => array('id'),
            'recursive' => -1,
            'conditions' => array('url' => $data['url'])
        ));
        if( $id )
            $data['id'] = $id['Url']['id'];
        return parent::save($data, $validate, $fieldList);
    }
}

PostsUrl Model

class PostsUrl extends AppModel {

    public $belongsTo = array(
        'Post' => array(
            'className' => 'Post',
            'foreignKey' => 'post_id'
        ),
        'Url' => array(
            'className' => 'Url',
            'foreignKey' => 'url_id'
        'Anchor' => array(
            'className' => 'Url',
            'foreignKey' => 'anchor_id'
        )*/
    );
}

Post Model

class Post extends AppModel {

    public $hasMany = array(
        'PostsUrl' => array(
            'className' => 'PostsUrl',
            'foreignKey' => 'post_id',
            'dependent' => false
        )
    );


    public function save($data = NULL, $validate = true, $fieldList = array()) {
        $id = Post::find('first', array(
            'fields' => array('id'),
            'recursive' => -1,
            'conditions' => array('external_post_id' => $data['external_post_id'])
        ));
        if( $id )
            $data['id'] = $id['Post']['id'];

        return parent::save($data, $validate, $fieldList);
    }
}

Submitting Data

I've created a form to test my model. This is the code I'm using to save the array created by the form. I am getting a message saying that things saved successfully, but only the post saves. Nothing is entered into the other three tables. I'm also using DebugKit and no SQL calls reference any of that data.

$this->Post->saveAssociated($this->request->data, array('deep' => true))

Array
(
    [Post] => Array
        (
            [external_post_id] => 12345
            [sentiment_score] => 3.3
        )

    [URL] => Array
        (
            [url] => http://test.com
        )

    [Anchor] => Array
        (
            [anchor] => Test Anchor
        )
)

I've also tried formatting my arrays to have the URL and Anchor underneath PostsUrl as a subarray, but that didn't work either.

My Model::save functions are there to keep me from duplicating data, and they work properly in other models I have used in the past (though I'm open to suggestions on a better way to do this, as this uses a database call for each check). I've also tried commenting them out, and it doesn't affect my code. How should I structure this to save properly?

like image 538
Derek Perkins Avatar asked May 09 '13 17:05

Derek Perkins


1 Answers

First of all about your Model::save functions, for example:

public function save($data = NULL, $validate = true, $fieldList = array()) {
    $id = Post::find('first', array(
        'fields' => array('id'),
        'recursive' => -1,
        'conditions' => array('external_post_id' => $data['external_post_id'])
    ));
    if( $id )
        $data['id'] = $id['Post']['id'];

    pr($data);

    return parent::save($data, $validate, $fieldList);
}

it prints $data in this way :

Array
(
    [id] => 3 //for example 3
    [Post] => Array
        (
            [external_post_id] => 12345
            [sentiment_score] => 3.3
        )

    [URL] => Array
        (
            [url] => http://test.com
        )

    [Anchor] => Array
        (
            [anchor] => Test Anchor
        )
)

this $data is incorrect, the correct data is:

  Array
    (
        [Post] => Array
            (
                [id] => 3 //for example 3
                [external_post_id] => 12345
                [sentiment_score] => 3.3
            )

        [URL] => Array
            (
                [url] => http://test.com
            )

        [Anchor] => Array
            (
                [anchor] => Test Anchor
            )
    )

You must change your Model::save function this way:

public function save($data = NULL, $validate = true, $fieldList = array()) {
    $id = Post::find('first', array(
        'fields' => array('id'),
        'recursive' => -1,
        'conditions' => array('external_post_id' => $data['external_post_id'])
    ));
    if( $id )
        $data[$this->name]['id'] = $id['Post']['id'];

    return parent::save($data, $validate, $fieldList);
}

second , you can't save this data with single save, you should save your data this way:

$postData = array
(
    'Post' => array
        (
            'external_post_id' => 12345
            'sentiment_score' => 3.3
        )
);

$this->Post->save($data);

$postUrlId = $this->PostsUrl->find('first', array(
'conditions' => array(
                   'post_id' => $this->Post->id
                     ),
'fields' => array('id')
));

$urlAnchorData = array(
    'URL' => array
        (
            'url' => 'http://test.com'
        ),
    'Anchor' => array
        (
            'anchor' => 'Test Anchor'
        ),
    'PostsUrl' => array(
            'id' => $postUrlId['PostsUrl']['id']
     )
);

$this->PostsUrl->saveAll('$urlAnchorData');
like image 128
Arash Mousavi Avatar answered Oct 21 '22 22:10

Arash Mousavi