How to trim() data of a modelless form before validate in Cakephp 4 - trim

In cakephp 4, in the case of a form with Model Table I use the callback method ModelTable::beforeMarshal() to trim() data before validation, but where can I do that in the case of a Modelless Form ?

There's no events/callbacks for it, there's only Form.buildValidator, but this only applies when validation is actually happening, and on top of that, only when the buildValidator() method has been implemented.
If you want to do it generally, and before validation, then you could for example override \Cake\Form\Form::execute():
public function execute(array $data, array $options = []): bool
{
// modify $data here
return parent::execute($data, $options);
}

Related

Date field type default only when no object is passed

I'm trying my hand at my first Symfony application, but as I'm working with forms, something a little counterintuitive is happening.
As you can see from my code, I have a date field that defaults to the current day. But when I pass an object to the form, this default overrides the object's current date.
I know this is how it is supposed to happen ('The default values for form fields are taken directly from the underlying data structure (e.g. an entity or an array). The data option overrides this default value.', from http://symfony.com/doc/current/reference/forms/types/date.html#data).
Is there any way to suppress this behaviour and only show the default if there is NO object passed?
$builder
// other code
->add('date', 'date', array(
'data' => new \DateTime()
))
// other code
I would probably set it directly in my new Entity, not fixed in a form
class YourClass
{
private $date;
//...
public function __construct()
{
$this->date = new \DateTime;
}
}

handleRequest($request) does not work for "GET" method in Symfony 2

I am a noobie in Symfony2. The handleRequest() function does not work for "GET" method whereas same code works fine for "POST".
public function addAction(Request $request){
$std = new Student();
$form = $this->createForm(new StudentForm, $std,
array( 'method'=>'GET'));
$form->handleRequest($request);
if($form->isSubmitted()){
$std= $form->getData();
$em= $this->getDoctrine()->getManager();
$em->persist($std);
$em->flush();
return $this->render('target.twig');
}
return $this->render('target twig',
array('newStdForm'=> $form->createView(),));
}
The above code is not working but if I change 'method':'GET' to 'method':'POST', then it works fine.
Specify the form's method in the StudentForm class's buildForm method. Therefore, handleRequest will be able to grab the GET parameters.
class StudentForm
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->setMethod('GET');
}
}
I think it is because in POST requests, parameters are passed in the body of the HTTP request. And that handleRequest looks for those values inside the body of the request. But in a GET request, parameters are passed in the url directly. So I think that is why the handling doesn't work.
Usually we use GET to fetch a page or url and a POST to send info to server.
Are you sure your twig template is correct?
I faced this issue today.
Pierre Roland's answer is partially correct for the current version.
I checked the default "HttpFoundationRequestHandler" which is called in "handleRequest".
An explicit GET form will be considered "submitted" if:
the form has no name (if you use a form class for example).
the request query contains a parameter with the form's name.

Drupal custom form twice on same page, i need the errors displayed in the one submitted and not on both

I've a custom module that builds a form with a couple of fields, so far so good.
In one of my pages, i print this form twice (different blocks), the form gets the same "form_id", so when i submit one of them and get an error, both of them get the error highlighted, and the fields populated.
I want that only the form i submit gets the errors, is there a way to do this?
Thanks!!
For anyone interested, to do this you need to use the hook_forms.
This hook only gets called when the form_id passed to a drupal_get_form doesn't exist, this is important, if you want to use this, make sure your calls use a non existing form_id, for example:
//Defining the form:
function mx_wtransnet_form_contacto($form, &$form_state, $block = null, $formType = null) {
}
I want to use this form multiple times and get different error handlers, instead of loading my form (mx_transnet_form_contacto), i'll call a non existing one:
$form = drupal_get_form("mx_wtransnet_form_contacto_invalid", "contacto-mini");
Then i create my hook:
function mx_wtransnet_forms($form_id, $args) {
$forms = array();
if (strpos($form_id, '_contacto_') !==false) {
$forms[$form_id] = array(
'callback' => 'mx_wtransnet_form_contacto',
);
}
return $forms;
}
This function will catch all my druapl_get_form calls that don't exist, so i can process/direct them, in my example, what i do is simply check that the form_id contains contacto and then set the callback for this form to the original function.
In this case better to create another form with different "form_id" but with the same submit handler.
Another case: when you output same form twice on the page it also may get JS errors because ID of form elements are the same.
In case you don't repeat the form code and its submit handler(DRY principle), I would recommend create a custom function that has the form array
function form_my_custom($form_id){
$form['my_first_field'] = array();
$form['my_second_field'] = array();
$form['#attributes']['id'] = $form_id;
$form['my_submit_button'] = array(
'#submit' => array('my_custom_form_submit')
);
return $form;
}
function my_block1_form(){
return my_custom_form('my_form_id_1');
}
function my_block2_form(){
return my_custom_form('my_form_id_2');
}
function my_custom_form_submit(&$form, &$form_state){
// your submit handler.
}

