How to replace a Form Element by a custom one in ZF2? - zend-framework2

I'm struggling with the MultipleCheckbox and particularly with the problem, that the form validation fails, if no checkbox of the list is selected. (See details here.)
To workaround the issue I want to replace the Zend\Form\Element\Checkbox by a custom one where I override the getInputSpecification() method:
Checkbox.php
namespace My\Form\Element;
use Zend\Form\Element\Checkbox as ZendCheckbox;
class Checkbox extends ZendCheckbox
{
public function getInputSpecification()
{
$spec = parent::getInputSpecification();
$spec['required'] = false;
return $spec;
}
}
module.config.php
return [
'form_elements' => [
'invokables' => [
'Zend\Form\Element\Checkbox' => 'My\Form\Element\Checkbox',
],
]
];
But my custom Checkbox class has not replaced the on of Zend. It's not ignored completely -- I see with Xdebug, that it's used in some cases (e.g. for a RadioButton element and for some single Checkbox elements). But in some other cases (particularly for the checkboxes of my MultiCheckbox) not.
What am I doing wrong? How to replace a Zend\Form\Element for the whole application?

Related

Multiple Symfony form types, all for same object, validate at once by faking submission

I have several FormTypes, all for the same entity type (Car, e.g.), each handles a different aspect (DoorsTyoe, EngineType, TrunkType, SeatsType). When the user has walked through all these forms, on different pages, I want to check before finally saving the results, once more all FormTypes.
So the idea was: Create a new FormType CarType, put all the different pieces on it (DoorsType, EngineType, ...), "fake a submit" and get the errors. But when I want to add the different "sub-forms", I cannot add them without specifying a "child name" for them. Is there any way around this? Right now, I create an array with all the child names, and have a the car object in a separate key:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add(
'doors',
DoorsType::class,
[
'getter' => function ($data, FormInterface $form): Car {
return $data['theCar'];
},
'setter' => function (array $data, Car $submittedData, FormInterface $form): void {
// just throw away what we get.
},
],
);
// And so on for EngineType etc.
}
Then I call this with:
$fooData = [
"doors" => "PLACEHOLDER",
"engine" => "PLACEHOLDER",
"theCar" => $car
];
$form = $this->formFactory->create($formType, $fooData, [
]);
$form->submit($fooData, true);
$errors = $form->getErrors(true, true);
dump($errors);
return $form->isValid();
My issue is the crazy structure with the $fooData array. I would prefer to work on $car only. When I change the DoorsType etc. to unmapped fields, the getters are not called so I assume they will not work at all?
Somehow I am missing a point...

Multi level embed form in symfony 1.4

I want to create multi-level embed form in symfony 1.4.
I have three forms:
1 - Question form,
2 - Option form and
3 - answer form.
In that, one question have many options fields and one option have many answers fields.
When I tried to add one more field in option criteria by rendering the HTML for answer fields using ajax and try to save form that time I am getting error Unexpected extra form field named "1"
And I have embedded Option form in Question form and Answer form in Option form respectively.
The below error is produce after submit form :
pick_type_option [0 [pick_type_answers [Unexpected extra form field named "1".]]]
Can any one please help me to find out the solution for this problem.
In advanced thanks
As it's unknown how many Option forms will be there during construction of the form (inside configure(), you have to override bind() method and add as many as needed:
class QuestionForm extends sfForm {
public function configure() {
$this->embedFormArray('question', new OptionForm);
}
private $formArray = array();
public function embedFormArray($name, \sfForm $form, $decorator = null, $innerDecorator = null, $options = array(), $attributes = array(), $labels = array()) {
$this->formArray[] = array($name, $form, $decorator, $innerDecorator, $options, $attributes, $labels);
}
public function bind(array $taintedValues = null, array $taintedFiles = null) {
foreach($this->formArray as $fa) {
$c = (isset($taintedValues[$fa[0]]) ? count($taintedValues[$fa[0]]) : 0);
array_splice($fa, 2, 0, [$c]);
call_user_func_array(array($this, 'embedFormForEach'), $fa);
}
return parent::bind($taintedValues, $taintedFiles);
}
}

