J3.x

Difference between revisions of "Using Tags in an Extension"

From Joomla! Documentation

(Several spelling, punctuation and grammar corrections.)
 
(50 intermediate revisions by 11 users not shown)
Line 1: Line 1:
Joomla's tagging system is used in all core content extensions and is designed to be be easy to integrate into other extensions that use standard Joomla design patterns.  
+
== Introduction ==
 +
Joomla's tagging system is used in all core content extensions and is designed to be easy to integrate into other extensions that use standard Joomla design patterns.  
  
Using tags in an extension is relatively straightforward but it requires small changes in a number of specific places.
+
Using tags in an extension is fairly straightforward. It requires these changes to a typical extension:
  
 +
* Register a ''Content type'' for the extension view(s)
 +
* Add ''Observer methods'' to the extension table class(es)
 +
* Add ''Tag fields'' to the extension edit forms
 +
* Add ''Tag display'' code to the extension view files
 +
* Optionally, add a batch tagging method to the extension administration
  
== Create a content type for each view ==
+
== Register a Content Type for Each View ==
Note: Types can be added using either sql or postflight by creating a JTableContenttype instance and adding a row.
 
  
First identify which tables contain data that will be tagged and what the name of the single item view displaying each of these tables. For example:
+
In Joomla! 3, content types (articles, weblinks, contacts and so on) are registered in the <tt>#__content_types</tt> table with a separate record for each content view.
* #__contact_details  is the table displayed in the view contact of component com_contact with table class ContactTableContact.
 
* #__weblinks is the table displayed in the view weblink of com_weblinks so WeblinksTableWeblink.  
 
  
 +
Your extension installer needs to create a new record for each view.
  
