Remove form namespace in Symfony2 form (for REST API) - php

I'm designing REST API with Symfony2.
For POST and PUT request i'm using a FormType. Something like :
class EmailType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('subject', 'textarea')
[...]
;
}
public function getName()
{
return 'email';
}
}
But when I POST, i'm must pass fields with a namespace like :
{
"email": {
"subject": "subject"
}
}
But I don't want this top-level namespace !
Any ideas ?

A form type has to have a name because if you register it as a service tagged as a form type, you need to somehow reference it. In the following code snippet, email is the name of the form type:
$form = $this->formFactory->create('email', $email);
That's why you have to return a name in the form type class:
public function getName()
{
return 'email';
}
So, instead of creating a form type without a name, just create a form — a particular instance of that form type — with an empty name:
$form = $this->formFactory->createNamed(null, 'email', $email);
An empty string — '' — instead of null works as well.

I've used Symfony forms for JSON based APIs. You just need to change your getName() method to return '':
public function getName()
{
return '';
}
This, cobined with the FOSRestBundle, made working with POSTed data very easy.

Related

separate one form into 2 independent forms in symfony FOS

I created a login system in symfony with the possibility to register only for those who own a so called "client number", right now my problem is the registration form where user has basically to type in at first his client number which is going to be validated first if it exists in a database and then after submitting it, orm fetches some data about the company which possesses the client number and the application forwards user to another form where he has to type in other details like his username and password. So far right now I have it in one form and I created it after as being described in tutorial like with Registration Controller, Registration Form Handler and Registration Type, right now for example in Registration Type it looks like this
<?php
// src/AppBundle/Form/RegistrationType.php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
class RegistrationType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('name');
// $builder->add('ClientNr'); <-- I want to check it in another form before i come to registration
$builder->add('lanr');
$builder->add('personal_key', HiddenType::class, array(
'data' => $this->getID()));
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
// Or for Symfony < 2.8
// return 'fos_user_registration';
}
public function getBlockPrefix()
{
return 'app_user_registration';
}
// For Symfony 2.x
public function getName()
{
return $this->getBlockPrefix();
}
public function getID()
{
return $random = random_int(10000,99999);
}
as far as I know I have again to create files like "PreRegistration Controller, PreRegistrationFormHandler and PreType" or what are the exact steps to realize my idea?

Hidden fields are still listed from database in cakephp 3

I am getting the records from my database in two different points, using "get" and "find" methods. The problem is that when I am using "get", "first" or "last" the hidden fields aren't displayed (Its ok), but when I am using "find" they are still there.
<?php
//My Plugin in /plugins/Comunica/Files/src/Model/Entity/File.php
namespace Comunica\Files\Model\Entity;
use Cake\ORM\Entity;
class File extends Entity
{
protected $_hidden = ['password'];
protected $_virtual = ['protected'];
protected function _getProtected(){
return empty($this->_properties['protected']) ? false : true;
}
}
The Call Method:
<?php
$this->Files->find()->toArray();
Again. It is right when calling just one record (first, last, call), It's just wrong when trying with method "find". Any one knows how to solve this?
I have found an answer for this problem. The find returns an object that owns the entities of every result, so that you can convert them by using the "findAll" method inside the table's class.
<?php
//My Plugin in /plugins/Comunica/Files/src/Model/Entity/File.php
namespace Comunica\Files\Model\Entity;
use Cake\ORM\Entity;
use Cake\ORM\Query;//Include this class to manipulate the results
class File extends Entity
{
protected $_hidden = ['password'];
protected $_virtual = ['protected'];
protected function _getProtected(){
return empty($this->_properties['protected']) ? false : true;
}
//New formatation code
public function findAll(Query $query, array $options)
{
return $query->formatResults(function ($results) {
return $results->map(function($row) {
$row['upload_date'] = $this->dateTimeConvert($row['upload_date']);
return $row->toArray();
});
});
}
}
I solved it like this:
My main aim was to exclude hidden fields by default and have a way to explicitly get Entitys including hidden fields if I need them.
ModelsTable.php
public function beforeFind(Event $event, Query $query){
//ATTENTION: if password field is excluded we have to bypass for Auth-Component to work
if(array_key_exists('password',$_REQUEST)){
return $event;
}
$protected = $this->newEntity()->hidden;
$tableSchema = $this->schema();
$fields = $tableSchema->columns();
foreach($fields as $key => $name){
if(in_array($name,$protected)){
unset($fields[$key]);
}
}
$query->select($fields);
return $event;
}
Model.php
protected $_hidden = [
'password',
'otherSecret'
];
protected function _getHidden(){
return $this->_hidden;
}
To receive hidden fields you can simple add ->select('password') to your query, but to make it more nice I added a custom finder
ModelsTable.php
public function findSecrets(Query $query, array $options)
{
$tableSchema = $this->schema();
$fields = $tableSchema->columns();
return $query->select($fields);
}
Now you can build a query like this to receive Entity including hidden fields:
ModelsController.php
$secretModels = $this->Models->find()->find('secrets');
or whatever query you loke, simply add the custom finder
NOTE: is does not work with ->get($id) so you have to use ->findById($id)->find('secrets')->first()
I'm happy to know what you think about this solution or what you would change - feel free to commend :-)

Symfony: how to get a parameter from the route within a FormType?