How to use form view helper into custom view helper in zend framework 2

I am creating custom view helper. and i want to know how can i use formElement "Zend\Form\View\Helper\FormElement" in my own view helper. Here is my code.
use Zend\Form\ElementInterface;
use Zend\Form\FieldsetInterface;
use Zend\Form\View\Helper\FormElement;
use Zend\Form\View\Helper\AbstractHelper;
use Zend\View\Renderer\PhpRenderer;
class JudgeCareerViewHelper extends AbstractHelper {
private $output;
public function __invoke($formCollection) {
foreach ($formCollection as $elementOrFieldset) {
if ($elementOrFieldset instanceof FieldsetInterface) {
$obj = new FormElement();
$this->output .= $obj($elementOrFieldset->get('startServiceDate'));
} elseif ($elementOrFieldset instanceof ElementInterface) {
//set element markup
echo 'element';
}
};
echo $this->output;
die();
}
}
When i echo the output return from FormElement is empty. So i opened the zend "Zend\Form\View\Helper\FormElement" library to find out where is the problem. So i found that, below code return empty. I dont know what is the purpose of $renderer = $this->getView(); and how to get view.
$renderer = $this->getView();
if (!method_exists($renderer, 'plugin')) {
// Bail early if renderer is not pluggable
return '';
}
Purpose of creating custom view helper to generate my own markup (HTML) instead of zend buitin html.
So I found that, code $renderer = $this->getView() return empty.
You will need to ensure that the view helper is not directly instantiated using new but is called via the Zend\View\HelperPluginManager.
I suspect that the issue is because it is not correctly registered with the service manager as an invokable class.
// Module.php
public function getViewHelperConfig()
{
return array(
'invokables' => array(
'JudgeCareer'
=> 'FooModule\Form\View\Helper\JudgeCareerViewHelper',
),
);
}
This is to ensure that the Zend\View\Renderer\PhpRenderer is injected as the view.
Once the JudgeCareerViewHelper has the 'view' injected it would then be able to call other view plugins and have them loaded correctly, again via the HelperPluginManager.
The line:
$obj = new FormElement();
Should then be
$object = $this->getView()->plugin('form_element');
You may call any view helper from within your custom view helper by using
$this->getView()->anyRegisteredViewHelper();
So in your case to call the ZF2 built in form rendering view helpers you would use:
$this->getView()->formElement($element);
Whereby $element is your Form Element object (Select/Textarea/Checkbox etc)
You can of course also call explicit helpers for specific elements:
$this->getView()->formTextarea($textareaElement);

Selected attributes of genemu_jqueryselect2_entity not stored to database

