Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have made a class for generating html form elements:

class input
{
  public $tag = 'input', $type = 'text', $customClass, $name;

  public function __construct(array $cfg){
    if ( isset($cfg['name']) ){
      if ( isset($cfg['tag']){
        $this->tag = strtolower($cfg['tag']);
      }
      if ( $this->tag === 'input' && isset($cfg['type']){
        $this->type = $cfg['type'];
      }
      if ( isset($cfg['customClass']){
        $this->customClass= $cfg['customClass'];
      }
      //etc...
    }
  }
}

I would like to be able to set a custom default config for all my fields - without changing the class. For example I want all the fields in a given script to have 'myClass' as customClass. For me, the solution would be to make these default values static, and make a function to change them like this:

class input
{
  static $tag = 'input', $type = 'text', $customClass, $name;

  public static function setDefault(array $cfg){
    if ( isset($cfg['tag']) ){
      self::tag = strtolower($cfg['tag']);
    }
    if ( isset($cfg['type']) ){
      self::type = $cfg['type'];
    }
    if ( isset($cfg['customClass']) ){
      self::customClass= $cfg['customClass'];
    }
    //etc...
  }

  public function __construct(array $cfg){
    if ( isset($cfg['name']) ){
      $this->tag = isset($cfg['tag']) ? strtolower($cfg['tag']) : self::tag;
      $this->type = isset($cfg['type']) ? strtolower($cfg['type']) : self::type;
      $this->customClass = isset($cfg['customClass']) ? $cfg['customClass'] : self::customClass;
      //etc...
    }
  }
}

The problem is that I read everywhere - and particulary here - that static are evil. What other solution would be suitable in the present case? Or can someone tell me "yeah, that's the perfect case to use statics!"? :)

share|improve this question
    
some further information on how the class is used / in which context / which other components you do have regarding configuration etc... would be helpful. AFAIK there are many very clean solutions. However they highly depend on the available infrastructure and use-cases. –  Fge Dec 12 '12 at 14:13
    
I'm using a custom MVC framework, but I'd like this class to work as stand-alone, although I plan to use it with another class generating form from database table structure. –  Nabab Dec 12 '12 at 14:16

1 Answer 1

up vote 1 down vote accepted

You could either a configuration object or a builder for this. As you probably can see yourself, using static gets complicated quite fast.

Using a builder:

class InputBuilder
{
    private $_defaults = array(
        'tag' => null,
        'type' => null,
    );

    private $_current;

    public function __construct(array $defaults = null)
    {
        foreach ($defaults as $key => $value) {
            if (array_key_exists($this->_defaults[$key])) {
                $this->_defaults[$key] = $value;
            }
        }

        $this->reset();
    }

    public function reset()
    {
        $this->_current = array();
        foreach ($this->_defaults as $key => $value) {
            $this->_current[$key] = $value;
        }       
    }

    public function setTag($tag)
    {
        if (!is_string($tag)) {
            throw new InvalidArgumentException('...');
        }
        $this->_current['tag'] = $tag;
    }

    public function setType($type)
    {
        if (!is_string($type)) {
            throw new InvalidArgumentException('...');
        }
        $this->_current['type'] = $type;    
    }

    public function getInput()
    {
        return new Input($this->_current);
    }
}

Usage would be:

$builder->setType('text')
        ->build();  // tag: input, type: text
$builder->setType('password')
        ->build(); // tag: input, type: password
$builder->reset(); // reset everything to defaults
$builder->setTag('textarea') // tag: textarea: type: text
        ->build();

The builder pattern is extremely strong if you want to build the object programmatically. (SQL) Query-builder are a very common use case for this.

Using a configuration object:

class InputConfiguration
{
        private $_init = array(
            'tag' => null,
            'type' => null,
        );

        private $_current;

        public function __construct(array $defaults = null)
        {
            foreach ($defaults as $key => $value) {
                if (array_key_exists($this->_defaults[$key])) {
                    $this->_defaults[$key] = $value;
                }
            }
        }

        public function configure(Input $element)
        {
            $config = $this->_init['tag'];

            if (null !== $config['tag'] && null !== $element->getTag()) {
                $element->setTag($config['tag']);
            }

            if (null !== $config['type'] && null !== $element->getType()) {
                $element->setType($config['type']);
            }
        }
}

class Input 
{
     public function construct(InputConfiguration $config = null)
     {
           if (null !== $config) {
               $config->configure($this);
           }
     }

     // ...
}

Usage would be:

$configuration = new InputConfiguration(
    'tag' => 'input', 
    'type' => 'text',
);

$newObject = new Input($configuration); // has defaults applied

$newObject2 = new Input(); // we don't want to use defaults
$configuration->configure($newObject2); // or decide to apply them later on

The configuration object decouples the configuration from your object. Furthermore it makes one configuration reusable and optional.

share|improve this answer
    
Thanks, that looks like a good implementation. What I prefered in mine is there was only one class - so 1 file included but that's detail. Yes, I think a builder would be the solution for me. Still, why do you say "using static gets complicated quite fast"? –  Nabab Dec 12 '12 at 18:34
    
because you maintain almost the same information twice within the class + the class is responsible for knowing two thinks: the defaults and the current data. Look at your code above - in my opinion it's already overly complicated ;) –  Fge Dec 12 '12 at 20:25

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.