# Save tags to articles (belongsToMany) with joinData

**URL:** <https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383>\
**Category:** Need Help\
**Created:** [July 17, 2019, 8:41am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383 "2019-07-17T08:41:31Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [July 17, 2019, 8:41am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/1 "2019-07-17T08:41:31Z")

</div>

Hello there!

I have a TagTable with a “through-table” called TagsRelations. TagsTable holds all tags (title and type), TagsRelations the primary-keys of the subjects (tag\_id, subject\_id) and the section that describes, what has been tagged (a file, an article etc…). So TagsRelations.section must not be null.

When I add a new article and select two tags, the tags will be recognized but the tagsrelation with “section” (e.g. “articles” in that case) will not be filled. How can I say the view or the controller (or maybe even the table), that the “section”-column should be filled with “articles”?

My form in **add.ctp** looks like this (regarding the tags)

```auto
    echo $this->Form->control('tags._ids', [
        'type' => 'select',
        'multiple' => true,
        'options' => $tags,
    ]);

```

I also tried to add in the controller a foreach-loop that adds a “\_joinData”-array to the post-data.

**In short:** How to add joinData using a select-control in a belongsToMany-Relationship (or how do it otherwise), when we don’t know before, how many items (= tags) will be selected.

Thanks in advance.

---

<div class="post-metadata">

**Author:** ![kailashdesk](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/kailashdesk/32/1493_2.png) [@kailashdesk](https://discourse.cakephp.org/u/kailashdesk)\
**Post date:** [July 17, 2019, 9:07am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/2 "2019-07-17T09:07:36Z")

</div>

In TagsRelations table, have you provided the default value to the not null type field (section)?  
As whenever we make a column as not null, it will always ask for any value so in that case we can set a default value so that we can skip to fill it at the first time when we don’t know exactly what to fill. Please let me know if that make sense.

---

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [July 17, 2019, 9:21am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/3 "2019-07-17T09:21:51Z")

</div>

No problem, thanks. Here we go:

tags  
id | title

tags\_relations  
id | tag\_id | foreign\_key | section

articles  
id | title | content

When tags “joke” and “bestof” have ids 23 and 24 and article “bestjoke” has id 10, then tags\_relations should have two rows like that  
1 | 23 | 10 | article  
2 | 24 | 10 | article

And for the sake of completeness, here are my Table-Classes:

ArticlesTable

```auto
    $this->belongsToMany('Tags', [
            'foreignKey' => 'foreign_key',
            'through' => 'tags_relations',
        ]);//->setConditions(['tags_relations.section' => 'articles']);

```

TagsTable

```auto
    $this->hasMany('TagsRelations', [
            'foreignKey' => 'tag_id'
        ]);

```

In the meantime, I try to modify post-data in the beforeMarshal-event to add \_joinData with section = ‘articles’.

Problem is, that I can’t add extra-data to tags\_relations (here the section value of “articles”)

Thanks

---

<div class="post-metadata">

**Author:** ![kailashdesk](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/kailashdesk/32/1493_2.png) [@kailashdesk](https://discourse.cakephp.org/u/kailashdesk)\
**Post date:** [July 17, 2019, 9:43am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/4 "2019-07-17T09:43:24Z")

</div>

I think, now I get what is the actual problem. I am explaining my understanding of the problem:

When we create a new article, there can be two cases like below:

1. A new article may not have a tag, then we need to save it into the articles table only and not in the tags\_relations table. This is the simple case where we don’t need to provide any section value.

2. When a user select tags for a new article then he must provide a value for the sections so that we can save it into the tags\_relations table as well.

Am I correct?

---

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [July 17, 2019, 9:52am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/5 "2019-07-17T09:52:38Z")

</div>

You’re almost fully correct. I want to tag several subjects (e.g. articles, pictures, files…). Since tagging is always the same, I have one “base-table” called “tags” and one for the relations between tags and the user-content.

To keep track (or to describe the “foreign\_key” value of the tags\_relations-Table) I store wherefrom the “foreign\_key”-value comes from. When it comes from the Articles-Backend, I store “articles” to the relations and then I know, that this specific forein\_key is an article\_id. Then, I can work with that. Otherwise, I / cakephp would not know, what subject is behind that specific forein\_key. So it is just a mechanism for reusing the tag-system.

And this is why, your cases are correct with the speciality, that not the user should enter the “section” for each tag. But instead the backend should automatically add/detect/whatever which “section” needs to be applied.

So when we stick to the articles-example, the user should get all tags available while creating a new article, select these tags he want’s to add and the backend stores “section = articles” to the tags\_relations table.  
When loading an article, all TagsRelations.Tags with section = “article” should be retrieved and the result should be the tags of the article.

So, you are almost correct. Thanks

---

<div class="post-metadata">

**Author:** ![kailashdesk](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/kailashdesk/32/1493_2.png) [@kailashdesk](https://discourse.cakephp.org/u/kailashdesk)\
**Post date:** [July 17, 2019, 10:14am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/6 "2019-07-17T10:14:09Z")

</div>

If you are using different forms for each user content then probably a hidden field can be added for each form like a hidden form with name section having value as “article” for Article form and “file” for File form etc.

---

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [July 17, 2019, 10:16am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/7 "2019-07-17T10:16:12Z")

</div>

This is what I also thought of, but I don’t get this hidden value to the entity data. I think, the associations are messed up or I did an other mistake.

---

<div class="post-metadata">

**Author:** ![kailashdesk](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/kailashdesk/32/1493_2.png) [@kailashdesk](https://discourse.cakephp.org/u/kailashdesk)\
**Post date:** [July 17, 2019, 10:30am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/8 "2019-07-17T10:30:29Z")

</div>

Hi @TheMiller,

Can you check in the Entity **TagsRelation.php** file if the value for this field “ **section** ” is set to **True** in **$\_accessible** properties? If not, then make it True to get it accessible like below:

protected $\_accessible = [  
‘id’ =\> true,  
‘tag\_id’=\>true,  
‘foreign\_key’=\>true,  
**’section’=\>true**  
];

---

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [July 17, 2019, 10:39am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/9 "2019-07-17T10:39:14Z")

</div>

Good point. But all fields were accessible via `"*" => true`, but I changed it like you suggested. But without any luck.

---

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [July 17, 2019, 11:05am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/10 "2019-07-17T11:05:46Z")

</div>

I think this is the problem (took from here [creating-inputs-for-associated-data](https://book.cakephp.org/3.0/en/views/helpers/form.html#creating-inputs-for-associated-data) (last paragraph).

> // Multiple select element for belongsToMany  
> // Does not support \_joinData

But how to workaround? Since we don’t know before, how many tags the user will add, we can’t work with

```auto
echo $this->Form->control('tags.0._joinData.section');

```

So the only way seems to modify the requestData at beforeMarshal-Event (or sth. like this). Am I correct?

---

<div class="post-metadata">

**Author:** ![kailashdesk](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/kailashdesk/32/1493_2.png) [@kailashdesk](https://discourse.cakephp.org/u/kailashdesk)\
**Post date:** [July 17, 2019, 11:16am UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/11 "2019-07-17T11:16:39Z")

</div>

Yes, you are correct. You need to modify the requestData.

---

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [July 17, 2019, 12:11pm UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/12 "2019-07-17T12:11:27Z")

</div>

Thanks for your help. I finally came up with this solution (maybe not the only solution and maybe not the best). Maybe you (or sb. else) will have a look on it. In the beforeMarshal of the ArticleTable, I modify the requestData expected (as per the docs/book says). But I have to set the foreign\_key to a dummy value, since the validator says it is required. This foreign\_key-column will automatically be replaced with the correct id lateron. The user\_id as grabbed from the articles entity.

**ArticlesTable.php**

```auto
public function beforeMarshal($event, $data, $options)
    {
        foreach ($data as $key => $value) {
            if($key == "tags" && is_array($value)) {
                foreach ($value as $idx => $tag_id) {
                    $tag = [
                        'id' => (int)$tag_id,
                        '_joinData' => [
                            'foreign_key' => -1,
                            'user_id' => $data['user_id'],
                            'section' => 'articles'
                        ]
                    ];
                    $tags[] = $tag;
                }
                $data['tags'] = $tags;
            }
        }
    }

```

Is that correct / a good approach / just a workaround? Thanks !

---

<div class="post-metadata">

**Author:** ![dereuromark](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/dereuromark/32/37_2.png) [@dereuromark](https://discourse.cakephp.org/u/dereuromark)\
**Post date:** [August 8, 2019, 2:38pm UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/13 "2019-08-08T14:38:31Z")

</div>

I recommend looking into [plugins, e.g. Tags](https://github.com/dereuromark/cakephp-tags) that encapsulate lots of the logic to keep the application code simple and future proof. Ideally they are agnostic and configurable enough to provide support for many use cases, including yours here. They also, based on community contributions, often provide more complete and correct functionality.  
Basically always consult the [https://github.com/FriendsOfCake/awesome-cakephp](https://github.com/FriendsOfCake/awesome-cakephp) list when you are looking for some functionality.

---

<div class="post-metadata">

**Author:** ![TheMiller](https://avatars.discourse-cdn.com/v4/letter/t/3ec8ea/32.png) [@TheMiller](https://discourse.cakephp.org/u/TheMiller)\
**Post date:** [August 27, 2019, 1:58pm UTC](https://discourse.cakephp.org/t/save-tags-to-articles-belongstomany-with-joindata/6383/14 "2019-08-27T13:58:29Z")

</div>

Ah, sorry - I didn’t notice your answer. Many thanks, I’ll look at this plugin. On the one hand my solution/work was good for learning and understanding. But on the other hand (and after learning), it might be a wise decision to rely on plugins.

Thanks and sorry for the delay 😉
