Silverstripe - FormSchema with submissions and validation - silverstripe

I'm trying to make use of the new FormSchema class in Silverstripe 4 but I'm having a tough time with the workflow for submitting the form. I've been able to return the schema and state, but when submitting the form back to the controller is where I run in to issues. Here is some example code:
class TestController extends Controller {
private static $allowed_actions = [
'schema',
'TestForm'
];
public function schema(HTTPRequest $request) {
$schema = new FormSchema();
return json_encode($schema->getMultipartSchema(['schema', 'state', 'errors'], "FormID", $this->TestForm()));
}
public function TestForm() {
$fields = FieldList::create(
TextField::create('Name', 'Name'),
EmailField::create('Email', 'Email')
);
$actions = FieldList::create(
FormAction::create('doTestSubmit', 'Submit')
);
$required = RequiredFields::create(['Name', 'Email']);
return Form::create($this, 'TestForm', $fields, $actions, $required);
}
public function doTestSubmit($data, $form) {
return json_encode(array('response' => 'The form validated and was submitted.'));
}
}
So in this scenario hitting /schema returns TestForm schema in json then the front end renders the form. Submitting the form sends the data back to /TestForm where it is validated. If the submission is valid it'll continue to doTestSubmit and return the response. That's great! But, if the submission is not valid then TestForm attempts to return the form and not the schema with the validation messages.
I first though about using a condition in TestForm() like if($this->request->isPOST()) or if($form->validationResult()->isValid()) but it doesn't seem like the proper way to handle it.
Any input or a simple code sample would be great.

I think you cannot use the standard Form > Form Post > Form Action way, you have to create an own Form Handler that validates the form. Like this (untested code):
public function doTestSubmit($request) {
$form = $this->TestForm()
$form->loadDataFrom($request->postVars);
if(!$form->validationResult()->isValid()) {
$formSchema = new FormSchema();
$state = $formSchema->getState($form);
return json_encode($state);
}
//Valid, continue
}

Related

how to fetch post of auth follower user with including all post

how should i fetch post of user who he follows and fetch all post of database with it and give some condition like follow user post should not be fetch in all post result it should be fetch first.....
my relation:
public function following()
{
return $this->belongsToMany('App\User', 'follower_following', 'follower_id', 'following_id')
->select('id', 'uname', 'name');
}
public function posts(){
return $this->hasMany(Post::class)
->orderBy('created_at');
}
public function user()
{
return $this->belongsTo('App\User','user_id','id');
}
i have tried this :
1.
$user = User::with('following.files')->get();
return response()->json(['data' => $user], 200,[],JSON_NUMERIC_CHECK);
2.
$follows = Auth::user()->following->pluck('id');
$post = Posts::whereIn('user_id',$follows)
->with('user')
->latest()
->limit(10)
->get();
just getting the auth user following post what to use to get all post with following user post

handle and personalize symfony no route found exception response

I have a controller that response a json data to another application , this is the controller code :
/**
*
* #Get("/getXXX/{id}")
*/
public function getDataAction($id,Request $request){
$ceService = $this->container->get('comptexpertcews.service');
$employeNumber= $request->get('employeNumber') ;
$url = $this->container->getParameter('serverUri') . $id;
$res = new Response();
$res->setContent($ceService->getCews($url, wsUrl::ws_Headers));
$res->headers->set('Content-TYpe','application/json; charset=UTF-8');
return $res;
}
The problem is by default , if you don't give id in the url , symfony rise exception : not route foundexception , what i want is to handle the exception and personalize with my owner response like sending
{"error" :" id undefined "}
instead of the long message expcetion of symfony
You have two simple options:
Don't use param converter, get you data from a repository and then you can wrap it in try catch and create your own exception/message
If this is something you want to do globally, you can implement an event listener that catches onKernelException event and work with it from there, e.g.:
public function onKernelException(GetResponseForExceptionEvent $event): void
{
$exception = $event->getException();
if ($exception instanceof NotFoundHttpException) {
$response = $this->resourceNotFoundResponse(json_encode($exception->getMessage()));
}
if (isset($response)) {
$event->setResponse($response);
}
}
You also need to register you listener as a service, see the documentation here http://symfony.com/doc/current/event_dispatcher.html

FOSRestBundle & invalid form CRF token

