The shortest possible working version of this code is this as I tested this:
use Joomla\CMS\Factory; use Joomla\CMS\Helper\TagsHelper; function addTagsToArticle($ucmId, $articleId, $tags) { $tagsHelper = new TagsHelper(); $app = Factory::getApplication(); $factory = $app->bootComponent('com_content')->getMVCFactory(); $table = $factory->createTable('Article', 'Table'); $table->load($articleId); $tagsHelper->typeAlias = $table->typeAlias; $tagsHelper->tagItem($ucmId, $table, $tags, false); }
And you call this function like:
$newTags = ['#new#Millions', '#new#Worldwide']; $ucmId = 5; // core_content_id of the article from ucm_content table $itemID = 11; // article ID in #__content table addTagsToArticle($ucmId, $itemID, $newTags);
This way the code works and save new tags to an article. without the store() method at the end, it is not needed, and the tags has to be with #new# prefix in the array.
THE LONG WAY OF DOING THE SAME
I have tested some codes more deeply regarding tagging content items in Joomla 4. I am not sure that too many people want to tag articles/content programmatically and I do not think that there is any official way of doing that but this is one way of doing that. Someone really worked out a quite good and complex tagging system for Joomla and I just took some parts from that core code and you can use it in your own custom methods.
First I created a function createNewTags() using the Joomla4 core createTagsFromField() from the TagsHelper class. I used that because it is not worthy to create similar function and it is sure that it works well on saving new tags. If a tag was already in the tag table, then it does not save it again. However, we have to modify the core addTagMapping() method for our custom code.
use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Helper\TagsHelper; use Joomla\CMS\UCM\UCMType; use Joomla\Database\DatabaseDriver; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; /** * create new tags by array of strings (texts) * @param array $tags Array of tags to be applied. * * @return array -- created tags */ function createNewTags($tags = []) { $tagsHelper = new TagsHelper(); $new_tags = array_map(fn($tag) => "#new#$tag", $tags); // create tags first and save it $tags_ids = $tagsHelper->createTagsFromField($new_tags); return $tags_ids; } // we have to create our own custom function for mapping function addTagMapping($ucmId, $contentId, $table, $tags = []) { $db = $table->getDbo(); $item = $contentId; $ucm = new UCMType($table->typeAlias, $db); $typeId = $ucm->getTypeId(); // Prevent saving duplicate tags $tags = array_values(array_unique($tags)); if (!$tags) { return true; } $query = $db->getQuery(true); $query->insert('#__contentitem_tag_map'); $query->columns( [ $db->quoteName('core_content_id'), $db->quoteName('content_item_id'), $db->quoteName('tag_id'), $db->quoteName('type_id'), $db->quoteName('type_alias'), $db->quoteName('tag_date'), ] ); foreach ($tags as $tag) { $query->values( implode( ',', array_merge( $query->bindArray([(int) $ucmId, (int) $item, (int) $tag, (int) $typeId]), $query->bindArray([$table->typeAlias], ParameterType::STRING), [$query->currentTimestamp()] ) ) ); } $db->setQuery($query); return (bool) $db->execute(); } function tagThoseArticleItems($ucmId, $itemID, $ourTags = [], $replace = true) { $tagsHelper = new TagsHelper(); $tags = $tagsHelper->createNewTags($ourTags); $app = Factory::getApplication(); $factory = $app->bootComponent('com_content')->getMVCFactory(); $table = $factory->createTable('Article', 'Table'); $tagsHelper->typeAlias = $table->typeAlias; // $table->setTagsHelper($tagsHelper); // only if the table does not have it foreach ($tags as $tagId) { $tagsHelper->tagDeleteInstances($tagId); } // we have to use our own custom function on this // with added itemID (content item ID) to the call $result = addTagMapping($ucmId, $itemID, $table, $tags, $replace = true); return $result; }
And you call this whole thing like:
$ourTags = ['getty', 'Roma', 'New York']; // this is an existing article ID from the #__ucm_content table $ucmId = 5; $itemID = 11; // The article ID in #__content table $our_tags_created = tagThoseArticleItems($ucmId, $itemID, $ourTags, $replace = true);
The second function is just doing the tag mapping correctly. I hardcoded the content Table in this, but you can refactor this for using other tables too, if not content tagging.
Important to note that $ucmId is the core_content_id from the #__ucm_content table. so you should use that ID as the first argument and NOT a type like com_content.article and not the article ID from the #__content table. In our custom code we have to provide the article ID too from the content table.
The challenge
I tried but I have not found a short, straightforward solution on this task in Joomla on tagging content articles from an outside extension for example. Anybody else on this? The question is interesting because if someone came with a Wordpress developer background, rightfully would ask and look for a one line solution on this, like WP has on tagging a Post:
wp_set_post_tags( int $post_id, string|array $tags = '', bool $append = false )
Unfortunately at many of these small points we could realize the big difference on developer friendliness of the Content Management Systems.