<source lang="sql">
+
Here is the structure of the <tt>#__content_types</tt> table. Each column is described in the following sections.
CREATE TABLE IF NOT EXISTS `#__content_types` (
+
{| class="wikitable"
  `type_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+
|-
  `type_title` varchar(255) NOT NULL DEFAULT '',
+
! Column name !! Column type !! Purpose
  `type_alias` varchar(255) NOT NULL DEFAULT '',
+
|-
  `table` varchar(255) NOT NULL DEFAULT '',
+
| <tt>type_id</tt> || int(10) || Record key
  `rules` text NOT NULL,
+
|-
  `field_mappings` text NOT NULL,
+
| <tt>type_title</tt>|| varchar(255)|| Type title e.g. <tt>Article</tt>
`router` varchar(255) NOT NULL DEFAULT '',
+
|-
+
| <tt>type_alias</tt> || varchar(255)|| Type alias e.g. <tt>com_content.article</tt>
</source>
+
|-
 +
| <tt>table</tt> || varchar(255)|| Information about the Table class
 +
|-
 +
| <tt>rules</tt> || text || Not currently used
 +
|-
 +
| <tt>field_mappings</tt> || text || Maps the table column names to standard Joomla! names
 +
|-
 +
| <tt>router</tt> || varchar(255)|| Optional: name of a router method
 +
|-
 +
| <tt>content_history_options</tt> || varchar(5120)|| Optional: ????
 +
|}
  
For each table/option/view you will need to make an entry in #__content_types.  You can do this either using sql or postflight by creating an instance of JTableContenttype. Don't forget a category type if you use the Joomla categories API.
+
===type_id===
 +
This is the auto-incremented record key, nothing to be done here.
  
A string giving component.view (that would be in the page request, typically matching the model name) goes in the **type_alias** field.
+
===type_title===
 +
The title for your Content Type. For example, Article, Contact, Weblink. 
 +
You can do this either using sql or postflight by creating an instance of JTableContenttype. Don't forget a category type if you use the Joomla categories API.
  
'''table''' gives the complete table class information for the table class stored as a JSON object in which the first element represents your "special" table and the second an optional common table (otherwise it will default the JTableCorecontent. This includes the name of the database table, the name of the primary key, the prefix, the name, an option array as used in your constructor and getInstance() methods. This enables the tagging system (and other APIs) to access your table easily.  
+
===type_alias===
 +
A string identifying the component and view. (That would be in the page request, typically matching the model name.) For example, <tt>com_contact.contact</tt>, <tt>com_weblinks.weblink</tt>.
  
If you are using Joomla categories make sure to create a category type so that they can be tagged. In Joomla 3.1 and 3.1.1 there is an error where the tag field will show even if there is not a type, but this will be corrected in 3.1.2. 
+
===table===
 
+
Identify which tables used by your component contain records to be tagged. For example, the ''Contact'' view of the Contacts component uses <tt>#__contact_details</tt>. Similarly the ''Weblinks'' view of the Weblinks component uses <tt>#__weblinks </tt>.
  
'''Important note''' : Please note that the table name for the common table is #__ucm_content. This is INCORRECT in 3.1 and 3.1.1 data but is not currently used.  The data will be updated in 3.1.2.
+
Each of these components uses a class to write to the table: <tt>ContactTableContact</tt> for the Contacts component and <tt>WeblinksTableWeblink</tt> for the Weblinks component. You will need the class prefixes e.g. <tt>ContactTable</tt>, <tt>WeblinksTable</tt>.
  
 +
Note that Category types all use the <tt>#__categories</tt> table.
  
 +
The <tt>type_table</tt> entry gives the complete table class information for the table class as a JSON object with two elements. The first element represents your "special" table and the second an optional common table. (Otherwise it will default to <tt>JTableCorecontent</tt>.)
  
<source lang="php">
+
Here is the JSON entry for the Weblink content type:
{"special":{"dbtable":"#__content","key":"id","type":"Content","prefix":"JTable","config":"array()"},"common":{"dbtable":"#__ucm_content","key":"ucm_id","type":"Corecontent","prefix":"JTable","config":"array()"}}
 
</source>
 
  
 +
<source lang="php">{
 +
  "special": {
 +
    "dbtable": "#__weblinks",
 +
    "key": "id",
 +
    "type": "Weblink",
 +
    "prefix": "WeblinksTable",
 +
    "config": "array()"
 +
  },
 +
  "common": {
 +
    "dbtable": "#__ucm_content",
 +
    "key": "ucm_id",
 +
    "type": "Corecontent",
 +
    "prefix": "JTable",
 +
    "config": "array()"
 +
  }
 +
}</source>
  
The '''type_title''' field would potentially be used for display although this is not implemented currently except in the contentttype field.  Usually it should begin with an upper case letter if it is in English. See note about how to make this translatable.  
+
The values to be included are:
 +
{| class="wikitable"
 +
|-
 +
! Entry name !! Content
 +
|-
 +
| <tt>dbtable</tt> || Table name
 +
|-
 +
| <tt>key</tt>|| Primary key name
 +
|-
 +
| <tt>type</tt> || Content type
 +
|-
 +
| <tt>prefix</tt> || Class prefix
 +
|-
 +
| <tt>config</tt> || An option array, as used in your component constructor and ''getInstance()'' methods. It may be empty.
 +
|}
  
note:
+
This information enables the tagging system (and other APIs) to access your table easily.  
To make your type names translatable add
 
<source lang="php">
 
COM_TAGS_CONTENT_TYPE_ + the type_title
 
</source>
 
in both the ini and sys.ini files.  
 
  
'''Rules'''  is currently not used. It will likely be removed in favor of an asset_id for each type, but currently you can ignore this field which will be managed by JTable.  
+
If you are using Joomla categories, be sure to create a category type so that they can be tagged. In Joomla 3.1 and 3.1.1, there is an error where the tag field will show even if there is not a type. This is corrected in Joomla 3.1.4. 
 +
 
 +
====Notes====
 +
* The table name for the common table is <tt>#__ucm_content</tt>. This is incorrect in Joomla 3.1 and 3.1.1 data but is not currently used. The data was updated in Joomla 3.1.4.
  
'''field_mappings''' maps the names of specific fields in your table to a set of standard names. This mapping is stored as a JSON array with the first element mapping to the common fields and the second one mapping the other fields from the table.
+
* The <tt>type_title</tt> field would potentially be used for display although this is not implemented currently except in the ''contenttype'' field. Usually it should begin with an upper case letter if it is in English. See the note about how to make this translatable. To make your type names translatable, add <tt>COM_TAGS_CONTENT_TYPE_+type_title="Type Title"</tt> in both the ''.ini'' and ''.sys.ini'' files.
  
'''Important note'''  The JHelperTags and JUcm APIS currently (at 3.1.1) support arrays for this field, but our intention is to change this to support either arrays or objects with a default of objects.  This is planned for implementation for 3.1.2.
+
===rules===
 +
Rules is currently not used. It will likely be removed in favor of an <tt>asset_id</tt> for each type. You can ignore this field which will be managed by <tt>JTable</tt>.
  
<source lang="php">
+
===field_mappings===
'{"common":[{"core_content_item_id":"id","core_title":"title","core_state":"published","core_alias":"alias","core_created_time":"created_time","core_modified_time":"modified_time","core_body":"description", "core_hits":"hits","core_publish_up":"null","core_publish_down":"null","core_access":"access", "core_params":"params", "core_featured":"null", "core_metadata":"metadata", "core_language":"language", "core_images":"null", "core_urls":"null", "core_version":"version", "core_ordering":"null", "core_metakey":"metakey", "core_metadesc":"metadesc", "core_catid":"parent_id", "core_xreference":"null", "asset_id":"asset_id"}], "special": [{"parent_id":"parent_id","lft":"lft","rgt":"rgt","level":"level","path":"path","extension":"extension","note":"note"}]}'
+
This entry maps the names of specific fields in your table to a set of standard Joomla! names. This mapping is stored as a JSON array with the first 'common' element mapping to the common fields and the second 'special' element mapping the other fields from the table.
</source>
 
  
is the field mapping for the Article type. Note that article uses the name attribs for the core field called params.  If your table does not contain a field, put “null” instead.  Leaving it blank may cause SQL issues. The special fields are optional.  At a minimum for common fields you need to map: content_item_id, alias and title in order to successfully create urls in the tagged items list.  You also will probably want: access, status, and language.
+
Here is the ''field_mappings'' entry for the Banner Content Type (chosen because it is a richer example than Article or WebLink). Note that 'common' mappings that have no equivalent are included as ''null'' (Leaving them blank may cause SQL issues.) The names and values in the 'special' element all match as they are limited to this component.
  
'''Router''' is an optional name of a static helper router method for this type found in its front end helpers folder.
+
<source lang="php">{
If you only store data in #__ucm_content you will eventually be able to leave the router field blank although this option is not currently implemented. If you do not have a custom router tags falls back to the rules found in JHelperRoute.
+
  "common": {
 +
    "core_content_item_id": "id",
 +
    "core_title": "name",
 +
    "core_state": "published",
 +
    "core_alias": "alias",
 +
    "core_created_time": "created",
 +
    "core_modified_time": "modified",
 +
    "core_body": "description",
 +
    "core_hits": "null",
 +
    "core_publish_up": "publish_up",
 +
    "core_publish_down": "publish_down",
 +
    "core_access": "access",
 +
    "core_params": "params",
 +
    "core_featured": "null",
 +
    "core_metadata": "metadata",
 +
    "core_language": "language",
 +
    "core_images": "images",
 +
    "core_urls": "link",
 +
    "core_version": "version",
 +
    "core_ordering": "ordering",
 +
    "core_metakey": "metakey",
 +
    "core_metadesc": "metadesc",
 +
    "core_catid": "catid",
 +
    "core_xreference": "null",
 +
    "asset_id": "null"
 +
  },
 +
  "special": {
 +
    "imptotal": "imptotal",
 +
    "impmade": "impmade",
 +
    "clicks": "clicks",
 +
    "clickurl": "clickurl",
 +
    "custombannercode": "custombannercode",
 +
    "cid": "cid",
 +
    "purchase_type": "purchase_type",
 +
    "track_impressions": "track_impressions",
 +
    "track_clicks": "track_clicks"
 +
  }
 +
}</source>
  
== Modify your component's table class (or classes if you have multiple tables) ==
+
At a minimum for common fields you need to map: content_item_id, alias and title in order to successfully create URLs in the tagged items list. You also will probably want: access, status, and language. The special fields are optional. 
  
This has several parts.
+
====Note====
 +
* The <tt>JHelperTags</tt> and JUcm APIs at Joomla 3.1.1 supported arrays for this field. As of Joomla 3.1.4, either arrays or objects are supported. The default is objects.
  
=== Add a property ===
+
===router===
 +
This is an optional entry to include the name of a static helper router method for this type found in its front end helpers folder. For example,  <tt>WeblinksHelperRoute::getWeblinkRoute</tt>.
  
<source lang="php">
+
If you do not have a custom router, Tags falls back to the rules in <tt>JHelperRoute</tt>.
/**
 
* Indicator that the tags have been changed
 
*
 
* @var    JHelperTags
 
* @since  3.1
 
*/
 
protected $tagsHelper = null;
 
</source>
 
This property helps to manage change in tags.
 
  
=== Modify your constructor ===
+
If you only store data in <tt>#__ucm_content</tt>, you will eventually be able to leave the router field blank although this option is not currently implemented.
  
Follow this example to modify your consructor which provides substantial reduction in duplicate code.
+
===content_history_options===
<source lang="php">
+
This section was added in Joomla! 3.2 to link to the Content History component.
$this->tagsHelper = new JHelperTags();
 
$this->tagsHelper->typeAlias = 'com_contact.contact';
 
</source>
 
  
=== Modify your store() method ===
+
Here's the entry for the Banner Content Type:
Management of tagging and associated data is largely handled through the store() method. This provides maximum flexibility for the handling of tags across many extensions.
 
  
If you don't have a store() method you will need to add one.  The assumption is that tables will inherit from JTable.  
+
<source lang="php">{
 +
  "formFile": "administrator\/components\/com_banners\/models\/forms\/banner.xml",
 +
  "hideFields": [
 +
    "checked_out",
 +
    "checked_out_time",
 +
    "version",
 +
    "reset"
 +
  ],
 +
  "ignoreChanges": [
 +
    "modified_by",
 +
    "modified",
 +
    "checked_out",
 +
    "checked_out_time",
 +
    "version",
 +
    "imptotal",
 +
    "impmade",
 +
    "reset"
 +
  ],
 +
  "convertToInt": [
 +
    "publish_up",
 +
    "publish_down",
 +
    "ordering"
 +
  ],
 +
  "displayLookup": [
 +
    {
 +
      "sourceColumn": "catid",
 +
      "targetTable": "#__categories",
 +
      "targetColumn": "id",
 +
      "displayColumn": "title"
 +
    },
 +
    {
 +
      "sourceColumn": "cid",
 +
      "targetTable": "#__banner_clients",
 +
      "targetColumn": "id",
 +
      "displayColumn": "name"
 +
    },
 +
    {
 +
      "sourceColumn": "created_by",
 +
      "targetTable": "#__users",
 +
      "targetColumn": "id",
 +
      "displayColumn": "name"
 +
    },
 +
    {
 +
      "sourceColumn": "modified_by",
 +
      "targetTable": "#__users",
 +
      "targetColumn": "id",
 +
      "displayColumn": "name"
 +
    }
 +
  ]
 +
}</source>
  
The handling involves preStoreProcess(), a call to the parent store() method, and then a postStoreProcess().
+
===Creating the Record===
 +
You can create records in the installer in one of three ways:
 +
# in the [[J2.5:Managing_Component_Updates_(Script.php)#postflight|postflight]] method
 +
# by creating a <tt>JTableContenttype</tt> instance and adding a row, or
 +
# by using SQL to create a record directly
  
<source lang="php">
+
== Modify your Component's Table Class (or Classes for Multiple Tables) ==
$this->tagsHelper->preStoreProcess($this);
 
$result = parent::store($updateNulls);
 
  
return $result && $this->tagsHelper->postStoreProcess($this);
+
Add the following to your JTable constructor:
</source>
 
  
It is important that this be in the correct order since completion of tag processing depends on having primary key data from the parent store() method.
+
$this->_observers = new JObserverUpdater($this);
 +
JObserverMapper::attachAllObservers($this);
  
Note that much of this code is designed to prevent storage in in #__ucm_content when items are not tagged. If you want to always store in #__ucm_content you should modify the handling appropriately.
+
'''The additional material formerly located here is no longer needed after Joomla 3.1.4''' Please read the document history for this page if you are required to support Joomla 3.1.0 or 3.1.1.
  
=== Add or modify a delete() method ===
+
'''For Joomla 3.2, I needed this line:''' (Replace Weblinks by your component name.)
  
The basic structure:
+
JObserverMapper::addObserverClassToClass('JTableObserverTags', 'WeblinksTableWeblink', array('typeAlias' => 'com_weblinks.weblink'));
  
 +
'''For Joomla 3.4.1, use this:''' (Replace content by your component name.)
 +
JTableObserverTags::createObserver($this, array('typeAlias' => 'com_content.item'));
  
<source lang="php">
+
== Add Tags to the getItem() Method of the Model ==
/**
 
* Method to delete a row from the database table by primary key value.
 
*
 
* @param  integer  $pk  Primary key to delete.
 
*
 
* @return  boolean  True on success.
 
*
 
* @since  3.1
 
* @throws  UnexpectedValueException
 
*/
 
public function delete($pk = null)
 
{
 
$return = parent::delete($pk, $children);
 
if ($return)
 
{
 
$helper = new JHelperTags;
 
$helper->tagDeleteInstances($pk);
 
}
 
return $return; }
 
  
</source>
+
This is still required for Joomla 3.4.1. Add the following to your model ''getItem''. (Replace ''com_content.article'' with your component name/model.):
 +
  if (!empty($item->id))
 +
  {
 +
  $item->tags = new JHelperTags;
 +
  $item->tags->getTagIds($item->id, 'com_content.article');
 +
  }
  
This manages the deletion of all related data if a content item is deleted.
+
'''Note''' this was only required in Joomla 3.1.0 and 3.1.1. It should not be used in Joomla 3.1.4. Please read the history of this page if you need instructions for older versions.
  
 +
== Add a Tag Field to Edit Screens ==
  
== Add tags to the getItem() method of the model ==
+
In any edit layouts where you want to allow tagging, add the field to the ''.xml'' and the appropriate layouts if necessary. The core layouts use a JLayout to manage this and the same layout by any extension.
  
In the getItem() (or similar) method of your model add
+
Note that in '''Joomla 3.1.1 only''', there is special handling of this in the core. Tags in the edit screen '''must''' be part of a metadata ''<fields></fields>'' group.  The core provides two ''JLayouts'' to help you manage standard layouts—one (details) for the metadata and one for the sidebar that includes the tabs. In updating an extension to Joomla 3.1.4, you might need to adjust your edit views.
<source lang="php">
 
// Load item tags
 
if (!empty($item->id))
 
{
 
$item->tags = new JHelperTags;
 
$item->tags->getTagIds($item->id, 'com_contact.contact');
 
$item->metadata['tags'] = $item->tags;
 
}
 
</source>
 
changing to your component and alias name, matching what is in the type table.and changing the name of id property to the name of your primary key.
 
 
 
== Add a tag field to edit screens ==
 
In any edit layouts where you want to allow tagging, you need to add the field to the xml and the appropriate layouts if necessary.
 
 
 
Update: Note that there is new handling of this in the core. Tags in the edit screen should be part of a metadata <fields></fields> group.  The core provides two JLayouts to help you manage standard layouts, one (details) for the metadata and one for the sidebar that includes the tabs.  
 
  
 +
In Joomla 3.1.4 and later, this special handling is not necessary. The best practice is to use the standard ''JLayouts'' bacause this provides a consistent experience for users.
  
 
<source lang="xml">
 
<source lang="xml">
Line 165: Line 263:
 
class="inputbox span12 small" multiple="true"
 
class="inputbox span12 small" multiple="true"
 
>
 
>
</field></source>
+
</field>
 +
</source>
  
The field loads all the Javascript libraries required. You don't need to worry about that.
+
The field loads all the JavaScript libraries required. You don't need to worry about that.
  
 
The field supports two modes:   
 
The field supports two modes:   
* '''Nested tags mode.''' Hierarchical tag list. Doesn't support on the fly tag creation.
+
# '''Nested tags mode.''' The hierarchical tag list that does not support on the fly tag creation.
* '''AJAX mode.''' Tags are searched while user types (3 min. chars required to launch the AJAX search). Custom tags are added by pressing ENTER or COMMA keys. Tags show the global route/path. Example: <code>grandpa/parent/tag</code>
+
# '''AJAX mode.''' Tags are searched while user types. (A minimum of three characters are required to launch the AJAX search.) Custom tags are added by pressing ENTER or COMMA keys. The tags show the global route/path. Example: <code>grandpa/parent/tag</code>
  
The field mode can be forced or use the com_tags setting <code>Tag field mode</code> to determine its mode. To set/force the field mode we have to add '''mode="ajax"''' or '''mode="nested"''' to the tag field definition.
+
The field mode can be forced or use the ''com_tags'' setting <code>Tag field mode</code> to determine its mode. To set or force the field mode, add ''mode="ajax"'' or ''mode="nested"'' to the tag field definition.
 
 
Example of forced AJAX mode: 
 
  
 +
An example of the forced AJAX mode: 
  
 
<source lang="xml">
 
<source lang="xml">
Line 185: Line 283:
 
</field>
 
</field>
 
</source>
 
</source>
The field also includes an attribute to allow/deny the user to enter custom values. Currently this only works in AJAX mode. The attribute has to be added to the field definition like **custom="allow"** or **custom="deny"** 
 
  
Example field definition with custom tags denied:   
+
The field also includes an attribute to allow or deny the entry of custom values by the user. This only works in AJAX mode. The attribute has to be added to the field definition like ''custom="allow"'' or ''custom="deny"''.
 +
 
 +
An example field definition with custom tags denied:   
  
 
<source lang="xml">
 
<source lang="xml">
Line 195: Line 294:
 
>
 
>
 
</field>
 
</field>
 +
</source>
 +
 +
...if it is not already there. Usually ''multiple'' should be true unless you have a specific reason for it not to be false.
 +
In the core components in administrator, the field is in the group shown on the right, below the language field.
  
</source>
+
Note: As of Joomla 3.1.2 if you wish to use the field to designate parent tags, you must add parent="parent" to the ''.xml'' definition of the field.
if it is not already there. Usually multiple should be true unless you have a specific reason for it not to be.
 
In the core components in administrator, the field is in the group shown on the right, below the language field.
 
  
== Prepare the view ==
+
== Prepare the View ==
Add an appropriate version of this to your view.html.php file before loading the layout:
+
Add an appropriate version of this to your ''view.html.php'' file before loading the layout:
  
 
<source lang="php">
 
<source lang="php">
 
$item->tags = new JHelperTags;
 
$item->tags = new JHelperTags;
$item->tags->getItemTags('com_newsfeeds.newsfeed.' , $this->item->id);  
+
$item->tags->getItemTags('com_newsfeeds.newsfeed' , $this->item->id);  
 
</source>
 
</source>
  
 
The first parameter should match a type in the types table. The second parameter is the primary key under which this record is stored in your table. This would be used in any display in any view where you want the tags associated with the item to display.
 
The first parameter should match a type in the types table. The second parameter is the primary key under which this record is stored in your table. This would be used in any display in any view where you want the tags associated with the item to display.
  
 +
== Set Up the Display ==
  
== Set up the display ==
+
In any layout where you want to display the tags associated with an item add:
  
In any layout where you want to display the tags associated with an item add:
 
 
<source lang="php">
 
<source lang="php">
 
<?php $this->item->tagLayout = new JLayoutFile('joomla.content.tags'); ?>
 
<?php $this->item->tagLayout = new JLayoutFile('joomla.content.tags'); ?>
 
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
 
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
 +
</source>
  
</source>
+
...changing the object and property names as appropriate.
Changing the object and property names as appropriate.
 
  
You will most likely want to add the show_tags parameter to the item parameters, the menu item parameters and component configuration as appropriate for your use case.
+
You will most likely want to add the ''show_tags'' parameter to the item parameters, menu item parameters and component configuration as appropriate for your use case.
  
 
<source lang="php">
 
<source lang="php">
Line 227: Line 328:
 
<?php $this->item->tagLayout = new JLayoutFile('joomla.content.tags'); ?>
 
<?php $this->item->tagLayout = new JLayoutFile('joomla.content.tags'); ?>
 
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
 
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
<?php endif; ?></source>
+
<?php endif; ?>
 
+
</source>
== Batch processing ==
 
If you want to add the ability to do batch tagging to a backend list view do the following.
 
  
Add tag to the default_batch layout
+
== Batch Processing ==
 +
If you want to add the ability to do batch tagging to a backend list view, do the following.
  
 +
Add tag to the ''default_batch'' layout
 
 
        <source lang="php">
+
<source lang="php">
 
<div class="control-group">
 
<div class="control-group">
 
<div class="controls">
 
<div class="controls">
 
<?php echo JHtml::_('batch.tag');?>
 
<?php echo JHtml::_('batch.tag');?>
 
</div>
 
</div>
</div></source>
+
</div>
 +
</source>
  
And add tag to the button class in the modal footer (Not totally necessary at this point, but good for potential future changes e.g. if unTag is added.)
+
Also add a tag to the button class in the modal footer. (It is not necessary at this point, but good for potential future changes. For example, if ''unTag'' is added.
  
 
<source lang="php">
 
<source lang="php">
Line 248: Line 350:
 
</source>
 
</source>
  
=== Add a batch method to your model if you are not extending JModelAdmin or overriding the batch method. ===
+
=== Add a Batch Method to Your Model If Not Extending JModelAdmin or Overriding the Batch Method ===
 
 
  
 
<source lang="php">
 
<source lang="php">
Line 267: Line 368:
 
$tagsHelper = new JHelperTags();
 
$tagsHelper = new JHelperTags();
 
$tagsHelper->tagItems($value, $pks, $contexts);
 
$tagsHelper->tagItems($value, $pks, $contexts);
 
 
return true;
 
return true;
 
}
 
}
 
</source>
 
</source>
  
And modify your batch method by adding
+
Also modify your batch method by adding:
 +
 
 
<source lang="php">
 
<source lang="php">
 
if (!empty($commands['tag']))
 
if (!empty($commands['tag']))
Line 280: Line 381:
 
return false;
 
return false;
 
}
 
}
 
 
$done = true;
 
$done = true;
 
}
 
}
 
 
</source>
 
</source>
  
Pay attention to any JSON encoded strings that you need special handling for in batch processing -- remember that you are saving a copy of the core fields and you need save to work as expected.
+
Pay attention to any JSON-encoded strings for which special handling for in batch processing is required. You are saving a copy of the core fields and you need the save to work as expected.
 
 
 
 
 
 
That’s it, now create some tags, tag some items and you are set.
 
 
 
  
[[Category:Development]][[Category:Tags]]
+
<noinclude>
 +
[[Category:Development]]
 +
[[Category:Content Tags]]
 +
[[Category:Tutorials]]
 +
</noinclude>

Latest revision as of 21:44, 1 July 2019

Introduction[edit]

Joomla's tagging system is used in all core content extensions and is designed to be easy to integrate into other extensions that use standard Joomla design patterns.

Using tags in an extension is fairly straightforward. It requires these changes to a typical extension:

  • Register a Content type for the extension view(s)
  • Add Observer methods to the extension table class(es)
  • Add Tag fields to the extension edit forms
  • Add Tag display code to the extension view files
  • Optionally, add a batch tagging method to the extension administration

Register a Content Type for Each View[edit]

In Joomla! 3, content types (articles, weblinks, contacts and so on) are registered in the #__content_types table with a separate record for each content view.

Your extension installer needs to create a new record for each view.

Here is the structure of the #__content_types table. Each column is described in the following sections.

Column name Column type Purpose
type_id int(10) Record key
type_title varchar(255) Type title e.g. Article
type_alias varchar(255) Type alias e.g. com_content.article
table varchar(255) Information about the Table class
rules text Not currently used
field_mappings text Maps the table column names to standard Joomla! names
router varchar(255) Optional: name of a router method
content_history_options varchar(5120) Optional: ????

type_id[edit]

This is the auto-incremented record key, nothing to be done here.

type_title[edit]

The title for your Content Type. For example, Article, Contact, Weblink. You can do this either using sql or postflight by creating an instance of JTableContenttype. Don't forget a category type if you use the Joomla categories API.

type_alias[edit]

A string identifying the component and view. (That would be in the page request, typically matching the model name.) For example, com_contact.contact, com_weblinks.weblink.

table[edit]

Identify which tables used by your component contain records to be tagged. For example, the Contact view of the Contacts component uses #__contact_details. Similarly the Weblinks view of the Weblinks component uses #__weblinks .

Each of these components uses a class to write to the table: ContactTableContact for the Contacts component and WeblinksTableWeblink for the Weblinks component. You will need the class prefixes e.g. ContactTable, WeblinksTable.

Note that Category types all use the #__categories table.

The type_table entry gives the complete table class information for the table class as a JSON object with two elements. The first element represents your "special" table and the second an optional common table. (Otherwise it will default to JTableCorecontent.)

Here is the JSON entry for the Weblink content type:

{
  "special": {
    "dbtable": "#__weblinks",
    "key": "id",
    "type": "Weblink",
    "prefix": "WeblinksTable",
    "config": "array()"
  },
  "common": {
    "dbtable": "#__ucm_content",
    "key": "ucm_id",
    "type": "Corecontent",
    "prefix": "JTable",
    "config": "array()"
  }
}

The values to be included are:

Entry name Content
dbtable Table name
key Primary key name
type Content type
prefix Class prefix
config An option array, as used in your component constructor and getInstance() methods. It may be empty.

This information enables the tagging system (and other APIs) to access your table easily.

If you are using Joomla categories, be sure to create a category type so that they can be tagged. In Joomla 3.1 and 3.1.1, there is an error where the tag field will show even if there is not a type. This is corrected in Joomla 3.1.4.

Notes[edit]

  • The table name for the common table is #__ucm_content. This is incorrect in Joomla 3.1 and 3.1.1 data but is not currently used. The data was updated in Joomla 3.1.4.
  • The type_title field would potentially be used for display although this is not implemented currently except in the contenttype field. Usually it should begin with an upper case letter if it is in English. See the note about how to make this translatable. To make your type names translatable, add COM_TAGS_CONTENT_TYPE_+type_title="Type Title" in both the .ini and .sys.ini files.

rules[edit]

Rules is currently not used. It will likely be removed in favor of an asset_id for each type. You can ignore this field which will be managed by JTable.

field_mappings[edit]

This entry maps the names of specific fields in your table to a set of standard Joomla! names. This mapping is stored as a JSON array with the first 'common' element mapping to the common fields and the second 'special' element mapping the other fields from the table.

Here is the field_mappings entry for the Banner Content Type (chosen because it is a richer example than Article or WebLink). Note that 'common' mappings that have no equivalent are included as null (Leaving them blank may cause SQL issues.) The names and values in the 'special' element all match as they are limited to this component.

{
  "common": {
    "core_content_item_id": "id",
    "core_title": "name",
    "core_state": "published",
    "core_alias": "alias",
    "core_created_time": "created",
    "core_modified_time": "modified",
    "core_body": "description",
    "core_hits": "null",
    "core_publish_up": "publish_up",
    "core_publish_down": "publish_down",
    "core_access": "access",
    "core_params": "params",
    "core_featured": "null",
    "core_metadata": "metadata",
    "core_language": "language",
    "core_images": "images",
    "core_urls": "link",
    "core_version": "version",
    "core_ordering": "ordering",
    "core_metakey": "metakey",
    "core_metadesc": "metadesc",
    "core_catid": "catid",
    "core_xreference": "null",
    "asset_id": "null"
  },
  "special": {
    "imptotal": "imptotal",
    "impmade": "impmade",
    "clicks": "clicks",
    "clickurl": "clickurl",
    "custombannercode": "custombannercode",
    "cid": "cid",
    "purchase_type": "purchase_type",
    "track_impressions": "track_impressions",
    "track_clicks": "track_clicks"
  }
}

At a minimum for common fields you need to map: content_item_id, alias and title in order to successfully create URLs in the tagged items list. You also will probably want: access, status, and language. The special fields are optional.

Note[edit]

  • The JHelperTags and JUcm APIs at Joomla 3.1.1 supported arrays for this field. As of Joomla 3.1.4, either arrays or objects are supported. The default is objects.

router[edit]

This is an optional entry to include the name of a static helper router method for this type found in its front end helpers folder. For example, WeblinksHelperRoute::getWeblinkRoute.

If you do not have a custom router, Tags falls back to the rules in JHelperRoute.

If you only store data in #__ucm_content, you will eventually be able to leave the router field blank although this option is not currently implemented.

content_history_options[edit]

This section was added in Joomla! 3.2 to link to the Content History component.

Here's the entry for the Banner Content Type:

{
  "formFile": "administrator\/components\/com_banners\/models\/forms\/banner.xml",
  "hideFields": [
    "checked_out",
    "checked_out_time",
    "version",
    "reset"
  ],
  "ignoreChanges": [
    "modified_by",
    "modified",
    "checked_out",
    "checked_out_time",
    "version",
    "imptotal",
    "impmade",
    "reset"
  ],
  "convertToInt": [
    "publish_up",
    "publish_down",
    "ordering"
  ],
  "displayLookup": [
    {
      "sourceColumn": "catid",
      "targetTable": "#__categories",
      "targetColumn": "id",
      "displayColumn": "title"
    },
    {
      "sourceColumn": "cid",
      "targetTable": "#__banner_clients",
      "targetColumn": "id",
      "displayColumn": "name"
    },
    {
      "sourceColumn": "created_by",
      "targetTable": "#__users",
      "targetColumn": "id",
      "displayColumn": "name"
    },
    {
      "sourceColumn": "modified_by",
      "targetTable": "#__users",
      "targetColumn": "id",
      "displayColumn": "name"
    }
  ]
}

Creating the Record[edit]

You can create records in the installer in one of three ways:

  1. in the postflight method
  2. by creating a JTableContenttype instance and adding a row, or
  3. by using SQL to create a record directly

Modify your Component's Table Class (or Classes for Multiple Tables)[edit]

Add the following to your JTable constructor:

$this->_observers = new JObserverUpdater($this); JObserverMapper::attachAllObservers($this);

The additional material formerly located here is no longer needed after Joomla 3.1.4 Please read the document history for this page if you are required to support Joomla 3.1.0 or 3.1.1.

For Joomla 3.2, I needed this line: (Replace Weblinks by your component name.)

JObserverMapper::addObserverClassToClass('JTableObserverTags', 'WeblinksTableWeblink', array('typeAlias' => 'com_weblinks.weblink'));

For Joomla 3.4.1, use this: (Replace content by your component name.)

JTableObserverTags::createObserver($this, array('typeAlias' => 'com_content.item'));

Add Tags to the getItem() Method of the Model[edit]

This is still required for Joomla 3.4.1. Add the following to your model getItem. (Replace com_content.article with your component name/model.):

 if (!empty($item->id))
 {
  $item->tags = new JHelperTags;
  $item->tags->getTagIds($item->id, 'com_content.article');
 }

Note this was only required in Joomla 3.1.0 and 3.1.1. It should not be used in Joomla 3.1.4. Please read the history of this page if you need instructions for older versions.

Add a Tag Field to Edit Screens[edit]

In any edit layouts where you want to allow tagging, add the field to the .xml and the appropriate layouts if necessary. The core layouts use a JLayout to manage this and the same layout by any extension.

Note that in Joomla 3.1.1 only, there is special handling of this in the core. Tags in the edit screen must be part of a metadata <fields></fields> group. The core provides two JLayouts to help you manage standard layouts—one (details) for the metadata and one for the sidebar that includes the tabs. In updating an extension to Joomla 3.1.4, you might need to adjust your edit views.

In Joomla 3.1.4 and later, this special handling is not necessary. The best practice is to use the standard JLayouts bacause this provides a consistent experience for users.

		<field name="tags" type="tag"
			label="JTAG" description="JTAG_DESC"
			class="inputbox span12 small" multiple="true"
		>
		</field>

The field loads all the JavaScript libraries required. You don't need to worry about that.

The field supports two modes:

  1. Nested tags mode. The hierarchical tag list that does not support on the fly tag creation.
  2. AJAX mode. Tags are searched while user types. (A minimum of three characters are required to launch the AJAX search.) Custom tags are added by pressing ENTER or COMMA keys. The tags show the global route/path. Example: grandpa/parent/tag

The field mode can be forced or use the com_tags setting Tag field mode to determine its mode. To set or force the field mode, add mode="ajax" or mode="nested" to the tag field definition.

An example of the forced AJAX mode:

		<field name="tags" type="tag" mode="ajax"
			label="JTAG" description="JTAG_DESC"
			class="inputbox span12 small" multiple="true"
		>
		</field>

The field also includes an attribute to allow or deny the entry of custom values by the user. This only works in AJAX mode. The attribute has to be added to the field definition like custom="allow" or custom="deny".

An example field definition with custom tags denied:

		<field name="tags" type="tag" mode="ajax" custom="denied"
			label="JTAG" description="JTAG_DESC"
			class="inputbox span12 small" multiple="true"
		>
		</field>

...if it is not already there. Usually multiple should be true unless you have a specific reason for it not to be false. In the core components in administrator, the field is in the group shown on the right, below the language field.

Note: As of Joomla 3.1.2 if you wish to use the field to designate parent tags, you must add parent="parent" to the .xml definition of the field.

Prepare the View[edit]

Add an appropriate version of this to your view.html.php file before loading the layout:

$item->tags = new JHelperTags;
$item->tags->getItemTags('com_newsfeeds.newsfeed' , $this->item->id);

The first parameter should match a type in the types table. The second parameter is the primary key under which this record is stored in your table. This would be used in any display in any view where you want the tags associated with the item to display.

Set Up the Display[edit]

In any layout where you want to display the tags associated with an item add:

		<?php $this->item->tagLayout = new JLayoutFile('joomla.content.tags'); ?>
		<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>

...changing the object and property names as appropriate.

You will most likely want to add the show_tags parameter to the item parameters, menu item parameters and component configuration as appropriate for your use case.

	<?php if ($this->params->get('show_tags', 1) && !empty($this->item->tags)) : ?>
		<?php $this->item->tagLayout = new JLayoutFile('joomla.content.tags'); ?>
		<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
	<?php endif; ?>

Batch Processing[edit]

If you want to add the ability to do batch tagging to a backend list view, do the following.

Add tag to the default_batch layout

	<div class="control-group">
			<div class="controls">
				<?php echo JHtml::_('batch.tag');?>
			</div>
		</div>

Also add a tag to the button class in the modal footer. (It is not necessary at this point, but good for potential future changes. For example, if unTag is added.

<button class="btn" type="button" onclick="document.id('batch-category-id').value='';document.id('batch-access').value='';document.id('batch-language-id').value='';document.id('batch-user-id').value='';document.id('batch-tag-id)').value=''" data-dismiss="modal">

Add a Batch Method to Your Model If Not Extending JModelAdmin or Overriding the Batch Method[edit]

	/**
	 * Batch tag a list of item.
	 *
	 * @param   integer  $value     The value of the new tag.
	 * @param   array    $pks       An array of row IDs.
	 * @param   array    $contexts  An array of item contexts.
	 *
	 * @return  void.
	 *
	 * @since   3.1
	 */
	protected function batchTag($value, $pks, $contexts)
	{
		$tagsHelper = new JHelperTags();
		$tagsHelper->tagItems($value, $pks, $contexts);
		return true;
	}

Also modify your batch method by adding:

		if (!empty($commands['tag']))
		{
			if (!$this->batchTag($commands['tag'], $pks, $contexts))
			{
				return false;
			}
			$done = true;
		}

Pay attention to any JSON-encoded strings for which special handling for in batch processing is required. You are saving a copy of the core fields and you need the save to work as expected.