I am trying to take advantage of CakePHP's saveMany feature (with associated data feature), however am creating duplicate records. I think it is because the find() query is not finding authors, as the transaction has not yet been committed to the database.
This means that if there are two authors with the same username, for example, in the spreadsheet, then CakePHP will not associate the second with the first, but rather create two. I have made up some code for this post:
/*
* Foobar user (not in database) entered twice, whereas Existing user
* (in database) is associated
*/
$spreadsheet_rows = array(
array(
'title' => 'New post',
'author_username' => 'foobar',
'content' => 'New post'
),
array(
'title' => 'Another new post',
'author_username' => 'foobar',
'content' => 'Another new post'
),
array(
'title' => 'Third post',
'author_username' => 'Existing user',
'content' => 'Third post'
),
array(
'title' => 'Fourth post', // author_id in this case would be NULL
'content' => 'Third post'
),
);
$posts = array();
foreach ($spreadsheet_rows as $row) {
/*
* This query doesn't pick up the authors
* entered automatically (see comment 2.)
* within the db transaction by CakePHP,
* so creates duplicate author names
*/
$author = $this->Author->find('first', array('conditions' => array('Author.username' => $row['author_username'])));
$post = array(
'title' => $row['title'],
'content' => $row['content'],
);
/*
* Associate post to existing author
*/
if (!empty($author)) {
$post['author_id'] = $author['Author']['id'];
} else {
/*
* 2. CakePHP creates and automatically
* associates new author record if author_username is not blank
* (author_id is NULL in db if blank)
*/
if (!empty($ow['author_username'])) {
$post['Author']['username'] = $row['author_username'];
}
}
$posts[] = $post;
}
$this->Post->saveMany($posts, array('deep' => true));
Is there any way that this can be achieved, while also keeping transactions?
Update
You new requirement to save also posts that have no associated authors changes the situation a lot, as mentioned in the comments, CakePHPs model save methods are not ment to be able to save data from different models at once if it's not an association, if you need to do this in a transaction, then you'll need to handle this manually.
Save authors and their posts instead of posts and their authors
I would suggest that you save the data the other way around, that is save authors and their associated posts, that way you can easily take care of the duplicate users by simply grouping their data by using the username.
That way around CakePHP will create new authors only when neccessary, and add the appropriate foreign keys to the posts automatically.
The data should then be formatted like this:
And you would save via the
Author
model:Store non associated posts separately and make use of transactions manually
There is no way around this if you want to use the CakePHP ORM, just imagine what the raw SQL query would need to look like if it would need to handle all that logic.
So just split this into two saves, and use
DboSource::begin()/commit()/rollback()
manually to wrap it all up.An example
Here's a simple example based on your data, updated for your new requirements: