First of all, I suspect this issue is closely related to the issue I describe below.
I want to delete records in a many-to-many relationship but I'm not able to. Here's the edit action of my article controller:
$article = Article::findFirst($id);
$article->title = $this->request->getPost('title');
$article->content = $this->request->getPost('content');
$article->sticky = $this->request->getPost('sticky') ?: 0;
$article->type = $this->request->getPost('type');
$tags = array();
foreach (preg_split('/\s*,\s*/', $this->request->getPost('tag_names')) as $tag_name) {
$tag = Tag::findFirstByName($tag_name);
if (!$tag) {
$tag = new Tag();
$tag->name = $tag_name;
}
$tags[] = $tag;
}
$article->tags = $tags;
$success = $article->save();
The problem is that it appends to the tags collection instead of overwriting it. For example, say I had the following article in the database:
+----+--------------+---------+------------+-------------------+--------+--------+
| id | title | user_id | timestamp | content | sticky | type |
+----+--------------+---------+------------+-------------------+--------+--------+
| 1 | Hello World | 1 | 1419790166 | Hello world! | 1 | news |
+----+--------------+---------+------------+-------------------+--------+--------+
And the tags:
+----+---------+
| id | name |
+----+---------+
| 1 | Hello |
| 2 | World |
+----+---------+
And the article_tags:
+----+------------+--------+
| id | article_id | tag_id |
+----+------------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
+----+------------+--------+
If the post value for tag_names is "Test, Testing", it should create two new tags, resulting in the tags:
+----+---------+
| id | name |
+----+---------+
| 1 | Hello |
| 2 | World |
| 3 | Test |
| 4 | Testing |
+----+---------+
And the article_tags:
+----+------------+--------+
| id | article_id | tag_id |
+----+------------+--------+
| 3 | 1 | 3 |
| 4 | 1 | 4 |
+----+------------+--------+
In other words, it should delete the previous records from the join table and create two new article_tags.
The first part works great - the new tags are created - but the second part does not. It creates two new article_tags, but it doesn't delete the previous records. So I end up with the article_tags:
+----+------------+--------+
| id | article_id | tag_id |
+----+------------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 1 | 4 |
+----+------------+--------+
After reading other posts regarding this problem, as well as the GitHub issue referenced above, I tried to delete the tags before the save:
$article->tags->delete();
$article->tags = $tags;
$success = $article->save();
But this throws a foreign key constaint error:
PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`phalconsite`.`article_tag`, CONSTRAINT `article_tag_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tag` (`id`))' in /var/www/phalconsite/app/controllers/ArticleController.php...
Apparently tags->delete()
deletes the tags themselves and not the records from the join table. This seems broken to me.
One user had something like this, which I tried next:
$article->articleTags->delete();
$article->tags = $tags;
$success = $article->save();
The problem with this is I don't have an articleTags
property in that class, nor do I think I should. (Correct me if I'm wrong.)
What am I supposed to do?
(Anyone interested in the models and/or schema can look at my previous post.)