[tutorial] Simple Smart TagCloud with PHP

Discussion in 'PHP' started by jokerhacker, Dec 18, 2012.

  1. #1
    Hi all,

    Tag clouds are widely used on all kind of website, specially search tags.
    So, basically the website will log search terms and then call them to build the tag cloud.
    As we need such system to be smart and lightweight, we are going to use XML files because we hate DB systems :p

    1st function: Storing search terms
    Your website has (of course) a page where people can search contents on it.
    Its name is something like "search.php" or something. Basically, we need to cut search string into tags, we can do that with explode(), so

    $srch_str
    , will contain the search string (assuming it's well filtered and validated, if you want me to explain how to do so, just mention iit in a reply)
    $tags = explode(" ", $srch_str);
    PHP:
    We now have all the terms in the search string arranged in an array! :D
    After that, we need to store it, we need sonmething quick and efficient, a simple txt file would be enough. Let's say the filename is "tempsearch.txt"

    (i'll explain later why i added temp).
    $handle = fopen( getcwd() . "/tempsearch.txt", "a");
    foreach ( $tags as $tag )
         fwrite($handle, $tag . PHP_EOL);
    fclose($handle);
    PHP:
    What is that "a"?
    It's the type of access we require to the file, a means
    What is that "PHP_EOL"?
    Since every platform has its own caracters, we need to make many if statements to determine wich one we need to do line break (like \n or \n\r)
    we said we need it to be efficient and quick, that txt file is quick for execution so it won't slow down page execution time for visitiors, but we need it to

    be efficient too! Thats why we need to transfer tags from it to a better format: it's XML, buddy!
    Our XML file will be growing and act as a DB. Let's call it "tagindex.xml".
    We are going to create a small function to transfer tags from "tempsearch.txt" to "tagindex.xml" to be usable.
    Let's do it!
    We create our empty xml file
    <?xml version="1.0"?>
    <tags></tags>
    Code (markup):
    Now PHP!
    $tagdb = simplexml_load_file("tagindex.xml"); // assuming it does already exists!
    $handle = fopen( getcwd() . "/tempsearch.txt", "r+");
    if ($handle) {
        while (($tag = fgets($handle, 1024)) !== false) {
            $tagdb->addChild($tag);
    }
    fclose($handle);
    $tagdb->asXML("tagindex.xml");
    }
    
    PHP:
    Our XML file is updated now, when we want to show the tag cloud we will use that XML file not txt one.
    You can set this code to run each 15mins, less or more depending on your server capacity.

    2nd function: Showtime!
    We all know that terms in tagclouds are not equal, each element has its own font size depending on its "frequency", let's say our minimal font size

    is 10, and max 40.
    First of all we pickup randomly n different tags to display.
    $tagdb = simplexml_load_file("tagindex.xml");
    $n = 20; //beware n must be greater than the number of unique tags in tagdb!
    //for execution time we will require n to not to be greater than 1/2 of unique tags
    $half = sizeof((array)$tagdb) * 0.5;
    if($half < $n) 
         $n = sizeof((array)$tagdb); 
    $tags = array();
    while( sizeof($tags) < $n ){
         $rand_tag = array_rand((array)$tagdb);
         if (!in_array($rand_tag, $tags)
              $tags[] = $rand_tag;
    }
    
    PHP:
    Now for each tag we need a score, that's its frequenccy.
    $scores = array();
    foreach($tags as $tag) 
         scores[] = sizeof($tagdb->$tag);
    
    PHP:
    We need Some Maths to determine what font size we are going to use for each tag
    $min = min($scores);
    $max = max($scores);
    //some maths :)
    $coef = (40 - 10)/($max - $min);
    $suf = 40 - $coef * $max;
    
    PHP:
    Here is how we are going to show the output
    echo '<div id="tagcloud" class="tagcloud">
    <ul>
    ';
    foreach ( $tags as $i => $tag ) {
    	$size = round($coef * $scores[$i] + $suf);
    	echo '<li style="font-size:' . $size . 'px;">' . $tag . '</li>';
    }
    echo '</ul></div>';
    
    PHP:
    Hurray! Thats all we need to build our very own TagCloud system. It's quick, efficient and smart!
    you can integrate it in any website which has a search bar.

    Now let's organize our work, we'll create pages and include then to not to get nested code.

    search_include.php
    $tags = explode(" ", $srch_str);
    $handle = fopen( getcwd() . "/tempsearch.txt", "a");
    foreach ( $tags as $tag )
         fwrite($handle, $tag . PHP_EOL);
    fclose($handle);
    PHP:
    Now you have add this line to your search page
    include ('search_include.php');
    PHP:
    Our empty xml file
    <?xml version="1.0"?>
    <tags></tags>
    Code (markup):
    tag_cronjob.php
    $tagdb = simplexml_load_file("tagindex.xml"); // assuming it does already exists!
    $handle = fopen( getcwd() . "/tempsearch.txt", "r+");
    if ($handle) {
        while (($tag = fgets($handle, 1024)) !== false) {
            $tagdb->addChild($tag);
    }
    fclose($handle);
    $tagdb->asXML("tagindex.xml");
    //empty tempsearch.txt
    file_put_contents('tempsearch.txt', '');
    }
    PHP:
    showtags.php
    $tagdb = simplexml_load_file("tagindex.xml"); 
    $n = 20; //beware n must be greater than the number of unique tags in tagdb! 
    //for execution time we will require n to not to be greater than 1/2 of unique tags 
    $half = sizeof((array)$tagdb) * 0.5;
    if($half < $n) 
         $n = sizeof((array)$tagdb); 
    $tags = array(); 
    while( sizeof($tags) < $n ){ 
         $rand_tag = array_rand((array)$tagdb); 
         if (!in_array($rand_tag, $tags)) 
              $tags[] = $rand_tag; 
    } 
    $scores = array(); 
    foreach($tags as $tag)  
         $scores[] = sizeof($tagdb->$tag); 
    $min = min($scores); 
    $max = max($scores); 
    //some maths :) 
    $coef = (40 - 10)/($max - $min); 
    $suf = 40 - $coef * $max; 
    //output 
    echo '<div id="tagcloud" class="tagcloud"> 
    <ul> 
    '; 
    foreach ( $tags as $i => $tag ) { 
        $size = round($coef * $scores[$i] + $suf); 
        echo '<li style="font-size:' . $size . 'px;">' . $tag . '</li>'; 
    } 
    echo '</ul></div>';
    
    PHP:
    Now you include showtags.php wherever you want that tagcloud to be displayed. just put this
    include ('showtags.php');
    PHP:
    You also need some CSS to format your tag cloud, because it's ugly if you display it as a normal list on your home page :eek:

    Example of quick css styling
    padding:10px;
    display: inline;
    Code (markup):

    Well this is my first tutorial, i hope you all like it. I'll be glad to see your comments, reviews and code improvements.
    All of the above code is tested and is working correctly, however, please test and give feedback ;)

    Thanks for reading :)
     
    jokerhacker, Dec 18, 2012 IP