Entity form field and validation in Symfony2? - symfony

In my form I have field of type entity. How to disable validation of that entity when the form is submitted? This entity is already persisted in database, so there is no point for validator to validate this entity from my point of view.
EDIT:
Code looks like this:
class SearchRequest
{
/**
* #ORM\ManyToOne(targetEntity="ArticlePattern")
* #ORM\JoinColumn(name="article_pattern_id", onDelete="CASCADE")
* #Assert\NotBlank
*/
private $articlePattern;
}
form field:
$builder
->add('articlePattern', 'entity', array('class' => 'LonbitItssBundle:ArticlePattern')
Validation groups won't work because what I want to accomplish is for validator to check the constraint #Assert\NotBlank on $articlePattern field, but I don't want him to check the constraints defined in class ArticlePattern. So in other words, I want prevent validator from descending inside the $articlePattern object, but I want the constraint placed on $articlePattern field itself to be validated.
Any idea how to do that?
EDIT2:
Validation groups will work. So the final solution is to add groups={"search_request"} constraint option (name of the group is arbitrary) to every field assertion in SearchRequest class, like this:
/**
* #Assert\NotBlank(groups={"search_request"})
*/
private $articlePattern;
That way, validation won't descend to associated objects (assuming they don't belong to - in this case - "search_request" group).

1) If you want to disable this field, just don't use it in your class form ! (and remove it from the template)
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('content')
->add('username')
//->add('yourEntity')
;
}
2) Or better, use validation groups. You create a validation_group which don't call your validator entity and then you use this group in your class form:
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Sybio\BuzzBundle\Entity\SearchRequest',
'csrf_protection' => true,
'csrf_field_name' => '_token',
'intention' => '865c0c0b4ab0e063e5caa3387c1a8741',
'validation_groups' => array('without_article_pattern_ckecking'),
);
}
In your Entity class:
/**
* #ORM\ManyToOne(targetEntity="ArticlePattern")
* #ORM\JoinColumn(name="article_pattern_id", onDelete="CASCADE")
* #Assert\NotBlank(
* groups={"without_article_pattern_ckecking"}
* )
*/
private $articlePattern;
Your form will only validate validators that refer to without_article_pattern_ckecking group, so it should not test validators inside your ArticlePattern entity if they don't have this validation group.
I hope this helps you !

Related

Symfony Form - sorting data by changing sort option in dropdown list

I'm sorry for the title but I don't know how to define it good. I'd like my app to sort data (in Doctrine) depending on the sort option which user select it in form dropdown list.
The data I mentioned above are stored inside my Doctrine Entity which I called it Flashcards and the Flashcards Entity contains properties which them must be sorted by option that user select in dropdown. Flashcards Entity looks like the following (I gave only a few properties for simplicity):
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Words", inversedBy="flashcards")
* #ORM\JoinColumn(nullable=false)
*/
private $words;
/**
* #ORM\Column(type="datetimetz")
*/
private $creation_date;
Now the controller's code for form is usual, like the Symfony Doc say:
// code for form inside FlashcardController
$flashcard = new Flashcards();
$form = $this->createForm(FlashcardType::class, $flashcard);
And inside FlashcardController render() method I call createView() method on $form object.
The form code is placed inside FlashcardType class and contains code for mentioned above dropdown list and its options. It looks like this (for simplicity I gave only the methods):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('sortBy', ChoiceType::class, [
'label' => 'Sort by',
'choices' => [
'Date increase' => 1,
'Date decrease' => 2,
'Word alphabetically' => 3,
'Word not alphabetically' => 4
]
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Flashcards::class
]);
}
So as you see the dropdown options include sorting by: date increase, date decrease, word alphabetically, word not alphabetically. User choose one of them in view. So manipulating data by user does NOT change value of this data represented by properties. Changing sort option in dropdown should change Doctrine query to sort by user-changed value. I have no idea how to achieve this in Symfony. Could you give me plese some tips how to make it?
Thank you in advance for answers!
If I understood correctly you have a few options:
First: wire javascript / jquery to select input and submit the form when user changes order(it should reload the form applying the filter, you can set any data user used in the 'previous' page using RequestStack to get the form fields from request / query depending on the method)
Second: get all the nodes you display record and try to manipulate the order based on the select value every time user changes the value
Tell me if this is what you meant else correct me and I'll try to provide the answer