How to customize symfony2 forms upon retrieval?

I have a case where we create registration for sports events.
The registration contains some fields specific to each sport. Some of which will be named similarly although they will be different for each sport. Example: "favorite position on the field":
For Basketball it would be a choice field between:
Point guard
Shooting guard
etc...
For baseball, it would be the same choice field but with some different choices available:
Pitcher
Infield
Outfield
...
When first creating the form (for display), the sport is passed as part of the data in the registration:
$registration = new Registration;
$registration->setEvent($event);
and $event->getSport(); would return the sport for that event.
So far so good, and adding a listener to the generation of my form, I can set only the fields specific to that sport:
public static function getSubscribedEvents()
{
return [FormEvents::POST_SET_DATA => 'preSetData'];
}
/**
* #param event DataEvent
*/
public function preSetData(DataEvent $event)
{
$form = $event->getForm();
if (null === $event->getData()) {
return;
}
// (The get event here means the real life sports gathering)
$sport = $event->getData()->getEvent()->getSport();
/**
* Then I customize the fields depending on the current sport
*/
}
The problem comes when the user submits this form back. In this case, $event->getData()->getEvent() is null.
The "event" (real life one) is a document_id field in the registration form (using MongoDB here).
If I listen to the ::BIND event instead of ::PRE_SET_DATA, then I can access everything, but it's too late to customize the form as it is already bound. ::PRE_BIND does the same as ::PRE_SET_DATA.
How can I correctly retrieve my Event and Sport Documents here in order to customize my form and validate it appropriately?
Why would you need an event to do such task? You can define the fields in the buildForm() action of the form class. To access the event object simply use $options['data']->getEvent()
So ... Finally found how to do this properly.It requires subscribing to two different events.
First time the form is built, some data is passed to it, therefore, the PRE_SET_DATA event contains that data and everything works fine as explained in the question.
On the moment the form is submitted, it is first created with NO data, therefore the data accessed in PRE_SET_DATA will be null. In this case we skip over the form customization:
public function preSetData(DataEvent $event)
{
$myEvent = $event->getData()->getEvent();
if (null === $myEvent) {
return;
}
$this->customizeForm();
}
This ensures that we don't run into issues when submitting the form and no data is passed, however getData() will return an empty object and not NULL.
Now, when the form is submitted, we will bind it to the data received. That's when we want to interfere. So we'll also subscribe to the PRE_BIND event:
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_BIND => 'preBind',
FormEvents::PRE_SET_DATA => 'preSetData',
];
}
In pre-bind, the data we receive is only an array of values and not an object graph.
But if we injected the object manager in our listener, then we can find our objects and work with them:
public function preBind(DataEvent $event)
{
$data = $event->getData();
$id = $data['event'];
$myEvent = $this->om
->getRepository('Acme\DemoBundle\Document\Event')
->find(new \MongoId($id));
if($myEvent === null){
$msg = 'The event %s could not be found';
throw new \Exception(sprintf($msg, $id));
}
$this->customizeForm();
}

symfony2 render part of form

First of all, thanks for all of you who take a look of this problem.
I got a FormType like userFormType.
class UserFormType extends AbstractType{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('address','collection', ....)
->add('group','entity',....)
->add('companies','collection',....);
...
}
}
So you see i got two collection in the user form. I create the form and i set the companies. When i wanna to modify ONLY the information of companies and the address, but not in touch with group. So i have to render a user form, but not some company forms or address forms. so i write a controller like this:
$user= $this->get('security.context')->getToken()->getUser();
$form =$this->createForm(new UserForm(),$user);
$request = $this->get('request');
if ('POST' == $request->getMethod()) {
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($user);
$em->flush();
....
}
}
Of course, i do not wanna to modify the group so in the twig template, i dot not render the group. the form is rendered correctly, but every time i try to submit it, it tell me:
Argument 1 passed to ... User->setGroup() must be an instance of Group ... null given
So I'm asking, what should i do?
The error specifically is because your method definition in User is probably:
public function setGroup(Group $group);
but in order to set it null it would need to be:
public function setGroup(Group $group = null);
That will fix the error but it still might not be what you want functionality wise. My question is, why have the group field on the form if you are not using it? You may need another form type or pass an option to the form to not include the group field during edits.

Resources