Symfony2 : render form as a string - forms

I am trying to render a form from a string in Symfony2 and get the result in a variable.
I tried this solution, Symfony 2 Twig form functions not available but I am getting an error (service 'twig.form.renderer' not avaliable). So I changed the addExtension part (not sure about the classes).
Here is my code :
$env = new \Twig_Environment(new \Twig_Loader_String());
$rendererEngine = new \Symfony\Bridge\Twig\Form\TwigRendererEngine();
$env->addExtension(new \Symfony\Bridge\Twig\Extension\FormExtension(new \Symfony\Bridge\Twig\Form\TwigRenderer($rendererEngine)));
$form = $this->createFormBuilder()->add('testField', 'text');
$form = $form->getForm()->createView();
return $env->render(
'{{ form(form) }}',
array('form' => $form)
);
Now I am getting "Fatal error: Uncaught exception 'Symfony\Component\Form\Exception\LogicException' with message 'No block "form" found while rendering the form"

For twig templation i use this code:
$twig = $this->container->get('twig');
$twig->initRuntime();
return JsonResponse::create(array(
'success' => false,
'form' => $twig->getExtension('form')->renderer->searchAndRenderBlock($form->createView(), 'widget'),
));
for php template engine:
$this->container->get('templating.helper.form')->widget($form->createView());

You should include form_div_layout where form block is defined.

Related

How to integrate more than one form in one controller -> error at submit - Symfony 4.*

I'd like to know how can I put more than one form in one controller?
I did this, see the code below, in my controller which contains 2 forms; 1 to add a horse, and one to add a group of horses:
/*Add a horse*/
$cheval = new Chevaux();
$cheval->setParticulier($user->getParticulier());
$formAddHorse = $this->createFormBuilder($cheval)
->add('nom_cheval', TextType::class)
->add('save', SubmitType::class, array('label' => 'Ajouter'))
->getForm();
$formAddHorse->handleRequest($request);
if ($formAddHorse->isSubmitted() && $formAddHorse->isValid()) {
$cheval = $formAddHorse->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($cheval);
$em->flush();
return $this->redirectToRoute('app_acc');
}
/*add a group of horses*/
$team=new Team();
$team->setUser($user->getParticulier());
$formAddTeam = $this->createFormBuilder($team)
->add('profession', EntityType::class, array(
'class' => Profession::class,
'choice_label' => 'nom_prof',
'expanded' => true))
->add('save', SubmitType::class, array('label' => 'Nouvelle Team'))
->getForm();
$formAddTeam->handleRequest($request);
if ($formAddTeam->isSubmitted() && $formAddTeam->isValid()) {
$team = $formAddTeam->getData();
$em = $this->getDoctrine()->getManager();
/*
* Traitement des données
*/
return $this->redirectToRoute('app_acc');
}
and in the template :
<h2> -------Add a horse------ </h2>
{{ form_start(formHorse) }}
{{ form_widget(formHorse) }}
{{ form_end(formHorse) }}
<h2> ------------------------------ </h2>
<h2> -------Add team------ </h2>
{{ form_start(formTeam) }}
{{ form_widget(formTeam) }}
{{ form_end(formTeam) }}
<h2> ------------------------------ </h2>
However when I submit one of the forms, it seems that both are send. So it throws the error:
This form should not contain extra fields
in the form I didn't submit.
The data are send in the database properly, but the error is here and tells me that I am doing something wrong. How do I do to do this the right way?
I had a case like that once, here is what I did:
$form = $this->createForm(ProfileForm::class, $user);
$formPassword = $this->createForm(ChangePasswordForm::class, $user);
$form->handleRequest($request);
$formPassword->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
// My code to save some things...
}
}
if ($formPassword->isSubmitted()) {
if ($formPassword->isValid()) {
// My code to save some things...
}
}
It works for me but It seems like what you did, at first sight, though.
Maybe it's how you handle it in your template ?
You are looking for allow_extra_fields option which can be used like so.
$formBuilder = $this->createFormBuilder($team, array('allow_extra_fields' =>true))...
or like so for FormType class
public function setDefaultOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'allow_extra_fields' => true
)
);
}
This option allows you to submit some extra data, not specified in a form builder. As official documentation says
Usually, if you submit extra fields that aren't configured in your
form, you'll get a "This form should not contain extra fields."
validation error.
You can silence this validation error by enabling the
allow_extra_fields option on the form.

Symfony2 form unchecked checkbox not taken into account, why?