I'm trying to implement FOSRestBundle and Symfony forms.
I have found this tutorial but I have problem with this part
private function processForm(PageInterface $page, array $parameters, $method = "PUT")
{
$form = $this->formFactory->create(new PageType(), $page, array('method' => $method));
$form->submit($parameters, 'PATCH' !== $method);
if ($form->isValid()) { //form is not valid.
$page = $form->getData();
$this->om->persist($page);
$this->om->flush($page);
return $page;
}
throw new InvalidFormException('Invalid submitted data', $form);
}
ERROR: The CSRF token is invalid. Please try to resubmit the form.
Here is the controller from tutorial. And here is my class controller:
public function newAction(Request $request)
{
$form = new EntryType();
$newEntry = $this->container->get('entries.entry.handler')->post(
$request->request->get($form->getName())
);
return View::create()
->setStatusCode(200)
->setFormat('json')
->setSerializationContext(SerializationContext::create()->setGroups(array('list')))
->setData($newEntry);
}
Should I skip checking isValid() or fix this somehow? How?
OK, It is clear now. CRF verification (csrf_protection) should be disabled
CSRF token is invalid when calling rest post api from php Client
https://github.com/liuggio/symfony2-rest-api-the-best-2013-way/issues/1#issuecomment-31435232
CSRF validation needed or not when using RESTful API?
From part 3 of the tutorial :
It's possible to disable the CSRF based on the user’s role.
# app/config/config.yml
fos_rest:
disable_csrf_role: ROLE_API
# you can also try
# disable_csrf_role: IS_AUTHENTICATED_FULLY
See also this issue.

Add error to Symfony2 Form

I am trying to add error to form using FormError. Error must be displayed when user tries to create collection with existing name. But this code doesn't work, and I can't understand why
public function submitInObjectAction(Request $request)
{
$collection = new Collection();
$user = $this->getUser();
$form = $this->createForm(
new CollectionType(),
$collection
);
$form->handleRequest($request);
if ($form->isValid() && $form->isSubmitted()) {
$colname = $form["name"]->getData();
$existing = $this->getDoctrine()->getRepository('CollectionBundle:Collection')
->findBy(['name' => $colname, 'user' => $user]);
if ($existing != NULL) {
$error = new FormError("You already have collection with such name");
$form->get('name')->addError($error);
}
$em = $this->getDoctrine()->getManager();
$collection->setUser($user);
$em->persist($collection);
$em->flush();
return new JsonResponse([
'id' => $collection->getId(),
'name' => $collection->getName()
]);
}
}
I cannot use annotation on name field in Collection entity, because names must be unique only for particular user
I think it is too late in the chain. Form validation happens when you call $form->handleRequest() and by the time $form->isValid() is called your validation should be complete. It is better to add validation constraints further up the chain. See the Symfony guide on form validation and if necessary the validation component, for more info.
I would use annotations to set a unique constraint on the name field of the Collection entity in the CollectionBundle.
This not only validates this user input form, but any other form or component or bundle which uses CollectionBundle - and Doctrine will even prevent storage depending on the constraint leaving your database tidy!
EDIT: Another option for more advanced validation is writing a custom form event listener. Three events are dispatched when Form::handleRequest() or Form::submit() are called: FormEvents::PRE_SUBMIT, FormEvents::SUBMIT, FormEvents::POST_SUBMIT. This example also shows how to access the form itself.
$form = $formFactory->createBuilder()
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$user = $event->getData();
$form = $event->getForm();
// .. validation here
})
->getForm();

Symfony2 Doctrine - Flushing in kernel.response listener flushs bad data

In order to do some logging for my Symfony2 app, I created a service that logs any connection, here is the method called on kernel.response :
public function log(FilterResponseEvent $event)
{
$log = new Log();
$request = $event->getRequest();
$response = $event->getResponse();
//fill the Log entity with stuff from request & response data
$manager = $this->container->get('doctrine.orm.entity_manager');
$manager->persist($log);
$manager->flush();
}
All of this seems fine, however when I execute a test like this one (patch with empty data to trigger a failure):
$this->client->request(
'PATCH',
'/users/testificate',
array(
'firstName' => '',
)
);
Which calls this action :
protected function processForm($item, $method = 'PATCH')
{
$form = $this->createForm(new $this->form(), $item, array('method' => $method));
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$response = new Response();
// Set the `Location` header only when creating new resources
if ($method == 'POST') {
$response->setStatusCode(201);
$response->headers->set('Location',
$this->generateUrl(
'get_' . strtolower($class), array('slug' => $item->getId()),
true // absolute
)
);
}
else {
$response->setStatusCode(204);
}
$this->em->flush();
return $response;
}
$this->em->detach($item);
return RestView::create($form, 400);
}
Although the test fails, the entity is patched, and of course it must not.
After some search what I've learnt is:
The parameters enter the form validator
The validation fails, thus returning a 400 http code without flushing the entity
However during the validation process, the entity gets hydrated with the invalid data
When the service is called on kernel.response, the $manager->flush(); flush all the data... including the bad data provided by the PATCH test.
What I've tried thus far:
1) Do a $manager->clear(); before $manager->persist(); ... doesn't change anything
2) Do a $manager->detach($item); if the form validation failed... doesn't change anything
Thanks !
I recently stumbled across problems with flushing in kernel.response when upgrading from Doctrine 2.3.4 to the latest 2.4 branch. Try flusing the log entities from kernel.terminate. Leave any modifications to the Response in kernel.response.

Resources