In my code I need to download from database some data and put it to the form, but I don't know how to get doctrone outside Controller class.
I tried create new service, but it didn't work (I think I can't use in this case __controller(), am I right?). I tried also transfer instance of the controller to the parameters of buildForm() method but I got message: FatalErrorException: Compile Error: Declaration of MyBundle\Form\Type\TemplateType::buildForm() must be compatible with that of Symfony\Component\Form\FormTypeInterface::buildForm() ).
This is my code:
class TemplateType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', 'text')
// ...
->add('description', 'textarea');
}
public function getName() {
return 'template';
}
}
How can I use inside buildForm() doctrine?
In order to send data from doctrine to your form, you need to do this into your controller:
public function doSomethingWithOneObjectAction( $id )
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository( 'AcmeBundle:ObjectEntity' )->find( $id );
if ( ! $entity) {
throw $this->createNotFoundException( 'Unable to find Object entity.' );
}
$form = $this->createForm(
new TemplateType(),
$entity
);
return array(
'entity' => $entity,
'form' => $form->createView()
);
}
If you want to access a service from container inside your form type, you need first to register it as an service and inject into it the services you need. Something like this
Related
I'm developing a RESTFul API in Symfony 2.3.* with FOSUserBundle and FOSRestBundle, and I'm having trouble understanding how to create a registration method.
My controller look like this :
class UserRestController extends FOSRestController
{
//Other Methods ...
public function postUserAction()
{
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->createUser();
$param = $paramFetcher->all();
$form = $this->createForm(new UserType(), $user);
$form->bind($param);
if ($form->isValid() == false)
return $this->view($form, 400);
$userManager->updateUser($user);
return $this->view('User Created', 201);
}
//...
}
And my UserType class :
class UserType extends BaseType
{
public function __construct($class = "User")
{
parent::__construct($class);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username', 'username')
->add('email', 'email')
->add('plainPassword', 'repeated', array(
'first_name' => 'password',
'second_name' => 'confirm',
'type' => 'password'
))
->add('lastname')
->add('firstname')
->add('job_position')
->add('phone')
->add('company_name')
->add('website')
->add('sector')
->add('address')
->add('city')
->add('zip_code')
->add('country')
->add('billing_infos_same_as_company')
->add('billing_address')
->add('billing_city')
->add('billing_zip')
->add('billing_country')
->add('putf')
->add('das');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Wipsea\UserBundle\Entity\User',
'csrf_protection' => false
));
}
public function getName()
{
return 'wipsea_user_registration';
}
}
When I test it, no matter what the form isn't valid and shows no error.
And when I try to get the request :
"Validation Failed" "This form should not contain extra fields."
Is there a way to properly validate the form ?
EDIT : Updating my problem.
I would recommend you this tutorial in 3 parts - there is everything you need:
http://welcometothebundle.com/symfony2-rest-api-the-best-2013-way/
http://welcometothebundle.com/web-api-rest-with-symfony2-the-best-way-the-post-method/
http://welcometothebundle.com/symfony2-rest-api-the-best-way-part-3/
If you want to provide complex user validation you should create UserType form and pass data to this form instead of directly setting all properties:
public function postAction()
{
$user = new User();
$form = $this->createForm(new UserType(), $user);
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
// propel version
$user->save();
$response = new Response();
$response->setStatusCode(201);
// set the `Location` header only when creating new resources
$response->headers->set('Location',
$this->generateUrl(
'acme_demo_user_get', array('id' => $user->getId()),
true // absolute
)
);
return $response;
}
// return form validation errors
return View::create($form, 400);
}
In part 2 of this tutorial you have all information about creating form, passing data and validating it with RestBundle.
There is also a lot information about best practices using REST with Symfony2.
Take a look at this code:
https://github.com/piotrjura/fitness-api/blob/master/src/Fitness/FitnessBundle/Service/UsersService.php#L40
https://github.com/piotrjura/fitness-api/blob/master/src/Fitness/FitnessBundle/Controller/UsersController.php#L30
Also check validation.yml and entity serializer yml files.
You don't need forms to do the validation. And you definitly should not put the user creation and validation logic inside a controller. In case you'd like to make use of that form anyway later, eg. render it on the backend side, you'll have to write the same code twice.
I had to have the getName() method return '' in order for it to work for me.
https://github.com/FriendsOfSymfony/FOSRestBundle/issues/585
I am learning to use Symfony2 and in the documentation I have read, all entities being used with Symfony forms have empty constructors, or none at all. (examples)
http://symfony.com/doc/current/book/index.html Chapter 12
http://symfony.com/doc/current/cookbook/doctrine/registration_form.html
I have parametrized constructors in order to require certain information at time of creation. It seems that Symfony's approach is to leave that enforcement to the validation process, essentially relying on metadata assertions and database constraints to ensure that the object is properly initialized, forgoing constructor constraints to ensure state.
Consider:
Class Employee {
private $id;
private $first;
private $last;
public function __construct($first, $last)
{ .... }
}
...
class DefaultController extends Controller
{
public function newAction(Request $request)
{
$employee = new Employee(); // Obviously not going to work, KABOOM!
$form = $this->createFormBuilder($employee)
->add('last', 'text')
->add('first', 'text')
->add('save', 'submit')
->getForm();
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView(),
));
}
}
Should I not be using constructor arguments to do this?
Thanks
EDIT : Answered Below
Found a solution:
Looking into the API for the Controllers "createForm()" method I found something that is not obvious from the examples. It seems that the second argument is not necessarily an object:
**Parameters**
string|FormTypeInterface $type The built type of the form
mixed $data The initial data for the form
array $options Options for the form
So rather than pass in an instance of the Entity, you can simply pass in an Array with the appropriate field values:
$data = array(
'first' => 'John',
'last' => 'Doe',
);
$form = $this->createFormBuilder($data)
->add('first','text')
->add('last', 'text')
->getForm();
Another option (which may be better), is to create an empty data set as a default option in your Form Class.
Explanations here and here
class EmployeeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('first');
$builder->add('last');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'empty_data' => new Employee('John', 'Doe'),
));
}
//......
}
class EmployeeFormController extends Controller
{
public function newAction(Request $request)
{
$form = $this->createForm(new EmployeeType());
}
//.........
}
Hope this saves others the head scratching.
The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess,
but is an instance of class Ecs\CrmBundle\Entity\Customer.
Is the error I get in my browser..
FORM CODE:
<?php
namespace Ecs\CrmBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CustomerDefaultConfigType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('customerStatus')
->add('tags', null, array('multiple' => true, 'property' => 'tag_name'))
;
}
public function getName()
{
return 'ecs_crmbundle_customerdefaultconfigtype';
}
}
and the controller Action:
<?php
namespace Ecs\CrmBundle\Controller;
use Ecs\CrmBundle\Entity\CustomerDefaultConfig;
use Ecs\CrmBundle\Form\CustomerDefaultConfigType;
public function newAction()
{
$entity = new CustomerDefaultConfig();
$form = $this->createForm(new CustomerDefaultConfigType(), $entity);
return $this->render('EcsCrmBundle:CustomerDefaultConfig:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView()
));
}
This is using symfony2.1 with composer... Any ideas on how to get this working?
Since the last form refactoring, you have to specified the data_class in the setDefaultOptions method in your type.
See here (search for data_class).
Edit: Correct link
Currently, this is how I injected container into my form (by making it a service):
ApFloorType.php
namespace Techforge\ApartmentBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ApFloorType extends AbstractType
{
//in the controller, they are differently initialised
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function buildForm(FormBuilder $builder, array $options)
{
$bath_choices = $this->container->getParameter('bath_choices');
$bed_choices = $this->container->getParameter('bed_choices');
$builder
->add('baths', 'choice', array('choices' => $bath_choices))
->add('beds', 'choice', array('choices' => $bed_choices))
->add('name')
->add('file')
->add('plan_description', 'textarea')
->add('min_max_feet', 'text')
->add('deposit', 'text')
->add('application_fee')
->add('rental_deposit')
->add('lease_terms', 'textarea')
->add('threshhold_value')
->add('auto_accept')
->add('pending')
->add('apartment', 'hidden', array('property_path' => false))
->add('pre_post_update', 'hidden')
->add('photos', 'collection', array('type' => new ApFloorImageType()))
;
}
public function getName()
{
return 'techforge_apartmentbundle_apfloortype';
}
}
The problem is that the ApFloor CRUD system stoped working after this because, in my controller, I couldn't just write
$form = $this->createForm(new ApFloorType(), $floor);
This would produce the following error:
Catchable Fatal Error: Argument 1 passed to
Techforge\ApartmentBundle\Form\ApFloorType::__construct() must
implement interface
Symfony\Component\DependencyInjection\ContainerInterface, none given,
called in
/home/stormlifter/webapps/pmr/src/Techforge/ApartmentBundle/Controller/Manager/ApFloorController.php
on line 38 and defined in
/home/stormlifter/webapps/pmr/src/Techforge/ApartmentBundle/Form/ApFloorType.php
line 16
I solved this by calling form create method like this;
$form = $this->createForm($this->get('apfloortype'), $floor);
So my question is, is there a workaround this so I don't have to change each createForm() call in my CRUD controller after making it a service?
This is what I did in the FormType:
$data = Class::getYourData();
Because I don;t want (like you) to modify each controller to set extra parameter to the form..
How to bind data which is gained by using call:
$attributes = $em->getRepository('\OBB\Entity\Attribute')->findAllWithAllRelations($id);
to a Symfony 2 Form
Because according to a manual you need to have a method defined in Entity which is bound to a form.
You should add a form type for editing an individual attribute. This could look something like:
namespace OBB\Form;
class AttributeType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name');
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'OBB\Entity\Attribute',
);
}
public function getName()
{
return 'obb_attribute';
}
}
Then you can use a collection form to edit a collection of them simultaneously.
$form = $this->createForm('collection', $attributes, array(
'type' => new AttributeType(),
));