When I send a form with an unchecked checkbox, if the related entity property equals true, then it does not change to false.
The other way round (setting property to true when the form is sent with a checked checkbox) works fine, as well as all the forms other fields saving.
Here is how I build the form and declare the related property:
// --- Form creation function EntityType::buildForm() ---
$builder->add('secret', 'checkbox', array( 'required' => false ));
// --- Entity related property, Entity.php file ---
/** #ORM\Column(name="secret", type="boolean") */
protected $secret;
EDIT: The issue happens because the form is submitted using a PATCH request.
In Symfony, the Form::submit method is called by a Request Handler with this line:
$form->submit($data, 'PATCH' !== $method);
As a result the Form::submit $clearMissing parameter is set to false in the case of a PATCH request, thus leaving the non-sent fields to their old value.
But I do not know how to solve the problem. If I explicitely pass a JSON {secret: false} to the Symfony framework when the checkbox is not checked, it will interpret it as the "false" string and consider that a true value, thus considering the checkbox checked...
NB. I have exactly the same issue with an array of checkboxes using a choice field type (with multiple and extended to true) linked to a Doctrine Simple Array property: as soon as a given checkbox has been sent once as checked, it is impossible to set back the related property to false with subsequent unchecked submissions.
Non of above-mentioned didn't help me.
So, I am using this...
Explanation
Resolution for this issue when "PATCH" method was used, was to add additional hidden "timestamp" field inside of a form type and to have it next to the checkbox of issue in twig file. This is needed to pass something along with the checkbox, that would definitely change - time-stamp will change.
Next thing was to use PRE_SUBMIT event and to wait for form field to arrive and if it not set, I would set it manually... Works fine, and I don't mind extra code...
FormType
$builder
...
->add('some_checkbox')
->add('time_stamp', 'hidden', ['mapped' => false, 'data' => time()])
...
Twig
{{ form_widget(form.time_stamp) }}
{{ form_widget(form.some_checkbox) }}
PRE_SUBMIT event in builder
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use($options) {
$data = $event->getData();
$form = $event->getForm();
if (!$data) {
return;
}
/* Note that PATCH method is added as an option for "createForm"
* method, inside of your controller
*/
if ($options["method"]=="PATCH" && !isset($data['some_checkbox'])) {
$form->getData()->setSomeCheckbox(false);//adding missing checkbox, since it didn't arrive through submit.. grrr
}
});
The issue happens because the form is submitted using a PATCH request.
This has lead to open this Symfony issue.
As explained, one workaround is to explicitely send a specific reserved value (for instance the string '__false') when the checkbox is unchecked (instead of sending nothing), and replace this value by 'null' using a custom data transformer in the form type:
// MyEntityFormType.php -- buildForm method
$builder->add('mycheckbox', ...);
$builder->get('mycheckbox')
->addViewTransformer(new CallbackTransformer(
function ($normalizedFormat) {
return $normalizedFormat;
},
function ($submittedFormat) {
return ( $submittedFormat === '__false' ) ? null : $submittedFormat;
}
));
The case with the 'choice' field can't be solved the same way. It is actually a bug of Symfony, dealt with in this issue.
What version of Symfony are you using?
There should exist some code dedicated to the situation you're writing about, in vendor/symfony/symfony/src/Symfony/Component/Form/Form.php, in Form::submit():
// Treat false as NULL to support binding false to checkboxes.
// Don't convert NULL to a string here in order to determine later
// whether an empty value has been submitted or whether no value has
// been submitted at all. This is important for processing checkboxes
// and radio buttons with empty values.
if (false === $submittedData) {
$submittedData = null;
} elseif (is_scalar($submittedData)) {
$submittedData = (string) $submittedData;
}
Located at lines 525-534 for me. Could you check this works properly for you?
Another lead would be a custom form subscriber that do not work exactly as intended - by overwriting the provided value.
It's probably because the field isn't required on you schema. you can provide a default value to the checkbox with the following:
$builder->add('secret', 'checkbox', array(
'required' => false,
'empty_data' => false
));
See here or here
This solution works for me.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('isActive', CheckboxType::class, array(
'required' => false
))
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $e {
$entity = $e->getData();
$form = $e->getForm();
$isActive = empty($_POST[$form->getName()]['isActive']) ? false : true;
$entity->setIsActive($isActive);
});
}
Another possibility is to add a hidden element and make this with Javascript. It will fail in 0.1 % of the people that use a browser without javascript.
This is a simple example for a multiple checkboxes FormType element:
->add('ranges', ChoiceType::class, array(
'label' => 'Range',
'multiple' => true,
'expanded' => true,
'choices' => array(
null => 'None',
'B1' => 'My range',
)
))
<script>
$(document).ready(function() {
function updateDynRanges(object) {
if (object.prop("checked")) {
document.getElementById('ranges_0').checked = 0;
} else {
document.getElementById('ranges_0').checked = 1;
}
}
// On page onload
$('#ranges_1').change(function() {
updateDynRanges($(this));
});
updateDynRanges($('ranges_1'));
}
</script>
If after testing works you can just add a visibility:false to the second checkbox.
Twig template:
{{ form_label(form.dynamicRanges) }}<br>
{{ form_widget(form.dynamicRanges[1]) }}
<div class="hidden">{{ form_widget(form.ranges[0]) }}</div>
Looks like an ugly workaround, but I just wanted to compete with the other ugly suggested workarounds, in this case mostly updating the twig template.

Phalcon use Phalcon\Tag OR Phalcon\Forms for creating forms

I searched up and down but couldn't find which one is better Phalcon\Tag OR Phalcon\Forms for creating forms.
Both classes have functionality to create form elements. But I found there are some handy tags in the Phalcon\Tag, for example Phalcon\Tag::emailField() or Phalcon\Tag::dateField(array())
Phalcon documentation says:
"Phalcon\Forms is a component that aid the developer in the creation
and maintenance of forms in web applications."
"Phalcon\Tag is designed to simplify building of HTML tags. It
provides a set of helpers to generate HTML in a dynamic way."
Can anybody help me with the pros and cons of using both the methods.
Thanks
In simple meaning Phalcon\Tag are used to design only html (users view). but for validation && adding rules to the form you need to use phalcon\forms i will show you an example of phalcon\forums below
NEW FORM CLASS:
use Phalcon\Forms\Form,
Phalcon\Forms\Element\Password,
Phalcon\Forms\Element\Email as Emailfield,
Phalcon\Forms\Element\Check,
Phalcon\Forms\Element\Hidden,
Phalcon\Validation\Validator\PresenceOf,
Phalcon\Validation\Validator\Identical,
Phalcon\Validation\Validator\Email;
class LoginForm extends Form
{
public function initialize()
{
$email = new Emailfield('email', array(
'placeholder' => 'Type your Email'
));
$email->setLabel('E-Mail');
$email->setFilters('email');
$email->addValidators(array(
new PresenceOf(array(
'message' => 'E-mail is required'
)),
new Email(array(
'message' => 'E-mail is not valid'
))
));
$this->add($email);
$password = new Password('password', array(
'placeholder' => 'Type your Password'
));
$password->setLabel('Password');
$password->setFilters(array('striptags', 'string'));
$password->addValidators(array(
new PresenceOf(array(
'message' => 'Password required'
))
));
$this->add($password);
//Remember
$long_login = new Check('long_login', array(
'value' => 'yes'
));
$long_login->setLabel('Keep me logged in');
$this->add($long_login);
// CSRF
$csrf = new Hidden('csrf');
$csrf->addValidator(new Identical(array(
'value' => $this->security->getSessionToken(),
'message' => 'CSRF validation failed'
)));
// $this->add($csrf);
}
}
In Controller:
$form = new LoginForm();
if (!empty($_POST)) {
if (!$form->isValid($_POST)) {
$errors = array();
foreach ($form->getMessages() as $message) {
$errors[] = $message;
}
if (!empty($errors))
$this->flash->error(join('<br/>', $errors));
} else {
//Login Continues
}
}
$this->view->setVar('form', $form);
To convert this form to html below is the code:
<div class="form-group">
{{ form.label('email',['class': 'control-label']) }}
{{ form.render('email', ['class': 'form-control input-md']) }}
</div>
<div class="form-group">
{{ form.label('password',['class': 'control-label']) }}
{{ form.render('password', ['class': 'form-control input-md']) }}
</div>
<div class="checkbox">
{{ form.render('long_login') }}
{{ form.label('long_login') }}
</div>
Really great example in general but I'm struggling with the flash message. After a quick google search I was more confused after reading the documentation. Some say that $this->flash->output() should be placed to the view to see the flash messages. Though this causes errors and I believe it's something from the past. Can somebody tell me what flash method should I place to view to see the flash messages or Where I'm going wrong?
EDIT: Well I managed to get \Phalcon\Flash\Session to work and I believe it's event more suitable for me than Direct. For that you need to register flash service in Dependency Injector in config/services.php. I also replaces classes with bootstrap classes.
$di->set('flash', function() {
return new Phalcon\Flash\Session(array(
'error'=>'text-danger',
'warning'=>'text-warning',
'notice'=>'text-info',
'success'=>'text-success'
));
});
It depends on which you are working.
If we say, you need to insert lots of data with lots of validations and it may change during progress then its far better to use "Phalcon\Forms"
As they are very dynamic.
Means you can change text-box with select box very easily without touching template.
You can add validations without worrying about template and other stuff.
And for-most reusable you can reuse form if you need.
so if there is less data then you are free to use anyone of that but more suggestible is "Phalcon\Forms" its very dynamic and structural.

Symfony2 autocomplete form bundle

I use this bundle: GenemuFormBundle
I install it due to all information on this site.
But it still dont work.
Here is my type form:
$builder
->add('PermitsCompany', 'genemu_jqueryautocompleter_entity', array(
'route_name' => 'ajax_company',
'class' => 'MainCoreBundle:Company',
'property'=>'name'
))
;
Here is my routing:
ajax_company:
defaults: { _controller: MainAdminBundle:Permits:ajaxCompany}
pattern: /ajax_company/
type: annotation
And here is my controller:
/**
* #Route("/ajax_company", name="ajax_company")
*/
public function ajaxCompanyAction(Request $request)
{
$permits = $this->getDoctrine()->getRepository('MainCoreBundle:Company')->findAll();
$json = array();
foreach ($permits as $permit) {
$json[] = array(
'label' => $permit->getName(),
'value' => $permit->getId()
);
}
$response = new Response(json_encode($json));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
I have no idea what I am doing wrong. I have no error. But autocomplete did not work.
When i go to route /ajax_company/ i can see values from data base like here:
[{"property":"Company 1","value":1},{"property":"Company 2","value":2},{"Company":"Company 3","value":3},{"property":"Company 4","value":4}]
Did I add forget something in twig? I have only form_widget
Try including form_javascript or form_stylesheet, in your twig template.
From https://github.com/genemu/GenemuFormBundle#template:
Template
You use GenemuFormBundle and you seen that it does not work! Maybe you
have forgotten form_javascript or form_stylesheet.
The principle is to separate the javascript, stylesheet and html. This
allows better integration of web pages.

Load Form from module into custom page template

I have successfully added my own form (from the same module) into my custom template, but now I wish to load the taxonomy add term form (used by ubercart I think for product categories in the catalog vocab) into my template.
I have gotten this far with my module - filename simpleadmin.module
/**
* #file
* A module to simplify the admin by replacing add/edit node pages
*/
function simpleadmin_menu() {
$items['admin/products/categories/add'] = array(
'title' => 'Add Category',
'page callback' => 'simpleadmin_category_add',
'access arguments' => array('access administration pages'),
'menu_name' => 'menu-store',
);
return $items;
}
function simpleadmin_category_add() {
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
$output = drupal_get_form('taxonomy_form_term');
return theme('simpleadmin_category_add', array('categoryform' => $output));
}
function simpleadmin_theme() {
return array(
'simpleadmin_category_add' => array(
'template' => 'simpleadmin-template',
'variables' => array('categoryform' => NULL),
'render element' => 'form',
),
);
}
?>
And as for the theme file itself - filename simpleadmin-template.tpl.php, only very simple at the moment until I get the form to load into it:
<div>
This is the form template ABOVE the form
</div>
<?php
dpm($categoryform);
print drupal_render($categoryform);
?>
<div>
This is the form template BELOW the form
</div>
Its telling me that it is
Trying to get property of non-object in taxonomy_form_term()
and throwing up an error. Should I be using node_add() and passing the nodetype?
To render a taxonomy term form, the function should be able to know the vocabulary to which it belongs to. Otherwise how would it know which form to show? I think this is the proper way to do it.
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
if ($vocabulary = taxonomy_vocabulary_machine_name_load('vocabulary_name')) {
$form = drupal_get_form('taxonomy_form_term', $vocabulary);
return theme('simpleadmin_category_add', array('categoryform' => $form));
}
To redirect your form use hook_form_alter
function yourmodule_form_alter(&$form, &$form_state, $form_id) {
//get your vocabulary id or use print_r or dpm for proper validation
if($form_id == 'taxonomy_form_term' && $form['#vocabulary']['vid'] = '7' ){
$form['#submit'][] = 'onix_sections_form_submit';
}
}
function yourmodule_form_submit($form, &$form_state) {
$form_state['redirect'] = 'user';
}