Symfony form has ManyToOne field but still requires the __tostring() function

In a Symfony entity that stores the address information of a client I have made a ManyToOne connection with a entity that contains all countries. So the entity has de following link:
/**
* #var string
*
* #ORM\Column(name="country_code", type="string", length=2)
*
* #ORM\ManyToOne(targetEntity="Country")
* #ORM\JoinColumn(name="country_code", referencedColumnName="country_code")
*/
private $countryCode;
In the form generated of this entity I have defined it like this:
->add('countryCode', 'entity', array(
'class' => 'MyBundle:Country',
'choice_label' => 'name_en',
'choice_value' => 'country_code',
'data' => 'nl',
))
So it does not store the primary key but the country_code a two letter code like "nl" for the Netherlands.
Then I have to add __tostring() code to make it work, but why is that? I though the __tostring function would not be required anymore as there is already a ManyToOne connection.
public function __toString()
{
return strval($this->countryCode);
}
First in your entity you just have to write this :
/**
* #ORM\ManyToOne(targetEntity="PathTo/YourBundle/Entity/Country")
*/
private $country;
Then $country will be a reference to a Country Entity (it's ID in the DB),
and will allow you to acces ALL IT'S FIELDS.
After that in your form you should not use
->add('country', 'entity', array(....
As this syntax is deprecated and use instead :
use Symfony\Bridge\Doctrine\Form\Type\EntityType; //don't forget it on top of your form file
->add('country', EntityType::class, array(....
As you probably want to order your countries by alphabetical order you will use a query to do so and eventually your code may look like :
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
...
->add('country', EntityType::class, array(
'class' => 'MyBundle:Country',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.name_en', 'ASC');
},
'choice_label' => 'name_en',
'data' => 'nl'
))
you normally don't have to worry about choice_value, which will be the unique ID of the chosen country : when you'll acces your Client Entity, you will do :
$client->getCountry()->getCountryCode() ;
to get the country code.
I guess you are missing something about how database stores the relationships.
The first problem: you don't need this:
#ORM\Column(name="country_code", type="string", length=2)
... Because #ORM\ManyToOne already describes the schema for this property.
And here we meet the second problem: #ORM\ManyToOne creates an integer column, which contains an ID of the appropriate Country entity. Which means, when you create a form field for this property, it doesn't know which property should it render as a string representation, because your address book (or whatever you called it) entity stores only digits, not the country code.

Form Entity Type Checkboxes Does Not Remove Unchecked Items

I am using two entities having a ManyToMany relation, Jobsite having a workers property and vice versa. I have a form JobsiteType which propose a list of workers to select or not, it works quite welle with multiple select : select or unselect are well propagated in the db, but it does not work when using checkboxes : unselect item does not remove it from relation table.
I am using Symfony v 2.8, here is few details :
// Entity Jobsite
class Jobsite {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="UserWorker", inversedBy="jobsites", cascade={"all"})
* #ORM\JoinTable(name="jobsite_worker")
*/
protected $workers;
...
// Entity Worker
class UserWorker {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="Jobsite", mappedBy="workers", cascade={"all"})
*/
protected $jobsites;
...
// JobsiteType
public function buildForm(FormBuilderInterface $builder, array $options) {
...
$builder
->add('workers', EntityType::class, array(
'class' => 'MyBundle:UserWorker',
'choice_label' => 'name',
'choices_as_values' => true,
'by_reference' => false,
'multiple' => true,
'expanded' => true,// if false, multiple select is working almost well !
'label' => 'label.workers',
'required' => false,
'choices' => $workers,// list of possible workers
))
...
So does anybody knows what is wrong with checkboxes style (multiple + expanded) ? I know that unchecked inputs send nothing, so does it means I have to manually loop trough workers in a form event to apply the right changes ? Well I notice also that it does not work with multiple select box when no item is selected, it is probably related to the same 'nothing sended if empty' effect.
Well, for multiple select, it is needed to check if the field is set, if not apply an empty array as the field data, then data propagation is ok, empty set will remove items from the relation table...
But it does not resolve multiple checkboxes :(
$builder
...
->addEventListener( FormEvents::PRE_SUBMIT, function(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
// the trick : allow empty selection (reset list)
if(!isset($data['workers'])){ $data['workers'] = [];}
$event->setData($data);
}
)
EDIT RIGHT ANSWER
So simple as a submit flag that remove empty things
// contoller
$form->submit($request, true);// second arg true is the trick

How specify parameter's format in Nelmio ApiDocBundle

I use input property of #ApiDoc annotation for specifieng of parameters of my api that are form's fields.
* #ApiDoc(
* section="User",
* resource=true,
* input={
* "class"="Nik\UserBundle\Form\UserType",
* },
* ....
data_class of form is a entity that have constraint validation for properties.
I expect that nelmio api doc specify parameter format as validation constraints of entity, but format are empty.
How can i specify parameter formats in nelmio ApiDocBundle?
EDIT:
maybe i write a bad question.
we can specify parsers for input & output, if we no specify parser for these, it call all parser for input & output, then all parser are called for UserType.
nelmio have a parser named ValidationParser that have a method named parseConstraint that set format for input & output, but this method is not called for my document, why?
Format column is designed only for datetime, date and choice types.
For datetime and date it represents the date format like Y-m-d H:i:s and the array of choices for choice.
I haven't found any documentation about it, so I had to look through the source code. This is FormTypeParser class, the place where FormType is actually parsed and and you can see how format field is set.
In FormTypeParserTest class you can see how to use it. Just pass the string parameter with format name for one of the available types and the parser will handle it.
UPDATE: You are to define your constrains inside your FormType class.
For example:
class TestType extends AbstractType
{
/**
* #Assert\Type("string")
* #Assert\Length(min="10", max="255")
* #Assert\Regex("/^[^<>]+$/i")
*/
private $title;
/**
* #Assert\Type("string")
* #Assert\Length(min="10", max="255")
* #Assert\Regex("/^[^<>]+$/i")
*/
private $content;
/**
* #Assert\Date()
*/
private $created;
public function getName()
{
return 'test';
}
}
will be parsed into:
ValidationParser in
doParse() method finds all constrains defined in your FormType class and then executes parseConstraint() method for each of them.
You can also use FormTypeParser as I described above. For example:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('created', 'date', array('label' => 'Created', 'format' => 'yyyy-MM-dd'))
->add('color', 'choice', array('label' => 'Color', 'choices' => array('grey' => '#CCCCCC', 'red' => '#FF0000')))
->add('save', 'submit');
}
will be parsed as:
Hope it helps now!
i submit a pull request that use validation metadata for format property.
you can see this PR here
As you can see here you can specify filter in annotation like it done documentation example.
Here part of this example:
/**
* This is the documentation description of your method, it will appear
* on a specific pane. It will read all the text until the first
* annotation.
*
* #ApiDoc(
* resource=true,
* description="This is a description of your API method",
* filters={
* {"name"="a-filter", "dataType"="integer"},
* {"name"="another-filter", "dataType"="string", "pattern"="(foo|bar) ASC|DESC"}
* }
* )
*/
public function getAction()
{
}

Symfony2 form component - date widget not working with mongodb

Form field declaration:
$builder->add('birthday', 'date', array('label' => 'Data urodzenia:', 'years' => range(date('Y'), date('Y')-100)), 'required' => false);
Corresponding document field declaration:
/**
* #ODM\Field(type="date")
*/
protected $birthday;
/**
* Set bitrhday
*
* #param \DateTime $birthday
*/
public function setBirthday(\DateTime $birthday)
{
$this->birthday = $birthday;
}
/**
* Get bitrhday
*
* #return \DateTime $birthday
*/
public function getBirthday()
{
return $this->birthday;
}
When I set data in form value is saved to database correctly, but widget isn`t populated with data form database field when I visit form page after saving.
Other fields in form works as expected.
It`s sf2 bug or my mistake?
First, birthday seems to be misspelled. Setbitrhday & Getbitrhday
Second, ODM does not support the DateTime field type. ODM only supports the field type Date. See ODM Field Type Reference
ORM, on the otherhand does support the DateTime field type. See ORM Field Type Reference

Resources