Search

Just out of curiosity, and I think the answer to my question would be "no", but is it possible to get all the distinct tag list values used by the entries of a particular section (e.g. like you wanted to generate a tag cloud list)?

I've built a DS that basically gets all the entries, with no fields attached to it, but with the DS param option set to "keywords", which would be my tag list field: now, this gets all the tag list values, but obviously displays values more than once, if they're used by 2+ entries.

Ideas? I think it's an important feature that should be taken into consideration in the future.

Yes, this is possible using XSLT.

<xsl:apply-templates select="//tags/tag[not(. = following::tag)]" />

Your XPath will vary, but the important part is the predicate (between the brackets).

I have written a data source that does this without outputting a list of all entries — it uses a native SQL select statement instead. It requires a little configuration from the developer's point of view (adding the ID of the tag list field to the script). But the XML output is a list of distinct tags and the frequency of each (which can then be transformed into a tag cloud).

Here is the contents of my data.tag_cloud.php Data Source. Replace $tag_list_id with the ID of your tag list field. To find the ID of the field, I've found the quickest way is to view the source of the page when you edit your section, and the ID will be there.

<?php

    require_once(TOOLKIT . '/class.datasource.php');

    Class datasourceTag_Cloud extends Datasource{

        function about(){
            return array(
                    'name' => 'Tag Cloud',
                    'author' => array(
                        'name' => 'Nick Dunn',
                        'website' => 'http://nick-dunn.co.uk',
                        'email' => 'nick@nick-dunn.co.uk'),
                    'version' => '1.0',
                    'release-date' => '2008-12-09T12:00:00+00:00');
        }

        function grab(&$param_pool){

            $tag_list_id = "77";
            $result = new XMLElement("tag-cloud");

            $tags = $this->_Parent->Database->fetch("SELECT DISTINCT(handle), COUNT(handle) AS count, value FROM sym_entries_data_$tag_list_id GROUP BY handle ORDER BY handle ASC");

            foreach($tags as $tag){
                $tag_node = new XMLElement("tag", $tag["value"]);
                $tag_node->setAttributeArray(array("handle" => $tag["handle"], "count" => $tag["count"]));
                $result->appendChild($tag_node);
            }

            return $result;
        }
    }

?>

To create a tag cloud from the above I wrote this XSLT:

<xsl:template match="/">

    <xsl:call-template name="tag-cloud">
        <xsl:with-param name="range" select="'4'" />
        <xsl:with-param name="data-source" select="'tag-cloud'" />
    </xsl:call-template>

</xsl:template>

<xsl:template name="tag-cloud">

    <xsl:param name="range" />
    <xsl:param name="data-source" />

    <xsl:variable name="tags" select="/data/*[name()=$data-source]" />
    <xsl:variable name="constant" select="(math:max($tags/tag/@count) div $range)" />

    <ul>
        <xsl:for-each select="$tags/tag">
            <li class="size-{ceiling(@count div $constant)}">
                <a href="/tags/{@handle}/">
                    <xsl:value-of select="." />
                </a>
            </li>
        </xsl:for-each>
    </ul>

</xsl:template>

Note: the above XSLT uses the math:max() function provided by the EXSLT library. You'll need to modify the stylesheet element at the top of your document to import this namespace:

<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:math="http://exslt.org/math"
    extension-element-prefixes="math">

I call the tag-cloud template passing the name of the tag-list DS XML node, and the number range for the cloud. This range is used to create class names so that I can style the tags differently (in size, weight, colour etc.). In the example above, a range of 4 is provided, so the class attribute values will be one of: size-1, size-2, size-3 and size-4 depending on how frequently the tag has been used (the least used will be size-1, the most used size-4, and everything else evened out inbetween). To create more variety, increase the range.

Hope this helps!

Thankyou so much, everyone!

@nickdunn: I created a datasource, without changing anything. Opened it up in Textmate and copy-pasted your code. Updated the field id and saved. Now, the DS is de-listed in the Components page, and it can't be attached to a page either.

What did I do wrong?

After editing the different references to the name of the DS, it reappeared. Working as expected, thanks!

Could have been the name — the class name needs to match the file name of the PHP file.

How to filter this DS by url parameters?

Nick Dunn you glorious bastard! :D

This is why I love the Symphony community.

Cheers Henry. Just a note to say this custom DS spawned the idea of the Section Schemas extension which creates these Data Sources for you without needing to write any custom PHP.

Here’s the equivalent Tag Cloud example using Section Schemas:

http://getsymphony.com/community/discussions/849/

I am using your data.tag_cloud.php Data Source, but I get an error... There's an ampersand (&) within one tag and symphony doesn't like it. How can I fix it?

Yikes! The above code is four years old, I'm surprised it still works. Try:

$tag_node = new XMLElement("tag", General::sanitize($tag["value"]));

Hahah! Yes, it work and it's prfect!!!! Way simpler than Section Schemas extension. Exactly what I need. Thanks!!!

For your information there is the same ampersand issue with Section shema. At least with version 1.8

I am getting the following error

Fatal error: Call to a member function fetch() on a non-object in ...data.tag_list.php on line 23

anyone know how I can resolve this? I'm using 2.3

Guess that's because $this->_parent no longer exists in symphony 2.3. You should use Symphony::Engine() instead.

thanks for the response. I am only just picking up php, so not entirely sure about the correct syntax. I tried the following but got the same error

$tags = Symphony::Engine()->Database->fetch("SELECT DISTINCT(handle), COUNT(handle) AS count, value FROM sym_entries_data_$tag_list_id GROUP BY handle ORDER BY handle ASC");

Try:

$tags = Symphony::Database()->fetch("SELECT DISTINCT(handle), COUNT(handle) AS count, value FROM sym_entries_data_$tag_list_id GROUP BY handle ORDER BY handle ASC");

That works great, thanks all.

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details