5

I am trying to create an object oriented form generator. Please keep in mind it will be used only by a handful of people in our company to solve a specific problem.

I am currently facing two little caveats.

Syntax of creating elements

There are few approaches I can take.

Setting everything in constructor. As a drawback this could result in inconsitent constructor usage

Input::create('text', 'name', array('maxlength' => 10));

Limit constructor to type and expose only the most used attributes as methods (keep one method for mass attribute setting)

Input::create('text')->name('name')->value('value')->attribute('max_length', 10);

Expose every attribute as method with either creating a method for every attribute or with __call magic method which will result in no autocomplete support in an IDE. And even now, I can keep the attribute method.

Input::create()->type('text')->name('name')->value('value')->max_length(10)->id('id'); //etc.

At the moment, I consider the second approach as the best one, since it keeps "good" stuff from both worlds. As still provides a way to abstract some of the work, since e.g. method required would not only set the required attribute, but also mark this field for validation object as required.

Code duplication with approach 2 and 3

Since there are attributes that can be used by every element, but also attributes that are only usable by 3 or 4 elements, e.g. HTML5 attribute form.

Every element can inherit from base element which has methods for attributes that are universal for every element (e.g. name). Partially usable attributes can be solved with interfaces, but this leads to code duplication as they cant contain method body.

Traits would be the solution, but sadly, I am stuck at PHP 5.3 with no way to upgrade. That leaves me with either implementing Mixin or Composition pattern, which again, might lead to no autocomplete support. This would be partially mitigated when using second approach.

So to my actual question:

Which approach can end as most suitable? (suitable as in minimal code duplication, solid code reuse and easiness of implementation)

I realize this might very well spawn opinion based answers so I apologize in advance if it does.

6
  • Is Input::create() just a factory method for creating the real "Input Objects"? I got confused since you mentioned a constructor a few times, but never showed your constructor. Also for your inheritance problem there are abstract classes. They can have a method body and can also delegate implementation details to their children by defining abstract methods without a body. Commented Feb 24, 2015 at 12:48
  • Input::create() creates a standalone Input object, but multiple elements, e.g. Textarea, Button would have different constructor parameters. As I said, every element can inherit from base element (abstract class), but we are talking about single inheritance and thus code duplication when using multiple base elements (because I dont want attributes that can only be set for input in textarea). Using multiple base elements with interfaces would also result in duplication. Commented Feb 24, 2015 at 13:05
  • 1
    Ever considered looking what's already out there like github.com/naomik/htmlgen ? Commented Feb 24, 2015 at 13:58
  • I need to solve a specific usage case where I would spend more time updating something existing than creating it. Commented Feb 24, 2015 at 14:04
  • I'm not sure you'll have lots of duplicate code as the elements of a HTML form are predefined. In my form builder I have the following inheritance : BaseField -> TextField -> EmailField ; BaseField -> Button -> SubmitButton and so on. Perhaps this aproach can be suitable? Commented Feb 24, 2015 at 19:10

2 Answers 2

1

I realize this is an old question, but someone in the comments mention a project that I've created called htmlgen, mirrored on packagist. I'm chiming in with some support here as I've recently released a new version 2.x that makes HTML generation quite pleasant in PHP.

use function htmlgen\html as h;
echo h('input', ['name'=>'catQty', 'value'=>500])

Will render

<input name="catQty" value="500">

However, that example is barely scratching the surface in terms of potential

h('#wrapper',
  h('h1.title', 'Hello, World'),
  h('p',
    h('comment', 'link to duckduckgo'),
    h('a', ['href'=>'https://duckduckgo'], 'search the internet')
  )
);

Here's the output (actual output does not have whitespace)

<div id="wrapper">
  <h1 class="title">Hello, World</h1>
  <p>
    <!-- link to duckduckgo -->
    <a href="https://duckduckgo">search the internet</a>
  </p>
</div>

It's also very handy at rendering collections of data

use function htmlgen\html as h;
use function htmlgen\map;

$links = [
  'home' => '/',
  'cats' => '/cats',
  'milk' => '/milk',
  'honey' => '/honey',
  'donuts' => '/donuts',
  'bees' => '/bees'
];

echo h('nav',
  h('ul',
    map($links, function($href, $text) { return
      h('li',
        h('a', ['href'=>$href], $text)
      );
    })
  )
);

Would output (again, whitespace is here just for display)

<nav>
  <ul>
    <li><a href="/">home</a></li>
    <li><a href="/cats">cats</a></li>
    <li><a href="/milk">milk</a></li>
    <li><a href="/honey">honey</a></li>
    <li><a href="/donuts">donuts</a></li>
    <li><a href="/bees">bees</a></li>
  </ul>
</nav>

It's all 100% PHP and no custom, proprietary funny business. It's highly expressive and lends itself to great compositions. You can break down your templates into easy-to-reuse functions or require calls.

Sign up to request clarification or add additional context in comments.

Comments

0

IMO i'd go for the second option. Probably because it reminds me of jQuery.

Input::create('text')->name('name')->value('value')->attribute('max_length', 10);

Define the more generic fields like 'name', 'value', 'attribute' in an interface class 'FormElements' and implement/extend that in classes 'Input', 'Select' etc.

Although I personally prefer that 2nd option.. in the words of Paul the alien "Sometimes you just gotta roll the dice".

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.