How to add class to fieldset? - zend-framework2

I have a form in ZF2 with the following element being added:
$this->add(array(
'name' => 'animals',
'type' => 'radio',
'attributes' => array(
'id' => 'animals',
'class' => 'form-control',
),
'options' => array(
'label' => 'Favourite animal',
'options' => array(
'cat' => 'Cat',
'dog' => 'Dog',
'fish' => 'Fish',
),
),
));
And in my view script I have the folloing line:
<?php echo $this->formrow($form->get('animals')); ?>
Which is generating the following html:
<fieldset>
<legend>Favourite Animal</legend>
<label><input type="radio" name="animals" id="animals" class="form-control input-error" value="cat">Cat</label>
<label><input type="radio" name="animals" class="form-control input-error" value="dog">Dog</label>
<label><input type="radio" name="animals" class="form-control input-error" value="fish">Fish</label>
</fieldset>
How do I add a class to the fieldset?
I have tried adding the following to the options array, the attributes array, and as an option to the main array but it is not adding the class to the fieldset:
'fieldset_attributes' => array(
'class' => 'form-group',
),
[edit]
Looking into the code (\Zend\Form\View\Helper\FormRow::render) I've found this:
...
// Multicheckbox elements have to be handled differently as the HTML standard does not allow nested
// labels. The semantic way is to group them inside a fieldset
if ($type === 'multi_checkbox' || $type === 'radio' || $element instanceof MonthSelect ) {
$markup = sprintf('<fieldset><legend>%s</legend>%s</fieldset>', $label, $elementString);
}
...
Which means the only way to add a class to the fieldset (or legend if you wanted) is to extend the view helper.

I followed the answer as posted here (https://stackoverflow.com/a/27273068/351785).
From the answer (modified to suit my requirements):
Create the Application\Form\View\Helper\FormRow.php helper class like
below:
<?php
/**
* Extend zend form view helper formrow to allow class to be added to fieldset / legend
*/
namespace Application\Form\View\Helper;
use Zend\Form\View\Helper\FormRow as ZendFormRow;
class FormRow extends ZendFormRow
{
/**
* Utility form helper that renders a label (if it exists), an element and errors
*
* #param ElementInterface $element
* #throws \Zend\Form\Exception\DomainException
* #return string
*/
public function render(\Zend\Form\ElementInterface $element)
{
//... other code here
// Multicheckbox elements have to be handled differently as the HTML standard does not allow nested
// labels. The semantic way is to group them inside a fieldset
if ($type === 'multi_checkbox'
|| $type === 'radio'
|| $element instanceof MonthSelect
) {
$fieldset_class = $legend_class = '';
if($class = $element->getOption('fieldset_class')) {
$fieldset_class = sprintf(' class="%s"', $class);
}
if($class = $element->getOption('legend_class')) {
$legend_class = sprintf(' class="%s"', $class);
}
$markup = sprintf(
'<fieldset%s><legend%s>%s</legend>%s</fieldset>',
$fieldset_class,
$legend_class,
$label,
$elementString);
}
//... other code here
return $markup;
}
}
And override the factory in the onBootstrap() method of the Module.php
file like below:
namespace Application;
use Zend\Mvc\MvcEvent;
use Zend\View\HelperPluginManager;
class Module
{
/**
* On bootstrap for application module.
*
* #param MvcEvent $event
* #return void
*/
public function onBootstrap(MvcEvent $event)
{
$services = $event->getApplication()->getServiceManager();
// The magic happens here
$services->get('ViewHelperManager')->setFactory('formrow', function (HelperPluginManager $manager) {
return new \Application\Form\View\Helper\FormRow();
});
}
}
And add the classes as such:
$this->add(array(
'name' => 'animals',
'type' => 'radio',
'attributes' => array(
'id' => 'animals',
'class' => 'form-control',
),
'options' => array(
'label' => 'Favourite animal',
'fieldset_class' => 'form-group', //<== this
'legend_class' => 'form-legend', //<== and this
'options' => array(
'cat' => 'Cat',
'dog' => 'Dog',
'fish' => 'Fish',
),
),
));

Related

Symfony misd phone number bundle how to provide existing phone

I am using the great bundle https://github.com/misd-service-development/phone-number-bundle to store and retrieve phone number, but I have a problem when I want to pre-fill a form with it (not the same entity).
I have the following code in my Type
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use libphonenumber\PhoneNumberFormat;
use Misd\PhoneNumberBundle\Form\Type\PhoneNumberType;
class CreateAdType extends AbstractType
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
//Builds the form
public function buildForm(FormBuilderInterface $builder, array $options)
{
//Defines data
$user = $this->tokenStorage->getToken()->getUser();
$userEmail = is_object($user) ? $user->getEmail() : '';
$userPhone = is_object($user) ? $user->getPhone() : '';
//Defines fields
$builder
->add('phone', PhoneNumberType::class, array(
'label' => 'label.phone',
'disabled' => $disabled,
'widget' => PhoneNumberType::WIDGET_COUNTRY_CHOICE,
'country_choices' => array(
'FR',
),
))
->add('email', EmailType::class, array(
'label' => 'label.email',
'disabled' => $disabled,
'required' => true,
'attr' => array(
'placeholder' => 'placeholder.email',
'value' => $userEmail,
)))
;
}
public function finishView(FormView $view, FormInterface $form, array $options)
{
$view['phone']->children['number']->vars['data'] = '123456789';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Ad',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
// a unique key to help generate the secret token
'intention' => 'createAdForm',
));
}
public function getName()
{
return 'createAd';
}
}
The resulting html code is the following
<div class="form-group">
<label>Phone</label>
<div>
<div id="create_ad_phone" value="**THE_VALUE_APPEARS_HERE**">
<select id="create_ad_phone_country" name="create_ad[phone][country]">
<option value="FR" >France (+33)</option>
</select>
<input type="text" id="create_ad_phone_number" name="create_ad[phone][number]" value="**BUT_I_NEED_THE_VALUE_HERE**" />
</div>
</div>
</div>
This sets the value to the phone div, but not to the input phone_number, where I need it. Is there a way ?
Following in your form type, don't forget to use
...
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
...
public function finishView(FormView $view, FormInterface $form, array $options)
{
$view['phone']->children['number']->vars['value'] = $view['phone']->children['number']->vars['value'] ? $view['phone']->children['number']->vars['value'] : '+13008228232';
}
In you method add you can use choices for select
$builder
->add('phone', PhoneNumberType::class, array(
'widget' => PhoneNumberType::WIDGET_COUNTRY_CHOICE,
'country_choices' => array(
'FR',
),
'choices' => array('number' => $user->getPhone))
);
Are you rendering in twig template?
If so have you tried passing in "$user" object to twig template from controller, and then call the "getPhone" function in twig.
Like so, in your controller:
return $this->render('default/some_template.html.twig', array(
'form' => $form->createView(),
'user' => $user,
));
Then in your twig template somewhere:
{{ form_widget(form.phone, {'value' : user.getPhone}) }}
Try something like that to see if it works.
I haven't tried this - but it might work.

