How can I dynamically add an attribute to each option tag generated by sfWidgetFormPropelChoice? - symfony1

In my symfony 1.4 application, I'm generating a select drop down as part of a form.
I later want to apply some jQuery (ddSlick) to that select to re-style it. In order to do so, I need to add an attribute to each option tag.
So for example, I'd like my select to generate:
<select id="demo-htmlselect">
<option value="0" data-imagesrc="http://dl.dropbox.com/u/40036711/Images/facebook-icon-32.png"
data-description="Description with Facebook">Facebook</option>
<option value="1" data-imagesrc="http://dl.dropbox.com/u/40036711/Images/twitter-icon-32.png"
data-description="Description with Twitter">Twitter</option>
<option value="2" selected="selected" data-imagesrc="http://dl.dropbox.com/u/40036711/Images/linkedin-icon-32.png"
data-description="Description with LinkedIn">LinkedIn</option>
<option value="3" data-imagesrc="http://dl.dropbox.com/u/40036711/Images/foursquare-icon-32.png"
data-description="Description with Foursquare">Foursquare</option>
Any suggestions on how to achieve this? Perhaps with an alternate or extended widget?

If you want to customize the select render, you should extend the default widget and made your own render.
So, create this file for exemple: /lib/widget/myWidgetFormSelect.class.php with:
class myWidgetFormSelect extends sfWidgetFormSelect
{
protected function getOptionsForSelect($value, $choices)
{
$mainAttributes = $this->attributes;
$this->attributes = array();
if (!is_array($value))
{
$value = array($value);
}
$value_set = array();
foreach ($value as $v)
{
$value_set[strval($v)] = true;
}
$options = array();
foreach ($choices as $key => $option)
{
$attributes = array(
'value' => self::escapeOnce($key),
'data-imagesrc' => self::escapeOnce($option['imagesrc']),
'data-description' => self::escapeOnce($option['description'])
);
if (isset($value_set[strval($key)]))
{
$attributes['selected'] = 'selected';
}
$options[] = $this->renderContentTag('option', self::escapeOnce($option['title']), $attributes);
}
$this->attributes = $mainAttributes;
return $options;
}
}
Then, you should trick the way you gave the $choices to the widget. Here, the widget wait for an array like that:
$choices = array(
0 => array(
'title' => 'Facebook',
'imagesrc' => 'http://dl.dropbox.com/u/40036711/Images/facebook-icon-32.png',
'description' => 'Description with Facebook',
),
1 => array(
'title' => 'Twitter',
'imagesrc' => 'http://dl.dropbox.com/u/40036711/Images/twitter-icon-32.png',
'description' => 'Description with Twitter',
),
);

Related

How to add class to fieldset?

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',
),
),
));

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.

zf2 Fieldset ignores naming

I have the following problem:
I created a fieldset and included it to the form using the following add statement:
// get dynamic additionals fields
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'additionals',
'options' => array(
'label' => 'Please choose categories for this product',
'count' => 3,
'should_create_template' => true,
'allow_add' => true,
'template_placeholder' => '__index__',
'use_as_base_fieldset' => true,
'target_element' => array(
'type' => 'Tool\Form\DeductionStepTwoAddForm'
)
)
));
Now what ZF2 does is generate the following:
<span data-template="<fieldset><legend>additional</legend><input type="hidden" name="additional_checked" value="0"><input type="checkbox" name="additional_checked" value="1"><input name="additional_name" type="text" class="validate" value=""><input name="additional_cost" type="text" class="validate" value=""><select name="additional_key"><option value="m2">m²</option>
<option value="mea">MEA</option>
<option value="pers">Pers.</option>
<option value="m3">m³ / Verbrauch</option>
<option value="units">Einheiten</option></select></fieldset>"></span>
This is how the Fieldset is created:
class DeductionStepTwoAddForm extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('additional');
$this->setLabel('additional');
$selectVal = array(
'm2' => 'm²',
'mea' => 'MEA',
'pers' => 'Pers.',
'm3' => 'm³ / Verbrauch',
'units' => 'Einheiten',
);
The problem: Check the name's of the form elements -> they are not indexed and will get overwritten. What am I doing wrong?
Solution: We forgot to prepare the form in the view before generating the output.

How to change the behavior of sfWidgetFormSelectRadio in symfony?

new sfWidgetFormSelectRadio(
array('choices' => $images)));
The above will render each option something like:
<input type="radio" name="name" value="image_path">
How to make it render this way with minimal code:
<input type="radio" name="name" value="image_path"><img src="image_path" />
This is untested and straight from me reading the Symfony API docs for that widget. You'll need to extend the sfWidgetFormSelectRadio class, call it something like myWidgetFormSelectRadio and stick it in lib/widget/myWidgetFormSelectRadio.class.php in your project.
Override the formatChoices() method like so:
class myWidgetFormSelectRadio extends sfWidgetFormSelectRadio
{
protected function formatChoices($name, $value, $choices, $attributes)
{
$inputs = array();
foreach ($choices as $key => $option)
{
$baseAttributes = array(
'name' => substr($name, 0, -2),
'type' => 'radio',
'value' => self::escapeOnce($key),
'id' => $id = $this->generateId($name, self::escapeOnce($key)),
);
if (strval($key) == strval($value === false ? 0 : $value))
{
$baseAttributes['checked'] = 'checked';
}
$inputs[$id] = array(
'input' =>
$this->renderTag('input', array_merge($baseAttributes, $attributes))
. $this->renderTag('img', array('src' => self::escapeOnce($key))),
'label' => $this->renderContentTag('label', self::escapeOnce($option), array('for' => $id)),
);
}
return call_user_func($this->getOption('formatter'), $this, $inputs);
}
}
so you're basically appending the img tag to the input.
In your form's configure() method you'll then need to switch from using sfWidgetFormSelectRadio to using myWidgetFormSelectRadio to use the new widget.
Let me know if this works ;-)

Resources