I'm trying to create a web application with Silex.
For my application I have two objects : Project() and Credential().
The Project() one :
protected function buildDomainObject($row)
{
$credential = new Credential();
$credential->setIdCred($row['idCred']);
$credential->setNameCred($row['nameCred']);
$credential->setToken($row['token']);
$project = new Project();
$project->setId($row['id']);
$project->setName($row['name']);
$project->setBranch($row['branch']);
$project->setCredential($credential);
$project->setComment($row['comment']);
$project->setAlive($row['alive']);
$project->setNumberTaskList($row['numberTaskList']);
return $project;
}
And the Credential one :
protected function buildDomainObject($row)
{
$credential = new Credential();
$credential->setIdCred($row['idCred']);
$credential->setNameCred($row['nameCred']);
$credential->setToken($row['token']);
return $credential;
}
As you can see Project() contains Credential() in the value credential.
There are no issues when passing a new Project() object to FormBuilder.
public function addProjectAction(Request $request, Application $app)
{
$credentials = $app['credential_repository']->findAllAsArray();
$project = new Project();
$projectForm = $app['form.factory']->create(new ProjectType(), $project, ['credentialChoices' => $credentials]);
$projectForm->handleRequest($request);
if ($projectForm->isSubmitted() && $projectForm->isValid()) {
$app['project_repository']->save($project);
}
return $app['twig']->render('projectList_form.html.twig', array(
'title' => 'New project',
'legend' => 'New project',
'projectForm' => $projectForm->createView(),
)
);
}
The problem occurs when I try and fetch a Project() from the database and pass it into FormBuilder.
$credentials = $app['credential_repository']->findAllAsArray();
$project = $app['project_repository']->find($id);
$projectForm = $app['form.factory']->create(new ProjectType(), $project, ['credentialChoices' => $credentials]);
I have the following error :
The value of type "object" cannot be converted to a valid array key.
I think my problem comes from the fact that the Project() object has a property containing the Credential() object.
I was able to recreate the exception by using the object property in the buildForm method.
InvalidArgumentException in ArrayKeyChoiceList.php line 71: The value of type "object" cannot be converted to a valid array key.
You can't use credential property with the choice form field.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('credential', 'choice', array(
'choices' => $choices,
'multiple' => false,
'expanded' => false
))
->add('submit', 'submit')
;
}
You could try adding a credential id property to Project().
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('credentialId', 'choice', array(
'choices' => $choices,
'multiple' => false,
'expanded' => false
))
->add('submit', 'submit')
;
}
Or you might need to setup the entity field type as explained in this stackoverflow question
Related
I can not get value from EntityType. I have last version 3.3.6.
class BuildType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array
$options)
{
$builder
->add('title', TextType::class)
->add('save', SubmitType::class, array('label' => 'Create Post'))
->add('team', CollectionType::class, array(
// each entry in the array will be an "email" field
'entry_type' => TeamType::class,
// these options are passed to each "email" type
'entry_options' => array(
'attr' => array('class' => 'form-control'),
),
'label' => false,
'allow_add' => true,
'prototype' => true,
'mapped' => false
));
}
}
class TeamType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array
$options)
{
$builder
->add('name', EntityType::class, array(
'placeholder' => 'Choice a champ',
'required' => true,
'class' => 'AppBundle:Champions',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.name', 'ASC');
},
'choice_label' => 'name',
'choice_value' => 'id',
'attr' => array('class' => 'dropdown'),
));
}
I tried all but i cannot take value of 'name' of TeamType. After submission form, i do
foreach ($form["team"]->getData() as $value) {
'name' => $value['name']
but the value is empty. If i try dump request and the value is there. The other values i can get it and save in Database. Only EntityType i can not.
Someone know how do?
EntityType returns as the object. You should use model getter functions.
$form->get('name')->getData()->getId(); // getName() vs..
Here is a similar example.
symfony how get selected data from select
I suppose that you are using ManyToOne relationship.
With AJAX
if you are trying get data after submitted, You can do this first in your view:
$('#elementWhereYouAreTEAMType').find('input').each(function (i,v) {
var valTeam = $(this).val(); //take value
// adding data, create an associative array.
formData.append("team["+ i +"]", valTeam);
});
You must put formData like data argument to ajax JQuery
Now, server side:
public function createTeamNow(Request $request) {// ajax
$teams = $request->request->get('team'); // getting array
if(!is_null($teams)) {// if user added team
foreach ($teams as $team) {
// dump($team);
//create an instance for each element, it does not replace a data with the above
$teamType = new TeamType();
$teamName->setName($team);
$this->em->persist($teamType);
}
}
}
Without AJAX
/**
* #Route("/slim/1" , name="data_x")
*/
public function slimExampleAction(Request $request)
{
$form = $this->createForm(TeamType::class);
$form->handleRequest($request);
if ($form->isSubmitted() /*&& $form->isValid()*/) {
// When you've ManyToOne relationship, that field returns ArrayCollection class, it has several method to get data on differents ways, iterate with it, for example toArray is one
$data = $form->getData();
dump($data->getDescription()->toArray());die;
}
return $this->render('AppBundle:view.html.twig', array(
'form' => $form->createView(),
));
}
I'm trying to make my firsts unit tests and functionnal tests with Symfony3 / PHPunit and I don't find solutions to my problem.
To be simple, I'm trying to Unit Test a form created to manage Users and Groups (provided by FOSUserBundle)
I followed the steps of the Symfony documentation to test my form, and I already solved some issues concerning getExtensions(), etc... to be able to run my test without any error
Here is my UserType.php :
<?php
...
class UserType extends BaseCrudForm
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('username', null, array('label' => 'label.username'))
->add('email', EmailType::class, array(
'label' => 'label.email',
'constraints' => array(
new Email()
)
))
->add('enabled', CheckboxType::class, array('label' => 'label.enabled', 'required' => false))
->add('groups', EntityType::class, array(
'class' => 'Acme\AdminBundle\Entity\Group',
'multiple' => true,
'required' => false,
))
->add('avatarImageFile', FileType::class, array('label' => 'label.avatar', 'required' => false))
;
}
...
}
and my UserTypeTest.php (I left my tests into it) :
<?php
...
class UserTypeTest extends BaseTypeTestCase
{
public function testSubmitValidData()
{
$formData = array(
'username' => 'nathalie_' . time(),
'email' => 'nathalie_' . time() . '#portman.fr',
'enabled' => true,
'plainPassword' => 'pa$$word',
///// SOLUTION 1
'groups' => array(
$this->em->getRepository('AcmeAdminBundle:Group')->find(1)
),
///// SOLUTION 2
'groups' => array(1),
);
$form = $this->factory->create(UserType::class);
$user = User::fromArray($formData);
$form->submit($formData);
$this->assertTrue($form->isSynchronized());
// dump($user, $form->getData());
$this->assertEquals($user, $form->getData()); //Fails
$view = $form->createView();
$children = $view->children;
foreach(array_keys($formData) as $key) {
$this->assertArrayHasKey($key, $children);
}
}
}
Problem :
The assert $this->assertEquals($user, $form->getData()); fails, because of the collection User::groups that is not equal
Solution 1 is not working (see code above)
The collection of $user is ok, but the form data is empty
Solution 2 is not working neither (see code above)
The form data is ok, but the $user collection is instantiated with a simple array(1) (sees logic)
Question
How could I instanciate correctly my object User and my form at the same time with the same informations (as it's described in the Symfony documentation a linked in top of this topic) ?
Note: as I said I'm a newbie with Unit Tests, so don't hesitate to tell me if I miss the point
Edit
Here is the fromArray function, implemented as a Trait in the User entity :
<?php
namespace Acme\AdminBundle\Traits;
use Doctrine\Common\Util\Inflector;
trait FromArrayTrait
{
/**
* #param array $data
* #return self
*/
public static function fromArray(array $data)
{
$object = new self;
foreach ($data as $key => $value) {
$method = 'set' . Inflector::classify($key);
if (method_exists($object, $method)) {
call_user_func(array($object, $method), $value);
}
}
return $object;
}
}
Hi i am tying pass array collection (method getProjects() returns it) to form (select input) and fail. This code returns exception - A "__toString()" method was not found on the objects of type "Tasker\WebBundle\Entity\Project" passed to the choice field.
Can anybody help? Is needed transformer? Or what is right way?
Controller:
/**
* #Route("/pridaj", name="web.task.add")
* #Template()
*/
public function addAction(Request $request)
{
$task = new Task;
/** #var User $loggedUser */
$loggedUser = $this->get('security.token_storage')->getToken()->getUser();
$form = $this->createForm(new AddTaskType(), $task, ['user' => $loggedUser]);
if ($form->handleRequest($request) && $form->isValid()) {
// some stuff
}
return [
'form' => $form->createView()
];
}
Form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('project', 'entity', [
'label' => 'Projekt:',
'class' => 'TaskerWebBundle:Project',
'choices' => $options['user']->getProjects(),
'placeholder' => 'Označte projekt',
])
// ....
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setRequired(array(
'user',
));
$resolver->setDefaults(array(
'user' => null,
));
}
just add __ToString() to your Project class
Tasker\WebBundle\Entity\Project
class Project
{
....
function __toString() {
return $this->getName(); //or whatever string you have
}
}
I wanted to add another answer, because you do not have to add __toString() to your Project class. The Symfony entity field type allows you to specify which property/field to use for displaying. So instead of __toString() you could specify the property in the form configuration like so:
$builder
->add('project', 'entity', [
'label' => 'Projekt:',
'class' => 'TaskerWebBundle:Project',
'choices' => $options['user']->getProjects(),
'placeholder' => 'Označte projekt',
'property' => 'name'
])
If you check this part of the Symfony documentation you will see that __toString() is automatically called only if you do not specify the property.
I am trying to implement a one to one relation. Each User can be affected at a building (Etablissement for me). A building can have many people but each people can be affected at one building at most.
I have this error :
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Intranet\UserBundle\Entity\User::setUserEtab() must be an instance of Intranet\RhBundle\Entity\Etablissement, array given, called in C:\wamp\www\projet\vendor\symfony\symfony\src\Symfony\Component\PropertyAccess\PropertyAccessor.php on line 360 and defined in C:\wamp\www\projet\src\Intranet\UserBundle\Entity\User.php line 322
The line 322 of User.php is :
public function setEtablissement(\Intranet\RhBundle\Entity\Etablissement $etablissement = null)
It occurs at the line $form->handleRequest($request); of my controller. This is my controller :
public function editerAction(Request $request, User $user){
$form = $this->createForm(new EditerFormType, $user);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
die("ici");
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$this->get('session')->getFlashBag()->add('success', "L'utilisateur ". $user->getNom() ." ". $user->getPrenom() . " a été édité avec succès !");
$em->flush();
return $this->redirect($this->generateUrl('intranet_rh_homepage'));
}else
$this->get('session')->getFlashBag()->add('danger', "Erreur de formulaire !");
}
return $this->render('IntranetRhBundle:User:editer.html.twig',array('user' => $user, 'form' => $form->createView()));
}
The die doesn't work. Before adding the form, it was working.
This is the EditForm :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', 'email', array('label' => 'form.email', 'translation_domain' => 'FOSUserBundle'))
->add('nom', 'text')
->add('prenom', 'text')
->add('naissance','date',array(
'widget' => 'single_text',
'format' => 'dd/MM/yyyy',
'attr' => array('class' => 'date', 'readonly' => 'readonly')
))
->add('sexe', 'choice', array(
'choices' => array('Homme' => 'Homme', 'Femme' => 'Femme'),
'multiple' => false
))
->add('etablissement', new UserEtabType())
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Intranet\Userbundle\Entity\User'));
}
This is my UserEtab form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('etablissement', 'entity', array('class' => 'IntranetRhBundle:Etablissement', 'property' => 'nom', 'empty_value' => 'Aucun', 'empty_data' => -1, 'required' => false))
;
}
And my relation on User entity :
/**
* #ORM\OneToOne(targetEntity="Intranet\RhBundle\Entity\Etablissement", cascade={"persist", "remove"})
**/
private $etablissement;
The form's view is okay, there is a select list with all the building and an empty value. But when I post, I have this error I can't understand and solve.
I have the setter and getter if User entity :
/**
* Set Etablissement
*
* #param \Intranet\RhBundle\EntityEtablissement $etablissement
* #return User
*/
public function setEtablissement(\Intranet\RhBundle\Entity\Etablissement $etablissement = null)
{
$this->etablissement = $etablissement;
return $this;
}
/**
* Get Etablissement
*
* #return \Intranet\RhBundle\Entity\Etablissement
*/
public function getEtablissement()
{
return $this->etablissement;
}
But when I var_dump $request->get('user')->get('etablissement') it doesn't work :
Error: Call to undefined method Intranet\UserBundle\Entity\User::get() in C:\wamp\www\projet\src\Intranet\RhBundle\Controller\UserController.php line 69
And the building doesn't appear in var_dump of $request->get('user').
EDIT :
I don't have a setDefaultOptions method for my UserEtabType because I don't know what I have to do. I tried to implements it but I have this kind of error :
The form's view data is expected to be an instance of class Intranet\UserBundle\Entity\User, but is an instance of class Proxies__CG__\Intranet\RhBundle\Entity\Etablissement. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms an instance of class Proxies__CG__\Intranet\RhBundle\Entity\Etablissement to an instance of Intranet\UserBundle\Entity\User.
For the new UserEtabType :
namespace Intranet\UserBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserEtabType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('etablissement', 'entity', array('class' => 'IntranetRhBundle:Etablissement', 'property' => 'nom', 'empty_value' => 'Aucun', 'empty_data' => -1, 'required' => false))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Intranet\UserBundle\Entity\User'));
}
public function getName()
{
return 'intranet_userbundle_useretablissementtype';
}
}
Can you show the new UserEtabType() class ?
EDIT 1 : Try putting this line :
->add('etablissement', 'entity', array('class' => 'IntranetRhBundle:Etablissement', 'property' => 'nom', 'empty_value' => 'Aucun', 'empty_data' => -1, 'required' => false));
in the main EditForm
EDIT 2:
plus be careful :
$resolver->setDefaults(array('data_class' => 'Intranet\UserBundle\Entity\User'));
must be
$resolver->setDefaults(array('data_class' => 'Intranet\UserBundle\Entity\Etablissement'));
in your UserEtabType() class
Are you getting this error when you try to insert a record ? Or before that. For your information, setUserEtab() in the User entity expects an object of Etablissement, because of the one-to-one relationship. Passing the value of an array is not sufficient. Try passing the object of Etablissement it self (the selected object after performing dql).
Hope this helps someone.
Cheers!
I'm using entity choice list in my form. I want to use only specific entities (in example: only groups that user belongs to)
So, in controller, I'm getting these groups, and trying to pass them into formBuider.
Controller:
/.../
$groups = $em->getRepository('VendorMyBundle:Group')->getUserGroups($user);
$form = $this->createForm(new Message($groups), $message);
/.../
so, what now? how to use it in formBuilder?
how to change this line to use passed array of groups?
->add('group','entity',array('class' => 'Vendor\MyBundle\Entity\Group', 'label'=>'Group:'))
or in the other way:
class MessageType
{
/.../
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('group','entity',
array(
'class' => 'Vendor\MyBundle\Entity\Group',
'property' => 'name',
'query_builder' => function ($repository) {
$qb = $repository->createQueryBuilder('group');
$qb->add('where', 'group.administrator = :user');
$qb->setParameter('user', $user->getId());
return $qb;
},
'label' => 'Group'
)
)
// Continue adding fields
;
}
/.../
}
so how can i get object $user to use in form builder? ($user represent current logged user)
You can give the object you want to use in the __construct() method.
Eg :
$form = $this
->get('form.factory')
->create(new ApplyStepOneFormType($this->company, $this->ad), $applicant);
In your form type :
function __construct(\Your\Bundle\Entity\Company $company, \DYB\ConnectBundle\Entity\Ad $ad) {
$this->company = $company;
$this->ad = $ad;
}
And then in your form type in buildForm method :
$company = $this->company;
$builder->add('ad', 'entity', array(
'class' => '\Your\Bundle\Entity\Ad',
'query_builder' => function(\Your\Bundle\Repository\AdRepository $er) use ($company) {
return $er->getActiveAdsQueryBuilder($company);
},
));
//In controller pass the value which you want to use in builder form in array like
$object = new Question();
$form->create(new QuestionType() , $object , array('sqtname'=>2,'question_type'=>2));
//In Form type class
public function buildForm(FormBuilderInterface $builder , array $options)
{
//for setting data field dynamically
if (array_key_exists('question_type', $options) && $options['question_type'] != '') {
$data = $em->getReference("RecrutOnlineStandardBundle:StdQuestionType",$options['question_type']->getId());
} else {
$data = "";
}
$builder->add('StdQuestionType', 'entity', array(
'class' => 'TestStandardBundle:StdQuestionType',
'property' => 'name',
'empty_value' => 'Sélectionner un question type',
'required' => true,
'data' => $data,
'query_builder' => function(EntityRepository $er ) use ( $options ) {
if (isset($options['sqtname']) && $options['sqtname'] != '') {
return $er->createQueryBuilder('sqt')
->where("sqt.name!= ".$options['sqtname']);
} else{
return $er->createQueryBuilder('sqt');
}
}
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Test\QuestionBundle\Entity\Question',
'required' => false,
'sqtname' => '',
'question_type' =>''
));
}
Bacteries' solution IS NOT a good one. For example, if you declare your type as service, it is impossible to pass an object to constructor.
A perfect solution is options - just pass data as option to form builder.
If you want to use custom query, you have to set query_builder option as follows:
use Doctrine\ORM\EntityRepository;
...
$message = new Message();
$form = $this->createFormBuilder($message)
->add('group', 'entity', array(
'class' => 'Vendor\MyBundle\Entity\Group',
'label'=>'Group:',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('g')
->... // whatever you want to do
}
))
->getForm();
You can find more info about query builder in Doctrine manual and about options for entity in Symfony2 manual.
Bacteries' solution is a real good one. Just a note to save headache to other guy like me :)
In this part may I point out the use ($company) part.
It was hidden by the frame and of course nothing works properly without it.
$builder->add('ad', 'entity', array(
'class' =>
'\Your\Bundle\Entity\Ad',
'query_builder' =>
function(\Your\Bundle\Repository\AdRepository $er) use ($company) {
return $er->getActiveAdsQueryBuilder($company);
},
)
);
Best way (my opinion) is give to your form entityManager and select all you need in it. But don't forget to declare empty key in setDefaults() otherwise data won't pass to your builder.
Something like this one
public function buildForm(FormBuilderInterface $builder, array $options)
{
$options['em']->getRepository(''); // select all you need
$builder->add('title', 'text')
->add('content', 'textarea');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Main\BlogBundle\Entity\Post',
'validation_groups' => array('post'),
'required' => false,
'em' => null // this var is for your entityManager
));
}
Apply EM as simple option...