I'm using genemu_jqueryselect2_entity for a multiple selection field within a form (located in an Sonata admin class) for a so called Uni (university) entity:
->add('courses', 'genemu_jqueryselect2_entity',array('multiple' => true, 'class' => 'PROJECT\UniBundle\Entity\Course'))
But the selected entries are not filled into my entity. With firebug I was able to detect, that the ids of the courses are passed correctly via POST.
Maybe the field is not correctly mapped to the Uni entity, but I have no idea why.
This is the adding method of my Uni entity, which doesn't even get called:
public function addCourse(\PROJECT\UniBundle\Entity\Course $courses)
{
$this->courses[] = $courses;
return $this;
}
How can I get the field to be mapped with the courses attribute of Uni? How could I debug this?
Any help will be appriciated!
Try writing that method like this:
public function addCourse(\PROJECT\UniBundle\Entity\Course $course)
{
$this->courses[] = $course;
$course->setUniversity($this); // Or similar.
return $this;
}
Otherwise foreign key is not set on a course row in the DB.
Try to create method setCourses
public function setCourses(\Doctrine\Common\Collections\Collection $courses)
{
$this->courses = $courses;
...
I don't know why, but the method addCourse isn't called.
Anyway, Tautrimas Pajarskas's answer was usefull to me so I gave an upvote.
The foreign key relationship was the necessary and missing part of my code.
I implemented it in the university sonata admin like this:
private function addUniToCourses ($university) {
foreach($university->getCourses() as $course) {
if(!$course->getUniversities()->contains($university)) {
$course->addUniversity($university);
}
}
}
public function prePersist($university) {
$this->addUniToCourses($university);
}
public function preUpdate($university) {
$this->addUniToCourses($university);
}
This was the solution to my problem.
I had the same problem a while ago: Symfony2, $form->bind() not calling adder methods of entity
Solution:
For the adder (addCourse()) to be called, you have to disable the by_reference option of the field:
->add('courses', 'genemu_jqueryselect2_entity',
array(
'by_reference' => false, // This line should do the trick
'multiple' => true,
'class' => 'PROJECT\UniBundle\Entity\Course'))

Symonfy 1.4 dynamic validation possible?

I'm trying to create a form that change the validation of a field based on the select option from the html form field.
Ex: if user select a option 1 from drop down field "options", I want the field "metric" to validate as sfValidatorInteger. If user select option 2 from field "options", I want the field "metric" to validate as sfValidatorEmail, etc.
So inside the public function configure() { I have the switch statement to capture the value of "options", and create the validator based on that value returned from the "options".
1.) How do I capture the value of "options" ? I've tried:
$this->getObject()->options
$this->getTaintedValues()
The only thing that's currently working for me is but it's not really MVC:
$params = sfcontext::getInstance()->getRequest()->getParameter('options');
2.) Once I've captured that information, how can I assign the value of "metric" to a different field? ("metric" is not a real column in db). So I need to assign the value of "metric" to different field such as "email", "age" ... Currently I'm handling this at the post validator like this, just wondering if I can assign value within the configure():
$this->validatorSchema->setPostValidator(new sfValidatorCallback(array('callback' => array($this, 'checkMetric'))));
public function checkMetric($validator, $values) {
}
Thanks!
You want to use a post validator. Try doing something like this in your form:
public function configure()
{
$choices = array('email', 'integer');
$this->setWidget('option', new sfWidgetFormChoice(array('choices' => $choices))); //option determines how field "dynamic_validation" is validated
$this->setValidator('option', new sfValidatorChoice(array('choices' => array_keys($choices)));
$this->setValidator('dynamic_validation', new sfValidatorPass()); //we're doing validation in the post validator
$this->mergePostValidator(new sfValidatorCallback(array(
'callback' => array($this, 'postValidatorCallback')
)));
}
public function postValidatorCallback($validator, $values, $arguments)
{
if ($values['option'] == 'email')
{
$validator = new sfValidatorEmail();
}
else //we know it's one of email or integer at this point because it was already validated
{
$validator = new sfValidatorInteger();
}
$values['dynamic_validation'] = $validator->clean($values['dynamic_validation']); //clean will throw exception if not valid
return $values;
}
1) In a post validator, values can be accessed by using the $values parameter. Just use $values['options'] and it should be fine... or did you want to access this values from another part of you code? $this->getObject()->widgetSchema['options'] should work too I think, once your form is bound to an object.
2) The configure() method is called at the end of the form constructor, so values are not bound nor accessible yet, unless you are initializing your form with an object from the db (which does not require any validation). But if you want to initialize your form from $_POST, a post validator is definitely the way to go IMHO.
I got the validation error to appear alongside the field by throwing a sfValidatorErrorSchema instead of a sfValidatorError.
$values['dynamic_validation'] = $validator->clean($values['dynamic_validation']);
…becomes…
try
{
$values['dynamic_validation'] = $validator->clean($values['dynamic_validation']);
}
catch(sfValidatorError $e)
{
$this->getErrorSchema()->addError($e, 'dynamic_validation');
throw $this->getErrorSchema();
}
Not sure if this is the best way to get this result, but it seems to be working for me at the moment.

Resources