extending zfcuser with custom radio fields

i am using ZFcuser with zendframework 2 and want to extend the registration form to enable me to add a radio field.
i followed the tutorial here
it works ok and i am able to render and obtain the values for imput tags. the problem comes when i try to render a radio button. it only renders one of the values.
here is my code:
public function onBootstrap(MVCEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$em = $eventManager->getSharedManager();
$em->attach(
'ZfcUser\Form\RegisterFilter',
'init',
function($e)
{
$filter = $e->getTarget();
$filter->add(array(
'name' => 'accept',
'required' => FALSE
));
}
);
// custom form fields
$em->attach(
'ZfcUser\Form\Register',
'init',
function($e)
{
/* #var $form \ZfcUser\Form\Register */
$form = $e->getTarget();
$form->add(
array(
'name' => 'username',
'options' => array(
'label' => 'Username',
),
'attributes' => array(
'type' => 'text',
),
)
);
$form->add(array(
'type' => 'Zend\Form\Element\radio',
'name' => 'terms',
'options' => array(
'label' => 'Accept Terms',
'value_options' => array(
'1' => 'Yes',
'0' => 'No',
),
),
));
}
);
$zfcServiceEvents = $e->getApplication()->getServiceManager()->get('zfcuser_user_service')->getEventManager();
$zfcServiceEvents->attach('register', function($e) {
$form = $e->getParam('form');
$user = $e->getParam('user');
the HTML
the html from this output only render 1 input tag; i.e
<input type="radio" value="" name="terms">
however, i need to render two radio buttons; one for the yes, and one for the no
does anyone have any ideas how to do this; i really appreciate your help
This is a limitation of the current ZfcUser(v. 1.x), but is something that will be fixed in the coming version(v. 2.x).
In the meantime: overwrite the standard view with your own.

zendfamework 2 form validation set attributes to an input

Is it possible to do while validate a form with an input filter to not just send an error message but also set the border colour change to red?
something like this:
$this->add(array(
'name' => 'test',
'required' => true,
'attributes' => array(
'style' => 'border-color:red'
),
'validators' => array(
array(
'name' => 'NotEmpty',
'options' => array(
'messages' => array(
\Zend\Validator\NotEmpty::IS_EMPTY => 'Please fill me out'
)
)
)
)
));
One way of doing this would be to create your own view helper to render a form element and in that add error classes if the field has errors.
To start you need to create your view helper and if the form element has errors, then you add a class.
The view helper:
namespace Application\View\Helper;
use Zend\Form\View\Helper\FormInput as ZendFormInput;
use Zend\Form\ElementInterface;
use Zend\Form\Exception;
class FormInput extends ZendFormInput
{
/**
* Render a form <input> element from the provided $element
*
* #param ElementInterface $element
* #throws Exception\DomainException
* #return string
*/
public function render(ElementInterface $element)
{
$name = $element->getName();
if ($name === null || $name === '') {
throw new Exception\DomainException(sprintf(
'%s requires that the element has an assigned name; none discovered',
__METHOD__
));
}
$attributes = $element->getAttributes();
$attributes['name'] = $name;
$attributes['type'] = $this->getType($element);
$attributes['value'] = $element->getValue();
return sprintf(
'<input %s class="form-control '.(count($element->getMessages()) > 0 ? 'error-class' : '').'" %s',
$this->createAttributesString($attributes),
$this->getInlineClosingBracket()
);
}
}
And to your module.config.php you will need to add
'view_helpers' => array(
'invokables'=> array(
'formInput' => 'Application\View\Helper\FormInput',
),
),

How to get data from different model for select?

I have form with some attributes:
class ToraForm extends Form
{
public function __construct($name = null)
{
parent::__construct('tora');
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'id',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'name' => 'name',
'attributes' => array(
'type' => 'text',
'required' => true,
),
'options' => array(
'label' => 'name',
),
));
}
but I want add drop-down list with data taken from another model. How to do it?
There are several different approaches you can take. Ultimately your Form has a dependency, which needs to be injected. I have written an in-depth blog-post about the three most common use-cases for Form-Dependencies for a Select-List.
Zend\Form\Element\Select and Database-Values
My BlogPost covers the following scenarios:
Zend\Form\Element\Select via DbAdapter
Zend\Form\Element\Select via TableGateway
DoctrineModule\Form\Element\DoctrineObject via Doctrine2
Here i will demonstrate only the DbAdapter approach without much explanation. Please refer to my blogpost for the in-depth explanations.
public function formDbAdapterAction()
{
$vm = new ViewModel();
$vm->setTemplate('form-dependencies/form/form-db-adapter.phtml');
$dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');
$form = new DbAdapterForm($dbAdapter);
return $vm->setVariables(array(
'form' => $form
));
}
Then the respective Form Class:
class DbAdapterForm extends Form
{
protected $dbAdapter;
public function __construct(AdapterInterface $dbAdapter)
{
$this->setDbAdapter($dbAdapter);
parent::__construct('db-adapter-form');
$this->add(array(
'name' => 'db-select',
'type' => 'Zend\Form\Element\Select',
'options' => array(
'label' => 'Dynamic DbAdapter Select',
'value_options' => $this->getOptionsForSelect(),
'empty_option' => '--- please choose ---'
)
));
}
// more later...
// Also: create SETTER and GETTER for $dbAdapter!
}
And last but not least the DataProvider function:
public function getOptionsForSelect()
{
$dbAdapter = $this->getDbAdapter();
$sql = 'SELECT t0.id, t0.title FROM selectoptions t0 ORDER BY t0.title ASC';
$statement = $dbAdapter->query($sql);
$result = $statement->execute();
$selectData = array();
foreach ($result as $res) {
$selectData[$res['id']] = $res['title'];
}
return $selectData;
}
My use is like that:
Class Useful{
/**
* All languages
* #return array
*/
public static function getLanguages(){
return array(
'fr_DZ'=>'Algeria - Français',
'es_AR'=>'Argentina - Español',
'en_AU'=>'Australia - English',
'nl_BE'=>'België - Nederlands',
'fr_BE'=>'Belgique - Français',
'es_BO'=>'Bolivia - Español',
'bs_BA'=>'Bosna i Hercegovina - Hrvatski',
...
);
}
}
After I use like that:
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'languages',
'attributes'=>array(
'multiple'=>"multiple",
),
'options' => array(
'label' => 'My languages I speak',
'description' => '',
'value_options' => Useful::getLanguages()
),
));

