tags:

views:

115

answers:

5

I made a class. I give it some objects (mostly retreived database rows) as input, and tell it which fields it has to show, and which buttons I want. Then it renders a very nice html table! It's pretty awesome, I think.

$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->buttons = array('edit','delete');
$ot->render();

However, I also want to be able to manipulate the data it shows. For example, i want to be able to truncate the 'description' field. Or to display an image thumbnail (instead of 'picture.jpg' as text). I don't know how to pass these functions to the class. Perhaps something like:

$ot->functions = array(null,null,'truncate','thumbnail');

But then I don't know how to convert these strings to run some actual code, or how to pass parameters.

There must be a nice way to do what I want. How?

+2  A: 

You are looking for create_function().

However, creating functions on runtime and adding them to a class doesn't sound right to me. It's likely to become a maintenance nightmare very quickly. There are better ways to achieve what you want. What kind of manipulation would the functions to to the data? How about storing them in a "tools" class and connecting that with the table object when needed?

Pekka
+2  A: 

$functions = array(null,null,'truncate','thumbnail');
$function_1 = $functions[3];
$my_string = 'string to truncate';

$result = call_user_func($functions[2], $my_string);

If you want to pass multiple parameters, use call_user_func_array instead.

lo_fye
+3  A: 

PHP has a pseudo-type called "callback", which is actually an ugly closure in disguise. You can call such callbacks using call_user_func() or call_user_func_array():

$callback = 'strlen';
echo call_user_func($callback, '123');
// will output 3 (unless you're using a strange encoding)
soulmerge
As Gordon points out correctly, PHP 5.3 has less ugly closures now. I had totally forgotten them since I'm currently stuck to PHP 5.2.
soulmerge
+1  A: 

You might also want to explore call_user_func, which allows you to call a function based on a string representing its name.

Skilldrick
Damnit, beaten again!
Skilldrick
+7  A: 

Check this question and the answer is:

As of PHP5.3 you could use closures or functors to pass methods around. Prior to that, you could write an anonymous function with create_function(), but that is rather awkward.

But what you are trying to achieve is best solved by passing Filter Objects to your renderer though. All filters should use the same method, so you can use it in a Strategy Pattern way, e.g. write an interface first:

interface Filter
{
    public function filter($value);
}

Then write your filters implementing the interface

class TruncateFilter implements Filter
{
    protected $_maxLength;
    public function __construct($maxLength = 50)
    {
        $this->_maxLength = (int) $maxLength;
    }
    public function filter($value)
    {
        return substr(0, $this->_maxLength, $value) . '…';
    }
}

Give your ObjectTable a method to accept filters

public function addFilter($field, Filter $filter)
{
    if(in_array($field, $this->fields)) {
        $this->_filters[$field][] = $filter;
    }
    return $this;
}

And when you do your ObjectTable instantiation, use

$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->addFilter('description', new TruncateFilter)
   ->addFilter('name', new TruncateFilter(10))
   ->addFilter('image', new ThumbnailFilter);

Then modify your render() method to check if there is any Filters set for the fields you are rendering and call the filter() method on them.

public function render()
{
    foreach($this->fields as $field) {
        $fieldValue = // get field value somehow
        if(isset($this->filters[$field])) {
            foreach($this->filters[$field] as $filter) {
               $fieldValue = $filter->filter($fieldValue)
            }
        }
        // render filtered value
    }
}

This way you can add infinite filters.

Gordon
Wow, awesome reply. Thank you very much!
tape2
you're welcome :)
Gordon