It's not clear to me how I should use the Symfony Form Component with FOSRestBundle for POST endpoints that I use to create resources.
Here is what I've got in my POST controller action:
//GuestController.php
public function cpostAction(Request $request)
{
$data = json_decode($request->getContent(), true);
$entity = new Guest();
$form = $this->createForm(GuestType::class, $entity);
$form->submit($data);
if ($form->isValid()) {
$dm = $this->getDoctrine()->getManager();
$dm->persist($entity);
$dm->flush();
return new Response('', Response::HTTP_CREATED);
}
return $form;
}
What I do is:
Send an application/json POST request to the endpoint (/guests);
Create a form instance that binds to an entity (Guest);
Due to the fact that I'm sending JSON, I need to json_decode the request body before submitting it to the form ($form->submit($data)).
The questions I have:
Do I really always have to json_decode() the Request content manually before submitting it to a Form? Can this process be somehow automated with FosRestBundle?
Is it possible to send application/x-www-form-urlencoded data to the controller action and have it handled with:
-
$form->handleRequest($request)
if ($form->isValid()) {
...
}
...
I couldn't get the above to work, the form instance was never submitted.
Is there any advantage of using the Form Component over using a ParamConverter together with the validator directly - here is the idea:
-
/**
* #ParamConverter("guest", converter="fos_rest.request_body")
*/
public function cpostAction(Guest $guest)
{
$violations = $this->getValidator()->validate($guest);
if ($violations->count()) {
return $this->view($violations, Codes::HTTP_BAD_REQUEST);
}
$this->persistAndFlush($guest);
return ....;
}
Thanks!
I'm trying to do the same thing to you and it's difficult to find some answers for this subject. I think today it's an important subject to develop website with api rest for mobile apps. Anyway!
Please find my answer below:
Do I really always have to json_decode() the Request content manually before submitting it to a Form?
No, i get data like this
$params = $request->query->all();
$user->setUsername($params['fos_user_registration_form']['username']);
Can this process be somehow automated with FosRestBundle?
I don't think so.
Is it possible to send application/x-www-form-urlencoded data to the controller action and have it handled with
Yes, but i'm currently trying to handle my form like that and I can not do it.
Is there any advantage of using the Form Component over using a ParamConverter together with the validator directly - here is the idea:
No
I'm guessing if I'm not going to only register the user with:
$userManager = $this->get('fos_user.user_manager');
and make my control manually.
I posted an issue here and i'm waiting:
https://github.com/FriendsOfSymfony/FOSUserBundle/issues/2405
did you manage to get further information ?
Related
Let's say I run a clothing web shop. I have an order form that my customers can place orders. A customer fills out their order for a t-shirt and submits the form. The problem is the t-shirt is out of stock.
This isn't a validation issue, because the order was valid, so i wouldn't create a custom validator.
I'm using the FOSRestBundle and trying to support both HTML and JSON. I'm also trying to accomplish a lot through domain events.
Here's my pseudo coded controller
class OrderController extends BaseOrderController
{
public function createAssetAction(Request $request)
{
$form = $this->getOrderCreateForm();
$form->handleRequest($request);
if ($form->isValid()) {
// should i wrap this in a try/catch?
$this->dispatch(OrderEvents::PRE_CREATE, $event = new FormEvent($form, $form->getData()));
// or check if the event is stopped and pull out an error message?
if ($order = $event->getData()) {
$this->getOrderRepository()->persist($order);
$this->dispatch(OrderEvents::POST_CREATE, new ResourceEvent($order));
$view = $this->routeRedirectView('my_orders', array(), Codes::HTTP_MOVED_PERMANENTLY);
$view->setData(array('order' => $order));
return $this->handleView($view);
}
}
$view = $this->view($form)
->setTemplateVar('form')
->setTemplate('OrderBundle:Order:newOrder.html.twig');
return $this->handleView($view);
}
}
I'm thinking i would check the inventory of the order in the OrderEvents::PRE_CREATE event. I don't know whether to throw an exception in an event listener if there is insufficient stock or stop the event propagation and add a message to the event.
If i threw an exception, i'd have to catch it in the controller so i could set the exception message as a flash message for an HTML request or just return the message directly for a JSON response.
If i did that, i'd have to wrap all event dispatching in the controller in try/catches, which seems wrong.
I've seen the LiipCacheControl bundle which converts flash messages to a cookie but that introduces state into my stateless REST API.
Anybody have suggestions?
so my question is: What would be the best way to return the html of a response as JSON encoded string?
I know there are the typical json action helpers but they dont fit my need.
What i need is a way to return the complete html of a view as a json string if the request is an ajax-request.
Because i want to change my app to only load the content of the main container and i also need to pass along a few other variables during the request i need to find a way to do so :)
Maybe someone has already had some experience with that!
I can propose another solution:
1 - create a plugins directory in application directory.
In this directory create the plugin POutput.php like that:
<?php
class Application_Plugin_POutput extends Zend_Controller_Plugin_Abstract
{
public function postDispatch(Zend_Controller_Request_Abstract $request)
{
if (Zend_Registry::get('Output_Json'))
Zend_Layout::getMvcInstance()->setLayout('layout_json');
}
}
2 - Create a new layout "layout_json.phtml" like that:
<?php
echo $this->json($this->layout()->content); ?>
You can see the documentation for options about this helper
3 - Call the plugin in the bootstrap like that:
protected function _initPlugins(){
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Application_Plugin_POutput());
}
4 - In the bootstrap, for example, initiate the variable Output_Json like that:
protected function _initJson(){
Zend_Registry::set('Output_Json', true); // true for Json output, False for Html output
}
With this method, you do not have to change all your controller. ;)
I hope that answers your question. :)
I do not know what is the best way, but I'll give you mine.
To send a json to ajax call, I do it in the controller. I disable the layout, view, and I send the json.
for example to send an array, I do:
public function fooAction() {
$this->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
...
//get array $auth with value $authentification
$auth = array('authentification' => $authentification);
return $this->_helper->json($auth);
}
And it works very well :)
If you want a Json response that interacts with your Model layer, you should use this:
return new JsonModel(array);
And if you need a simple response, you should use the Response class, like this:
$response = new Response();
$response->getHeaders()->addHeaders(
array(
'Content-type' => 'application/json'
));
$response->setContent($yourContent);
return $response;
For my application in Zend Framework 2 I created a 'search form' in my layout.phtml.
I did this in my Application module by adding the search form as an variable.
(Form location Application/src/Application/Form/SearchForm.php)
Application/Module.php:
public function setFormToView($e)
{
$searchForm = new SearchForm();
$viewModel = $e->getViewModel();
$viewModel->setVariables(array(
'searchForm' => $searchForm,
));
}
The form directs to an Action in another module, here I want to handle what to do with
the incoming query.
MyModule/src/MyModule/Controller/MyModuleController.php
public function dataAction()
{
$form = new SearchForm();
$form->get('submit')->setValue('Add');
$website = $this->params()->fromQuery('search');
return array('searchForm', $form);
}
Getting the query 'search' is no problem, it works well. No I want to use my inputFilter I created under 'Application/src/Application/Model/Search.php'.
I tried to add this in my 'dataAction()' but no results, but I even can't change the submit value as I tried in my example above.
How do I set this in the right way in this situation? Or else, what is the right situation to handle a search form in layout.phtml.
Thanks in advance,
Nick
I am fairly new to symfony and I have 2 fields relating to my table "Pages"; created_by and updated_by. These are related to the users table (sfGuardUser) as foreign keys. I want these to be hidden from the edit/new forms so I have set up the generator.yml file to not display these fields:
form:
display:
General: [name, template_id]
Meta: [meta_title, meta_description, meta_keywords]
Now I need to set the fields on the save. I have been searching for how to do this all day and tried a hundred methods. The method I have got working is this, in the actions class:
protected function processForm(sfWebRequest $request, sfForm $form)
{
$form_params = $request->getParameter($form->getName());
$form_params['updated_by'] = $this->getUser()->getGuardUser()->getId();
if ($form->getObject()->isNew()) $form_params['created_by'] = $this->getUser()->getGuardUser()->getId();
$form->bind($form_params, $request->getFiles($form->getName()));
So this works. But I get the feeling that ideally I shouldnt be modifying the web request, but instead modifying the form/object directly. However I havent had any success with things like:
$form->getObject()->setUpdatedBy($this->getUser()->getGuardUser());
If anyone could offer any advice on the best ways about solving this type of problem I would be very grateful.
Thanks,
Tom
After processing and saving the form you could set those fields on the object and re-save:
protected function processForm(sfWebRequest $request, sfForm $form)
{
$form->bind($request->getParameter($form->getName()));
if ($form->isValid())
{
$page = $form->save();
$user = $this->getUser()->getGuardUser();
$page->setUpdatedBy($user);
if (empty($page->created_by))
{
$page->setCreatedBy($user);
}
$page->save();
$this->getUser()->setFlash('notice', 'Successfully saved page.');
$this->redirect('#homepage');
}
}
There's also a Doctrine extension called Blameable that automatically sets edited_by and created_by fields on specified models. The Doctrine website is undergoing some reorganization but here is the cached page for the extension.
To process your form create a new object, set the fields then save.
$article = new Article();
$article->setName($request->getParameter($form->getName());
$article->setDescription($request->getParameter($form->getDescription());
$article->setMetaKeywords($request->getParameter($form->getMetaKeywords());
$article->save();
What you want to do is customize your form and unset the 'created_at' and 'updated_at' pieces of the form in configure
class SampleForm extends BaseSampleForm
{
public function configure()
{
unset(
$this['created_at'],
$this['updated_at']
);
}
}
Then they won't show up in the form and will get the values setup by the "Timestampable" behavior before being saved
http://stereointeractive.com/blog/2010/04/07/symfony-forms-hide-created_at-updated_at-columns/
I am building a form in Zend Framework 1.9 using subforms as well as Zend_JQuery being enabled on those forms. The form itself is fine and all the error checking etc is working as normal. But the issue I am having is that when I'm trying to retrieve the values in my controller, I'm receiving just the form entry for the last subform e.g.
My master form class (abbreviated for speed):
Master_Form extends Zend_Form
{
public function init()
{
ZendX_JQuery::enableForm($this);
$this->setAction('actioninhere')
...
->setAttrib('id', 'mainForm')
$sub_one = new Form_One();
$sub_one->setDecorators(... in here I add the jQuery as per the docs);
$this->addSubForm($sub_one, 'form-one');
$sub_two = new Form_Two();
$sub_two->setDecorators(... in here I add the jQuery as per the docs);
$this->addSubForm($sub_two, 'form-two');
}
}
So that all works as it should in the display and when I submit without filling in the required values, the correct errors are returned. However, in my controller I have this:
class My_Controller extends Zend_Controller_Action
{
public function createAction()
{
$request = $this->getRequest();
$form = new Master_Form();
if ($request->isPost()) {
if ($form->isValid($request->getPost()) {
// This is where I am having the problems
print_r($form->getValues());
}
}
}
}
When I submit this and it gets past isValid(), the $form->getValues() is only returning the elements from the second subform, not the entire form.
I recently ran into this problem. It seems to me that getValues is using array_merge, instead of array_merge_recursive, which does render to correct results. I submitted a bug report, but have not gotten any feedback on it yet.
I submitted a bug report (http://framework.zend.com/issues/browse/ZF-8078). Perhaps you want to vote on it?
I think that perhaps I must have been misunderstanding the way that the subforms work in Zend, and the code below helps me achieve what I wanted. None of my elements share names across subforms, but I guess this is why Zend_Form works this way.
In my controller I now have:
if($request->isPost()) {
if ($form->isValid($request->getPost()) {
$all_form_details = array();
foreach ($form->getSubForms() as $subform) {
$all_form_details = array_merge($all_form_details, $subform->getValues());
}
// Now I have one nice and tidy array to pass to my model. I know this
// could also be seen as model logic for a skinnier controller, but
// this is just to demonstrate it working.
print_r($all_form_details);
}
}
I have a same problem to get value from subforms I solve it with this but not my desire one
code:
in controller i get value with this code that 'rolesSubform' is my subform name
$this->_request->getParam ( 'rolesSubform' );
Encountered the same problem. Used post instead of getValues.
$post = $this->getRequest()->getPost();
There are times when getValues does not return the same values returned by $post.
Must be a getValues() bug.