tgavin Posted July 5, 2013 Share Posted July 5, 2013 I have a class for building forms and I want to upgrade it to handle Bootstrap. I'd like to use my current methods to build the Bootstrap elements, but I don't want all of this new Bootstrap code cluttering up my current Forms class, so I'd like to put it into its own class in a separate file. Below is a very simple example of what I'm trying to do. Obviously this doesn't work and the code is wrong, however, from this you can hopefully see what I'm trying to accomplish. <?php class Forms { public function input_text($name) { return '<input type="text" name="'.$name.'">'; } } class Bootstrap { public function bootstrap_input_text($name) { $return = '<div class="control-group">'; $return .= '<label class="control-label" for="'.$name.'">First name</label>'; $return .= '<div class="controls">'; $return .= $this->Forms->input_text($name); $return .= '</div>'; $return .= '</div>'; return $return; } } $form = new Forms(); echo $form->bootstrap_input_text('fname'); Quote Link to comment Share on other sites More sharing options...
AbraCadaver Posted July 5, 2013 Share Posted July 5, 2013 class Bootstrap extends Forms{ public function bootstrap_input_text($name) { $return = '<div class="control-group">'; $return .= '<label class="control-label" for="'.$name.'">First name</label>'; $return .= '<div class="controls">'; $return .= $this->input_text($name); $return .= '</div>'; $return .= '</div>'; return $return; } } $bs = new Bootstrap(); echo $bs->bootstrap_input_text('fname'); You can even redefine input_text() in the Boostrap class, but here you wouldn't want to. Quote Link to comment Share on other sites More sharing options...
tgavin Posted July 5, 2013 Author Share Posted July 5, 2013 You know, 'Bootstrap extends Forms' is pretty much the first thing I tried, but I didn't want to create a new $bs object, because I wanted to keep it simple and use the $form object for everything. I know it's only the difference in typing $form-> vs. $bs->, but it's easy to forget when you're on a roll. So, if I don't want to create a new object, is my only option to put all of the bootstrap methods in the Forms class? Thanks! Quote Link to comment Share on other sites More sharing options...
AbraCadaver Posted July 5, 2013 Share Posted July 5, 2013 I'm not a hardcore OOP guy that uses interfaces, abstract classes and all the other stuff, so maybe someone else can chime in, but maybe something like this. I'm probably breaking all kinds of OOP / design pattern rules, but oh well: class Forms { public function bootstrap() { if(!is_object($this->bootstrap)) { $this->bootstrap = new Bootstrap; return $this->bootstrap; } } } $form = new Forms; $form->bootstrap()->bootstrap_input_text('fname'); The other thing I thought of if you will extend this more with other classes is to use __call() in Forms and have it instantiate the proper class and call the method. Not sure if I'm sending you down the wrong path or not. Quote Link to comment Share on other sites More sharing options...
KevinM1 Posted July 5, 2013 Share Posted July 5, 2013 Use dependency injection and delegate: class Forms { private $bootstrap; public function __construct($bootstrap) { $this->bootstrap = $bootstrap; } public function input_text($name) { return $this->bootstrap->bootstrap_input_text($name); } } Quote Link to comment Share on other sites More sharing options...
AbraCadaver Posted July 5, 2013 Share Posted July 5, 2013 I think he wants to use his existing Forms input_text() and also be able to call Bootstrap bootstrap_input_text() from the forms object and have bootstrap_input_text() be able to use his existing input_text(). If that makes sense. Quote Link to comment Share on other sites More sharing options...
ignace Posted July 5, 2013 Share Posted July 5, 2013 (edited) Kevin already mentioned it, you need delegation, more particular, the Decorator pattern. interface FormInterface { public function input($value, $type = 'text'); } abstract class FormDecorator implements FormInterface { protected $form; public function __construct(FormInterface $form) { $this->form = $form; } } class BootstrapFormDecorator extends FormDecorator { public function input($value, $type = 'text') { return '<bootstrap-here>' . $this->form->input($value, $type) . '</bootstrap-here>'; } } class Form implements FormInterface { public function input($value, $type = 'text') { return "<input type=\"$type\" value=\"$value\">"; } } $form = new BootstrapFormDecorator(new Form); echo $form->input('foo'); Edited July 5, 2013 by ignace Quote Link to comment Share on other sites More sharing options...
tgavin Posted July 5, 2013 Author Share Posted July 5, 2013 Thanks for your help guys. Before I try any of this, I'm thinking: what about just doing the following class Forms extends Bootstrap { .... } Seems simple enough, and works! Would there be any problems with this? Quote Link to comment Share on other sites More sharing options...
Solution ignace Posted July 6, 2013 Solution Share Posted July 6, 2013 (edited) The problem with that is that you will have to re-write your form every time you change framework. Your form never changes, an input remains an input so you should be able to re-use that code and decorate your form with the bootstrap css framework. The added benefit is that the interface you "talk" to is the same regardless wether you are using a plain form or a bootstrap form. Allowing you to even re-use your existing forms and changing them is as easy as: function get_form() { return new BootstrapForm(new SimpleForm); }to function get_form() { return new BlueprintForm(new SimpleForm); }And now all your existing forms on your website have been changed from bootstrap to blueprint. You can even go crazy and do something like: function get_form() { return new BootstrapForm(new BlueprintForm(new SimpleForm)); }If you are concerned about not enough flexibility you can always extend the FormInterface: interface FormInterface { public function input($name, $value, $type = 'text', $id = null, array $attrs = array()); public function textInput($name, $value, $id = null, array $attrs = array()); public function radioInput($name, $onOrOff = false, $id = null, array $attrs = array()); .. public function dropDown($name, array $options, $id = null, array $attrs = array()); .. }Also take a look at- http://framework.zend.com/manual/2.2/en/modules/zend.form.intro.html - and http://symfony.com/doc/current/book/forms.html Edited July 6, 2013 by ignace Quote Link to comment Share on other sites More sharing options...
tgavin Posted July 8, 2013 Author Share Posted July 8, 2013 Excellent advice. This is the way I'm going to go. Thanks guys! Quote Link to comment Share on other sites More sharing options...
tgavin Posted July 9, 2013 Author Share Posted July 9, 2013 I'm digging into this and am getting a better understanding of how this works - thank you guys for pointing it out to me - I can totally see the value of this! I'm curious though about the FormInterface... class? interface? What does that do? I can build my forms with all of the arguments right inside the Form class, so why are those additional methods set inside FormInterface? Quote Link to comment Share on other sites More sharing options...
ignace Posted July 10, 2013 Share Posted July 10, 2013 The FormInterface is required so that you form and your formdecorator define the same methods/interface. Since you don't want to use the Formdecorator on it's own, but only allow it to decorate a form. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.