I'm trying to access the parameter page from the current route within a FormType. It works in a Controller, but not in a FormType. I'd like to avoid passing the parameter like /?page=1 and prefer /page/1.
routing.yml
my_route:
path: /data/page/{page}
defaults:
_controller: MyBundle:MyController:myAction
src/myBundle/Form/Type/MyFormType.php
class MyFormType extends AbstractType {
// ...
public function buildForm(FormBuilderInterface $builder, array $options) {
$request = Request::createFromGlobals();
$page = $request->query->get('page');
echo "page: $page"; // empty
// ...
}
}
I'd like to avoid to pass the parameter through the Controller to the FormType and prefer to access it directly within the FormType.
Any ideas?
Thanks in advance!
Edit:
Regarding the selected answer; the page attribute is accessible via $request->attributes->get('page'), not via $request->query->get('page').
I'm not sure the answer provided by Yonel is the best because you inject a dependency to your form.
It has some drawbacks and the major one IMHO is that it will make the test difficult as the dependency on the page parameter is hidden.
A better solution will be to add it as form option to your form.
The request object is already available in your controller and you are probably creating your form this way :
$form = $this->createForm(WhateverFormType::class, $entity)
Using the createForm method, you can inject a third argument which are the options (i.e additional data) you want to pass to your form.
So in your controller :
$page = $request->query->get('page');
$form = $this->createForm(WhateverFormType::class, $entity, ['page' => $page]);
And in your form type, follow the example given in this answer for the same question : https://stackoverflow.com/a/10922788/2721918
You need to inject the request stack service into form type to do that:
class MyFormType extends AbstractType
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$request = $this->requestStack->getCurrentRequest();
//do something: for example hide/show fields from request parameters
}
}
Register the form type and their dependencies:
services:
app.form.type.myform:
class: AppBundle\Form\Type\MyFormType
arguments: ['#request_stack']
tags:
- { name: form.type }
However, is recommended instead to create the new option to pass all variables that you need for your form type.
It is not necessary to inject the request stack service in the construct of your formtype. You can access to the requestStack by using form event listener like that:
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$form->getConfig()->getRequestHandler();
// do what you need...
});

How to map two symfony form field to one model method?

Hi stackexchange users,
I have a data (model) class which has two methods which look like this:
class ContactDetails {
public function setWebsite($address, $type) {
//do something...
}
public function getWebsite($type) {
//do something...
}
}
Now I want to create a form where the user can input a website address and choose a type (e.g. "private" or "business") for the address.
To make this possible I have created a custom form type like this
class ContactDetailsType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('type', 'text') //better: choice, but for the sake of demo...
->add('website', 'text')
;
}
public function getName() {
return 'ContactDetailsType';
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver
->setDefaults(array(
'data_class' => 'ContactDetails',
));
}
}
The controller then looks like this:
public function indexAction(Request $request) {
//generate completely new cost unit
$costunit = new ContactDetails();
//generate form
$form = $this->createForm(new ContactDetailsType(), $costunit);
$form->add('save', 'submit');
$form->handleRequest($request);
if ($form->isValid()) {
//yay!
}
}
This obviously doesn't work, as the form component doesn't know how to map these two fields from the type to the data model class.
Question: What is the best practise to map the data of two fields of a form to one method call in a data model class and vice-versa?
On your place a i would make both fields virtual in form and then use event listener to set data in entity.
Info about form events

Using Symfony Form 2.3 in Silex

I'm trying to build a Symfony form (in Silex) by name. Using the configuration below, I believe I should be able to call $form = $app['form.factory']->createBuilder('address');, however the FormRegistry cannot find a form of this type.
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormTypeExtensionInterface;
class AddressType extends AbstractType implements FormTypeExtensionInterface
{
public function getName()
{
return 'address';
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('addressee', 'text');
// .. fields ..
$builder->add('country', 'text');
}
public function getExtendedType()
{
return 'form';
}
}
This is then added to the form registry using the form.type.extensions provider:
$app['form.type.extensions'] = $app->share($app->extend('form.type.extensions', function($extensions) use ($app) {
$extensions[] = new AddressType();
return $extensions;
}));
Is there something else I need to do or a different way of building the form in this way?
Why not use direct
$app['form.factory']->createBuilder('Namespace\\Form\\Types\\Form')
First, sorry for my poor english. :)
I think you should extend form.extensions, instead of form.type.extensions.
Something like this:
$app['form.extensions'] = $app->share($app->extend('form.extensions',
function($extensions) use ($app) {
$extensions[] = new MyTypesExtension();
return $extensions;
}));
Then your class MyTypesExtension should look like this:
use Symfony\Component\Form\AbstractExtension;
class MyTypesExtension extends AbstractExtension
{
protected function loadTypes()
{
return array(
new AddressType(),
//Others custom types...
);
}
}
Now, you can retrieve your custom type this way:
$app['form.factory']->createBuilder('address')->getForm();
Enjoy it!
I see, this question is quite old but:
What you do is creating a new Form Type not extending an existing one, so the correct way to register it to add it to the 'form.types'. (Remember: form type extension is adding something to the existing types so for the future all instance will have that new 'feature'. Here you are creating a custom form type.)
$app['form.types'] = $app->share($app->extend('form.types', function ($types) use ($app) {
$types[] = new AddressType();
return $types;
}));
I think when you are coming from Symfony to Silex form.type.extension can be misleading.
From Symfony How to Create a Form Type Extension:
You want to add a generic feature to several types (such as adding a "help" text to every field type);
You want to add a specific feature to a single type (such as adding a "download" feature to the "file" field type).
So as your code shows you want to add a FormType which exists in Symfony but you would use the FormServiceProvider in Silex without defining an AbstractType and just use the form.factory service as shown in this example:
In your app.php:
use Silex\Provider\FormServiceProvider;
$app->register(new FormServiceProvider());
In your controller/action:
$form = $app['form.factory']->createBuilder('form', $data)
->add('name')
->add('email')
->add('gender', 'choice', array(
'choices' => array(1 => 'male', 2 => 'female'),
'expanded' => true,
))
->getForm()
;

Categories