1
\$\begingroup\$

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!"? :)

\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$
    – Fge
    Commented Dec 12, 2012 at 14:13
  • \$\begingroup\$ 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. \$\endgroup\$
    – Nabab
    Commented Dec 12, 2012 at 14:16

1 Answer 1

1
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ 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"? \$\endgroup\$
    – Nabab
    Commented Dec 12, 2012 at 18:34
  • \$\begingroup\$ 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 ;) \$\endgroup\$
    – Fge
    Commented Dec 12, 2012 at 20:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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