Symfony disable form fields depending on user rol - forms

I'm developing an app in symfony for follow the workflow of a document. In the lifecycle of the document, take part various departments, I mean, people of each department with his own role inside de app (ROLE_USER, ROLE_SYSTEMS...). I have the same form for all of them but in the different states of the lifecycle of the document one of them has to complete specific fields and the rest will be disable or readonly.
I want to know how is the best way to do this in Symfony, check the role and disable the fileds the user can't edit in every moment of the lifecycle.
I have investigated and find something like this for my twig templates, but I will have to do this for each field and each role and I don't know if I can apply this to field attributes like disabled or readonly, is it?.
{{% if is_granted('ROLE_ADMIN') %}
Delete
{% endif %}
I also heard about voters but I don't have very clear how it works and if it is appropriate for my situation.
What do you think?

Easy but ugly way would be different form type classes (just to play around to find out which fields are necessary and which aren't, but it's optional).
The better way would be FormEvents like PRE_SET_DATA where you could add/remove (not sure if modifien also available).
Look here to find out more -> How to Dynamically Modify Forms Using Form Events
Basically you should try following:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Add your general fields which are available for all
//$builder->add(...)
// at the end add an EventListener
$builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPreSetData']);
}
and in the same form-class create the onPreSetData method:
public function onPreSetData(FormEvent $formEvent)
{
$form = $formEvent->getForm();
$entity = $formEvent->getData();
//if user is admin then $form->add(...) like above in with $buildForm->add(...)
}

Related

How to translate data models by the means of Symfony?

I would like to present another description for a general use case.
Form fields (or form types) can be generated like the following.
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class registration_form extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('address_street');
}
}
?>
The label “Address street” is accordingly displayed then on a test web page so far.
I would like to achieve that translated information will be shown on demand.
Thus I imagine that I need to pass dedicated parameters for the execution environment so that translated data will be retrieved from selected information sources and applied instead of the specified input field name.
Would you like to add any further ideas for such an application according to known key words?
national language support
internationalisation
localisation
translation
See also:
Symfony forms for Translatable Doctrine entities
How will the chances evolve to improve the software documentation for the involved function arguments so that data models (and corresponding form fields) can be translated in easier and safer ways?
I don't know if you took a look into FormType Field Reference.
You can use that configuration:
// instead of 'label' you can use 'label_format'
$builder->add('address_street', null, [
'label_format' => 'form.%name%',
]);
And Then, add the following code to your translations file:
form:
address_street: Your Translated Label # 'address_street' key replaced %name%

Removing ChoiceType transformers during Pre Submit events

I have a form with a collection subform inside. In the subform, there is a choiceType field for the attribute "resourceId". Its data are populated by ajax, with Select2 js plugin (because its data depends on another choice, in which you select the resource type).
In the specific case that i have already a value in resourceId choice, i can't validate my field :
transformationFailure: TransformationFailedException {#4328 ▼
#message: "Unable to reverse value for property path "resourceId": The choice "bd922d35fb828da6e39edf3c7927511c9a6be025" does not exist or is not unique"
This is due because i have to add the default value of the field by javascript (thanks Select2).
I need to cancel the validation on the field, but even if i use the ResetViewTransformers() in the BuildForm method and by rebuilding the field in the PreSubmit event, it still doesn't validate.
TL;DR : How can i cancel validation during PreSubmit event ? (only on my field if possible)
Found the solution. I was going in the wrong direction.
All i had to do was overriding the ChoiceType class and disable the ViewTransformers, then use the new class :
class NonTransformedChoiceType extends ChoiceType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->resetModelTransformers();
$builder->resetViewTransformers();
}
}
add some jquery and add the .ignore class to your specific form field. If you load whole form i suggest loading them seperately so that way you can easily add it.
$("#myform").validate({
ignore: ".ignore, :hidden"
})
Hidden will also make sure that any fields you do hide, for other reason will not be affacted and then give errors, reference: here

What alternatives are there to Yii active form?

