Skip to content

Generatic List Generator

Derek Jones edited this page Jul 5, 2012 · 25 revisions

Title should be "Generic List Generator with templating"

After having rewritten the same function for different controllers and only changing minor things within the function itself, I finally got round to creating a helperfunction that does the trick. This helper should be able to generate just about any list you might want, whether it be a UL, OL, /, table or a Script.aculo.us inPlaceCollectionEditor list.

The helper is tested but not yet fully. I created a thread in the forums, and added some more advanced examples in the 3rd post.

This helper contains one function: string generate_list(array/object $resource, array $options, string $template)

array/object $resource Takes either an array or an object and uses it as the data for its list, it can contain arrays which it will either accept as an associative array (to create a single entry with multiple parameters) or just multiple values to create a sublist which will be nested.

array $options Takes the following values:

  • int level (defaults to 0) Keeps track of the level, the toplist gets 0 and a nested list gets 1, a list nested in the nested list gets 2, etc...
  • string indent (defaults to '') Takes a string that will be added $option['level'] times where you put {INDENT}
  • string link (defaults to '') Will be added between items, but not before the first or after the last.
  • array ignore (defaults to array()) Takes an associated array where the values may be arrays. It checks each key/value in the $resource against this list if it is set and ignores any key/value(s) that match (which also ignores the children of this node).
  • string template_head (defaults to '') Will be added before each list (for example
      ), can be ignored for the top list.
  • string template_foot (defaults to '') Will be added after each list (for example
), can be ignored for the top list.
  • array alternate (defaults to array()) Takes multiple values that will alternate to replace the {ALTERNATE} tag.
  • array functions Takes an array that contains array with 2 values like this array(method_name, object_name), if there's no such method in the object or the object isn't set it also checks the general namespace for a function by the method_name. When called it passes the level and the number to the called function. It replaces a tag by the function name uppercase: {METHOD_NAME}
  • The following example:

    $this->load->helper('generate_list');
    $resource_test = array(array('something'=>array('a','b','c')),'test','and something else');
    
    $options = array(
        'template_head'=>'<ul>',
        'template_foot'=>'</ul>',
        'alternate'=>array('color: blue;', 'color: red;')
    );
    
    echo generate_list($resource_test, $options, '<li style="{ALTERNATE}">{CONTENT}</li>');

    Will generate (without the spaces/linebreaks):

    <ul>
      <li style="color: red;">something</li>
      <ul>
        <li style="color: red;">a</li>
        <li style="color: blue;">b</li>
        <li style="color: red;">c</li>
      </ul>
      <li style="color: blue;">test</li>
      <li style="color: red;">and something else</li>
    </ul>

    The contents of"generate_list_helper.php" (updated december 10, 1:49 GMT) Bugfix (11/20): added a str_replace at the end that replaces any double added $option['link'], should be prevented but don't have the time to figure out a decent patch. Bugfix (12/10): moved the foreach loop for external functions within and outside classes, which now detects if the expected class is the current controller. Though it still wouldn't handle a class correctly for which the classname and the variablename within CI aren't the same. (for example: a library "Example" would be instantiated on $this->example)

    &lt;?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
    
    function generate_list($resource, $options, $template)
    {
        if (is_object($resource)) $resource = get_object_vars($resource);
        
        // Check options, and put to default if unset
        if (!isset($options)) $options = array();
        if (!isset($options['level']))                 $options['level'] = 0;
        if (!isset($options['indent']))             $options['indent'] = '';
        if (!isset($options['link']))                 $options['link'] = '';
        if (!isset($options['ignore']))             $options['ignore'] = array();
        if (!isset($options['selected']))            $options['selected'] = array();
        if (!isset($options['template_head']))         $options['template_head'] = '';
        if (!isset($options['template_foot']))         $options['template_foot'] = '';
        if (!isset($options['use_top_wrapper']))    $options['use_top_wrapper'] = true;
        if (!isset($options['alternate']))             $options['alternate'] = array();
        if (!isset($options['current_alternate']))     $options['current_alternate'] = 0;
        if (!isset($options['functions']))             $options['functions'] = array();
        
        // create options for next level
        $next_options = $options;
        $next_options['level']++;
        
        // instantiate output variable
        $output = '';
        
        // add the list header
        if ($options['level'] > 0 or $options['use_top_wrapper']) $output = $options['template_head'];
        
        // count the number of values in resource
        $resource_count = count($resource);
        
        // create the list
        foreach($resource as $item)
        {
            // create a temporary variable for each item
            $item_output = $template;
            
            // if there are methods set, replace their tags
            if (count($options['functions']) > 0)
            {
                foreach($options['functions'] as $function)
                {
                    $ci =& get_instance();
                    
                    if (method_exists($function[1], $function[0]))
                    {
                        if (get_class($ci) == $function[1])
                            $replace_value = $ci->$function[0]($options['level'], $options['current_alternate'], $item['id']);
                        else
                            $replace_value = $ci->$function[1]->$function[0]($options['level'], $options['current_alternate'], $item['id']);
                        $item_output = str_replace('{'.strtoupper($function[0]).'}', $replace_value, $item_output);
                    }
                    elseif (function_exists($function[1]))
                    {
                        $replace_value = $function[0]($options['level'], $options['current_alternate'], $item['id']);
                        $item_output = str_replace('{'.strtoupper($function[0]).'}', $replace_value, $item_output);    
                    }
                }
            }
            
            // place the values in the item variable
            if (!is_array($item))
            {
                $item_output = str_replace('{CONTENT}', $item, $item_output);
            }
            else
            {
                // check the ignore values
                if (count($options['ignore']) > 0)
                {
                    foreach($options['ignore'] as $ignore => $ignore_value)
                    {
                        if (is_array($ignore_value))
                        {
                            foreach ($ignore_value as $ignore_val_val)
                            {
                                if ($item[$ignore] == $ignore_val_val)
                                {
                                    $item_output = '';
                                }
                            }
                        }
                        else
                        {
                            if ($item[$ignore] == $ignore_value)
                            {
                                $item_output = '';
                            }
                        }
                    }
                }
                
                foreach ($item as $key => $value)
                {
                    // if the value is an array, replace any instance of SUBS with a sublist
                    if (!is_array($value))
                    {
                        $item_output = str_replace('{'.strtoupper($key).'}', $value, $item_output);
                    }
                    else
                    {
                        if (empty($value)) $sublist = '';
                        else $sublist = generate_list($value, $next_options, $template);
                        if ($sublist != '') $sublist = $options['link'].$sublist;
                        $item_output = str_replace('{SUBS}', $sublist, $item_output);
                    }
                }
            }
            
            // check if any values were replaced, if not assume sublist, generate and continue
            if ($item_output == $template && is_array($item))
            {
                $item_output = generate_list($item, $next_options, $template);
                if ($resource_count > 1 && $item_output != '')
                {
                    $item_output .= $options['link'];
                    $resource_count--;
                }
                $output .= $item_output;
                continue;
            }
            
            // place the indent
            $item_output = str_replace('{INDENT}', str_repeat($options['indent'], $options['level']), $item_output);
            
            // place the alternation
            if (is_array($options['alternate']) && !empty($options['alternate']))
            {
                $alternate_max = count($options['alternate']);
                $alternate = $alternate_max;
                while($options['current_alternate'] % $alternate != 0 && $alternate > 0) $alternate--;
                $item_output = str_replace('{ALTERNATE}', $options['alternate'][$alternate - 1], $item_output);
            }
            
            // link the items with the link only if it isn't the last item
            if ($resource_count > 1 && $item_output != '')
            {
                $item_output .= $options['link'];
            }
            
            // update the counters before next iteration
            $options['current_alternate']++;
            $resource_count--;
            
            // cleanup and replace any leftover tags
            $item_output = preg_replace('/\{([a-zA-z0-9_]*)\}/', '', $item_output);
            
            // place the new item at the end of the current list
            $output .= $item_output;
            unset($item_output);
        }
        
        // close the list with the list footer
        if ($options['level'] > 0 or $options['use_top_wrapper']) $output .= $options['template_foot'];
        
        // remove any double "link"-s  -- IMPROVE THIS FIX, PREVENT FROM HAPPENING, happens at the end of a sublist when its ignored
        $output = str_replace($options['link'].$options['link'], $options['link'], $output);
        
        // ... and return the output
        return $output;
    }
    
    /* End of file generate_list_helper.php */
    /* Location: ./system/application/helpers/generate_list_helper.php */
    Clone this wiki locally