dynamic dropdown get in zf2?

I want to put a dropdown in my project which is made in zf2... I wasted all day but I only got a static dropdown, not dynamic. Can anyone help me with this problem??
UserForm.php
$this->add(array(
'name' => 'group_name',
'type' => 'select',
'attributes' => array(
'id'=>'group_name',
'class'=>'large',
'options' => array('1=>php','2'=>'java'),
),
'options' => array(
'label' => '',
),
));
Thanks in advance for your valuabe answer.
Try this:
$this->add(array(
'name' => 'group_name',
'type' => 'select',
'attributes' => array(
'id'=>'group_name',
'class'=>'large',
),
'options' => array(
'label' => '',
'value_options' => array(
'1' => 'php',
'2' => 'java'
),
),
));
This is what i did:
In my constructor for my form
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'color',
'options' => array(
'empty_option' => 'Select a Color',
'value_options' => self::getColors(),
'label' => 'Color',
),
));
In the form class yet, i created this method:
public static function getColors() {
// access database here
//example return
return array(
'blue' => 'Blue',
'red' => 'Red',
);
}
In my view script:
<div class="form_element">
<?php $element = $form->get('color'); ?>
<label>
<?php echo $element->getOption('label'); ?>
</label>
<?php echo $this->formSelect($element); ?>
</div>
Think about it from a abstract level.
You have one Form
The Form needs Data from the outside
So ultimately your Form has a Dependency. Since we've learned from the official docs, there's two types of Dependency-Injection aka DI. Setter-Injection and Constructor-Injection. Personally(!) i use one or the other in those cases:
Constructor-Injection if the dependency is an absolute requirement for the functionality to work
Setter-Injection if the dependencies are more or less optional to extend already working stuff
In the case of your Form, it is a required dependency (because without it there is no populated Select-Element) hence i'll be giving you an example for Constructor-Injection.
Some action of your controller:
$sl = $this->getServiceLocator();
$dbA = $sl->get('Zend\Db\Adapter\Adapter');
$form = new SomeForm($dbA);
That's all for the form. The population now happens inside your Form. This is only an example and may need some fine-tuning, but you'll get the idea:
class SomeForm extends \Zend\Form
{
public function __construct(\Zend\Db\Adapter\Adapter $dbA)
{
parent::__construct('my-form-name');
// Create all the form elements and stuff
// Get Population data
$popData = array();
$result = $dbA->query('SELECT id, title FROM Categories', $dbA::QUERY_MODE_EXECUTE)->toArray();
foreach ($result as $cat) {
$popData[$cat['id'] = $cat['title'];
}
$selectElement = $this->getElement('select-element-name');
$selectElement->setValueOptions($popData);
}
}
Important: I HAVE NO CLUE ABOUT Zend\Db the above code is only for how i think it would work going by the docs! This is the part that would need some optimization probably. But all in all you'll get the idea of how it's done.
In your controller you can do something like below;
On my first example assuming that you have a Group Table. Then we're going to fetchAll the data in group table;
We need the id and name to be display in select options;
public function indexAction()
{
$groupTable = new GroupTable();
$groupList = $groupTable->fetchAll();
$groups = array();
foreach ($groupList as $list) {
$groups[$list->getId()] = $list->getName();
}
$form = new UserForm();
$form->get('group_name')->setAttributes(array(
'options' => $groups,
));
}
OR
in this example the grouplist is hardcoded;
public function indexAction()
{
$groupList = array('1' => 'PHP', '2' => 'JAVA', '3' => 'C#');
$groups = array();
foreach ($groupList as $id => $list) {
$groups[$id] = $list;
}
$form = new UserForm();
$form->get('group_name')->setAttributes(array(
'options' => $groups,
));
}
Then in your view script;
<?php
$form = $this->form;
echo $this->formRow($form->get('group_name'));
?>
Or you can right a controller helper, you may check this link http://www.resourcemode.com/me/?p=327
Just came across the same problem and had to take a look into zf2 source.
Here's a more OOP solution:
Inside the form constructor:
$this->add(array(
'name' => 'customer',
'type' => 'Zend\Form\Element\Select',
'attributes' => array(
'options' => array(
0 => 'Kunde',
)
),
'options' => array(
'label' => 'Kunde'
)));
inside the controller:
$view->form = new SearchForm();
$customers = $view->form->get('customer')->getValueOptions();
$customers[] = 'Kunde1';
$customers[] = 'Kunde2';
$customers[] = 'Kunde3';
$customers[] = 'Kunde4';
$view->form->get('customer')->setValueOptions($customers);

Resources