I wish to build a form that does not create/update any active records. An example of such a form would be a search filter using check boxes for the user to select which categories to apply to the search.
Everything I read about forms in Yii is centred around the CActiveForm class. What is the Yii way to build forms that don't use active records?
If you want to embrace Yii's convenient form handling, you should use CActiveForm. It does not require CActiveRecord. But it always requires a model for your form data - which is a good thing, because this will keep the validation rules out of your view files. Instead of a CActiveRecord you could also build a simple model class from CFormModel.
class SomeForm extends CFormModel
{
public $name;
public $email;
public function rules()
{
return array(
array('name,email','required'),
array('email','email'),
);
}
}
Check the docs of the CHtml class. While it contains all the active* method, there are plain form methods in there as well, such as checkBox() or checkBoxList() that sound like what you're looking for.

Symfony 2.x Form Field Name

When I render a form, form Filed Name is given as an array. For example: search[item], search[keyword] etc. where search is name of the form.
I'm not great on working with forms but I think, the name should be rendered as simply, name="item" or name="keyword".
I've looked at all the documentation, customizing form rendering topic etc. but I can't find any way to change the default behaviour of Symfony form to render form filed name from 'search[item]' to 'item'.
This way, when I ask for the POST data, I can ask simply $this->getRequest()->request->get('item'), as I have to deal with lots of individual parameters.
Help would be great i) To figure out how to achieve what I want. ii) to let me know, why the name is rendered this way. is this the good practice?
Rather than accessing parameters from the Request object, you can bind the Request object to the form.
For example, in your controller method that you post your form to:
namespace Acme\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\Form\MyFormClass;
class MyFormController extends Controller
{
receiveFormAction(Request $request)
{
$form = new MyFormClass();
// you can specify that a route only accepts a post
// request in the routing definition
if ($request->isMethod('POST')) {
// this populates the form object with the data
// from the form submission
$form->bind($request);
if ( ! $form->isValid()) {
throw new \Exception('Invalid form');
}
// an array of the data the format you require
$data = $form->getData();
$data['item'];
$data['keyword'];
// etc.
}
}
}
The above is the way you should be handling forms in Symfony 2, and is how you can leverage the power that the forms component gives you, with validation etc.
Symfony supports multiple forms on a page. They might be instances of the same form or have similar field names. Having the fields for each form all together in an array makes this easy to do.

symfony2 forms custom fields

So, I have a form for editing blog articles.
Among other things I need to be able to edit article tags. They are stored as ArrayCollection inside my Blog entity. (ManyToMany cascade: persist,remove)
Now, Simfony handles this type of data with <select> tag and it works just fine for selecting, but I want to be able to remove and add tags too.
This is also possible and is very well explained in this Cookbook article: How to Embed a Collection of Forms
However, result of this tutorial is still not very elegant and I would love to have input box similar to StackOverflow tag box.
Since there are many already done solutions under free licences I decided to just use one of them, for example jQuery Tags Input.
Basically, all I need to do is run $('#tags_input_box').tagsInput() and it transforms it into SO-like tag box.
Now, I'm searching for the easiest way to bind some custom made input to my form and submit it back together with the rest of 'genuine' fields in a shape that will be understood by Symfony2.
Could anyone refer me to some document or give me some starting info where I should begin my research on this matter ?
It appears that plugin sends it in as a comma-separated string value.
Probably the easiest way would be to simply treat it as a single input in your form, and then split it up when you process the form.
// Entity to hold it in string form.
namespace Some\Entity;
class TagStringEntity {
protected $tagString;
// getTagString and setTagString
}
// Custom form type.
// Use this AbstractType in your form.
namespace Some\Form;
Symfony\Component\Form\AbstractType;
class TagType extends AbstractType {
public buildForm(FormBuilder $builder, array $options) {
$builder->add('tagString'); // will default to text field.
}
}
// In Controller
public function displayFormAction() {
// Join the tags into a single string.
$tagString = implode(',', $article->getTags()); // assuming it returns an array of strings.
$tagStringType = new TagStringType();
$tagStringType->setTagString($tagString);
// build form, etc...
}
public function checkFormAction() {
// ...
if ($form->isValid()) {
// Get the tag string, split it, and manually create your separated tag objects to store.
}
}
That's probably the cleanest and simplest way to do it using that jQuery plugin. Takes a bit of working around since you are turning multiple items into many and vice versa, but not too bad.