I'd like to tweak the Illuminate\Html\FormBuilder class in Laravel 4 to automatically include a CSS class with each input, corresponding to the type of input, e.g. <input type="radio" class="radio" />
I've created my own class, which extends the original:
namespace Shady;
class FormBuilder extends Illuminate\Html\FormBuilder {
public function input($type, $name, $value = null, $options = array()) {
$options['class'] = isset($options['class'])
? implode(' ', array_unique(array_merge(array($type), explode(' ', $options['class']))))
: $type;
parent::input($type, $name, $value, $options);
}
}
Now, the question is, how do I get Laravel 4 to use it?
I've had a look in app/config/app.php, and there's only a reference to the HTML Fascade class. I'm not sure how or where that translates into the Illuminate\Support\ServiceProvider\HtmlServiceProvider class, but that class hard-codes the FormBuilder class, ala
protected function registerFormBuilder()
{
$this->app['form'] = $this->app->share(function($app)
{
$form = new FormBuilder($app['html'], $app['url'], $app['session']->getToken());
return $form->setSessionStore($app['session']);
});
}
I'm wondering whether I can either trick it into loading my version of the class somehow? Or if not, how else do I add in this functionality smoothly and neatly? Cheers.
There's a new section in the docs about extending Laravel that should help: http://laravel.com/docs/extending
I also tried battling this issue as I wanted to do exactly the same as you, partly out of interest of extending core classes but also to stop repeating myself (DRY) from passing the same two classes in the second (options) array parameter each time I wanted a submit button, but to no avail.
Instead, I opted in the end to make a HTML macro, as others suggested above but here's an actual example for others who might come across this.
Example:
Save the following code to app/macros.php (this file doesn't exist by default so create it):
HTML::macro( 'submit', function( $value = null, $options = array() )
{
$options = array_merge( $options, array( 'class' => 'btn btn-info' ) );
return Form::input( 'submit', null, $value, $options );
});
Now include macros.php by adding require app_path().'/macros.php'; to the bottom of app/start/global.php.
And now in your views, instead of:
Form::submit( 'Submit', array( 'class' => 'btn btn-info' ) );
Use:
HTML::submit( 'Submit' );
And it will produce the same submit button, allowing the same parameters as Laravel by default but will automatically add btn and btn-info classes to the button, without you needing to repeat yourself each time. Hope this helps someone. =)
It's unnecessary to extend the HTML helper class. Instead, use CSS selectors to pick the elements.
As for making your own methods, you can use the HTML::macro()
See: http://laravel.com/api/source-class-Illuminate.Html.HtmlBuilder.html#32
And: http://laravel.com/api/source-class-Illuminate.Html.HtmlBuilder.html#390
Related
I have been trying to implement the solution in an earlier version of this question at:
How would I format Zend_Form_Element_Radio so the label follows the input?
By creating an extended helper MyLib_View_Helper_FormRadio but must be missing something obvious with this!
My question is how do I get Zend_Form_Element_Radio() to use this now instead of the version of the helper in Zend_View_Helper_FormRadio?
I thought initially that this was done by creating the element with
$radio = new MyLib_View_Helper_FormRadio();
but realised its not.
Changing the way the label and input are ordered in the Zend_View_Helper_FormRadio.php does the trick but I realise that the Zend files should not be altered.
If anyone can help me with t his I would be very grateful!
In your MyLib_View_Helper_FormRadio class, you have a method, like this (it's an example):
public function formRadioCustom($name, $value = null, $attribs = null,
$options = null, $listsep = "<br />\n"){...}
So to call it instead of formRadio of Zend_View_Helper_FormRadio, if I'm not mistaken, you have to do:
$element->addDecorators(array(array('ViewHelper',array('helper' => 'formRadioCustom'))));
And in your bootstrap add a method to add helper like this:
protected function _initViewHelpers() {
$view = new Zend_View();
$view->->addHelperPath('MyLib/View/Helper/', 'MyLib_View_Helper');
}
I created element in form object:
function createElement()
{
$template = new Zend_Form_Element_Hidden('field');
$template->addDecorator('ViewScript', array('placement' => 'prepend', 'viewModule' => 'admin', 'viewScript' => 'values.phtml'))
$this->addElement($template);
}
function setViewTemplate($values)
{
$view = new Zend_View();
$view->setScriptPath(APPLICATION_PATH . '/scripts/');
$view->assign('values', $values);
$this->getElement('field')->setView($view);
}
But in the view script 'values.phtml' I cannot get access to values like $this->values.
What I'm doing wrong here?
I know that it would be good to add own decorator, but it is interesting to use zends' decorators.
From the Zend Framework Documentation: Standard Form Decorators Shipped With Zend Framework Section
Zend_Form_Decorator_ViewScript
Additionally, all options passed to
the decorator via setOptions() that
are not used internally (such as
placement, separator, etc.) are passed
to the view script as view variables.
function setViewTemplate($values)
{
$this->getElement('field')
->getDecorator('ViewScript')
->setOptions('values', $values);
}
you can reslove it with using attribs
$template->setAttrib('key', 'value');
and in template
<?php echo $this->element->getAttrib('key'); ?>
I am building an application using Zend_Framework and I would like to ask you for an advice regarding Zend_Form. All forms on the site should be decorated with tables, instead of definition lists. My idea is that I should create a common form (with no elements) and then just instantiate it across the site and add elements as needed. This common form would need to modify the standard decorators, so that tables are used instead of definition lists. How do you suggest to do this? Override Zend_Form's addElement() method so that it alters decorators of new elements?
But there's another caveat. There should be an option of using a different set of decorators for a particular element, if needed. So I'm a bit puzzled how to do this. Do you have any advice?
There is no simple way to override the default decorators. The solution I use is to override all the elements and redefine the loadDefaultDecorators method.
The problem is that each element have a specific set of decorator. For example, the hidden element needs only the ViewHelper decorator while file element needs File, Errors, Description, HtmlTag (td), Label (th), HtmlTag (tr).
You can also use Zend_Form::setElementDecorators at the end of your init method (after calls to addElement). But you need to customize it for each form...
Use an intermediate class for your project wide configuration. You will then extend this class instead of Zend_Form
file My/Form.php
<?php
abstract class My_Form extends Zend_Form {
public function __construct ( $options = null ) {
parent::__construct($options);
$this->setElementDecorators(array(
// the base <input, <select, <textarea markup
'ViewHelper',
// wrap that into a <div class="input-wrap" />
array (
'HtmlTag',
array (
'tag' => 'div',
'class' => 'input-wrap',
)
),
// append errors in <ul/li>
'Errors',
// then prepend <label markup
'Label',
));
}
}
then in file My/Form/Demo.php
<?php
class My_Form_Demo extends My_Form {
public function init () {
// Your elements here
}
}
You can do this for specific element as well
file My/Form/Element/Group.php
<?php
class My_Form_Element_Group extends Zend_Form_Element_Select {
public function init () {
// Specific options
$this->addMultiOptions(array(
'A' => 'group A',
'B' => 'group B',
));
// This element doesn't need the div.input-wrap
$this->removeDecorator('HtmlTag');
}
}
I am using Zend Framework. For a particular form, there is not enough space to show the errors next to the form elements. Instead, I want to be able to display the errors above the form. I imagine I can accomplish this by passing $form->getErrorMessages() to the view but how do I disable error messages from being shown be each element?
The proposal above does not take into account that the default decorators may change. Instead of clearing the decorators and then reapplying all except the ones you do not need, it would be better to disable the decorators you don't need at form-initialization time, like:
class My_Form_Login extends Zend_Form
{
public function init()
{
$this->setMethod('post');
$username = new Zend_Form_Element_Text('auth_username');
$username->setLabel('Username')
->setRequired(true)
->addValidator('NotEmpty')
->removeDecorator('Errors')
->addErrorMessage("Please submit a username.");
etc.....
You could then, wherever you use the form, decide how to display the messages (if you're planning to display them apart from your form). Of course if they should be part of the form, just create a suitable decorator and add it to the form-element init method above. Here is a nice tutorial on form decorators from ZendCasts.com
Example of displaying messages apart from the form itself.
$elementMessages = $this->view->form->getMessages();
// if there actually are some messages
if (is_array($elementMessages))
{
foreach ($elementMessages as $element)
{
foreach ($element as $message)
{
$this->view->priorityMessenger($message, 'notice');
}
}
}
The priorityMessenger-helper used above can be found here: http://emanaton.com/code/php/zendprioritymessenger
You can add decorators to form elements using setElementDecorators. Zend_Form has a function called just after init entitled loadDefaultDecorators. In your subclass, you can override this like so:
/**
* Load the default decorators for forms.
*/
public function loadDefaultDecorators()
{
// -- wipe all
$this->clearDecorators();
// -- just add form elements
// -- this is the default
$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl')),
'Form'
));
// -- form element decorators
$this->setElementDecorators(array(
"ViewHelper",
array("Label"),
array("HtmlTag", array(
"tag" => "div",
"class" =>"element",
)),
));
return $this;
}
Assuming you added your elements in init, that applies those decorators to each element in the form. You'll note the absence of the "Errors" decorator in setElementDecorators. You could also try looping through the form elements and using removeDecorator to remove just the Errors decorator.
I'm using Symfony 1.2 in a standard Propel form class.
public function configure()
{
$this->setWidgets(array(
'graduate_job_title' => new sfWidgetFormInput( array(), array( 'maxlength' => 80, 'size' => 30, 'value' => '' ) )
));
//etc
}
However, I want the value of this field to come from the user information, which I'd normally access using $this->getUser()->getAttribute( '...' ). However, this doesn't seem to work in the form.
What should I be using?
It's a very bad idea to rely on the sfContext instance.
It's better to pass what you need during sfForm initialization in the options array parameter.
http://www.symfony-project.org/api/1_4/sfForm
__contruct method
for example in your action:
$form = new myForm(null,
array('attributeFoo' =>
$this->getUser()->getAttribute('attributeFoo'));
and then retrieve the value inside the form class:
$this->getOption('attributeFoo');
cirpo
Does that work?
sfContext::getInstance()->getUser()->getAttribute('...');
// Edit : See cirpo's recommandation on the use of sfContext instead.
If someone need the same in admin (backend) here is a solution:
http://blog.nevalon.de/en/wie-nutze-ich-die-rechteverwaltung-in-symfony-admin-generator-formularen-20100729
In Symfony 1.4, object $sf_user