still getting to grips with Symfony so please excuse me if this is already asked. I don't quite understand how to send data to forms to be processed properly so am need of a bit of guidance.
My issue is i wish to save my logged in user's ID into the database when they are creating a new gecko (so that this gecko's profile shows for only this user logged in). I am using FOSUserBundle by the way.
I know i can get the current user's id by using something like $user = $this->getUser()->getId(); However, i would like to save this into the database.
createAction:
public function createAction(Request $request)
{
$entity = new Gecko();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('gecko_show', array('name' => $entity->getName())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
GeckoType:
<?php
namespace Breedr\GeckoBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class GeckoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('aquisitionDate')
->add('morph')
->add('sex', 'choice', array(
'choices' => array(
'Male' => 'Male',
'Female' => 'Female'
)
))
->add('genetics')
->add('bio')
->add('bred', 'checkbox', array(
'required' => false
))
->add('hatchling', 'checkbox', array(
'required' => false
))
->add('clutch', 'text', array(
'data' => 0,
'required' => false
))
->add('imageFile', 'file', array(
'required' => false
))
->add('user_id', 'hidden', array(
'data' => $user,
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Breedr\GeckoBundle\Entity\Gecko'
));
}
/**
* #return string
*/
public function getName()
{
return 'breedr_geckobundle_gecko';
}
}
I would really appreciate any help with this. I just can't understand how the data is used/sent. Thanks in advance :)
Andy
Firstly sorry if you know this, but you'll need correct relationships set up between your Gecko and User entities.
Note your #ORM annotation may be different from mine (or you could be using YAML or whatever). My use is:
use Doctrine\ORM\Mapping as ORM;
This should be in your Gecko entity
/**
* #ORM\ManyToOne(targetEntity="Your\NameSpace\Entity\User", inversedBy="geckos")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
This should be in your User entity
/**
* #ORM\OneToMany(targetEntity="Your\NameSpace\Entity\Gecko",mappedBy="user")
*/
protected $geckos;
Note you'll need to generate getters & setters etc.
To save your new Gecko:
$gecko = new Gecko();
$gecko->setUser($this->getUser());
$form = $this->createForm(new GeckoType(), $gecko);
$entityManager = $this->getDoctrine()->getManager();
if ($request->isMethod('POST')) {
$form->submit($request);
if ($form->isValid()) {
$entityManager->persist($gecko);
$entityManager->flush();
// do something with success
} else {
// show errors by flash message or whatever
}
}
I'd suggest you read up on the symfony forms documentation & the databases & doctrine documentation as that has most of the information you need for getting started.
Related
This question already has answers here:
Symfony form - Choicetype - Error "Array to string covnersion"
(3 answers)
Closed 1 year ago.
I'm having trouble solving this problem, when i get in the page for creating a user in my Symfony web app, the user controller complains about the createForm() function and the error only says "Notice: Array to string conversion".
The form is the default created with the crud, actually everything is almost default so I can´t think on a single reason this thing doesn´t work as intended.
I've tried to modify the form to use a multiple selection but that didn´t work either.
And I tried to modify the data type in the entity but that just didn´t even worked because it throwed an error in the file immediately.
Here is the UserType.php (form generator)
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email')
->add('roles', ChoiceType::class, [
'label' => 'Tipo de usuario',
'attr' => [
'multiple' => true,
'class'=>'form-control myselect'
],
'choices' => [
'Admin' => 'ROLE_ADMIN',
'Usuario' => 'ROLE_USER'
],
])
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
The user controller in the breackpoint where Symfony shows the error:
/**
* #Route("/new", name="user_new", methods={"GET","POST"})
*/
public function new(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response
{
$user = new User();
$form = $this->createForm(UserType::class, $user); // <-- This is the highlighted part.
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->setPassword(
$passwordEncoder->encodePassword(
$user,
$form->get('plainPassword')->getData()
)
);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('user_index', [], Response::HTTP_SEE_OTHER);
}
return $this->render('user/new.html.twig', [
'user' => $user,
'form' => $form->createView(),
]);
}
And the user entity in the fragments where i declare the role variable and the getter and setter:
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
$roles is an array, you can see it in your configuration :
/**
* #ORM\Column(type="json")
*/
private $roles = [];
Even if we have type="json", it will be used as an array but in your database it will be a regular json.
ChoiceType here is a string so you can't use it in $roles.
You can add a data transformer (see similar response) to convert the value when displaying it in your select or when using it with your entity.
The simple way to do it is using a CallBackTransformer directly in your form type :
use Symfony\Component\Form\CallbackTransformer;
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
fn ($rolesAsArray) => count($rolesAsArray) ? $rolesAsArray[0]: null,
fn ($rolesAsString) => [$rolesAsString]
));
Or if your are not using PHP 7.4+
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($rolesAsArray) {
return count($rolesAsArray) ? $rolesAsArray[0]: null;
},
function ($rolesAsString) {
return [$rolesAsString];
}
));
I have many to many association in my entity and i would like to use the collection type field from symfony to add multiple times.
I've never done this way before and i'm kinda lost.
The field that i would like to have multiple times is headquarter with a non mapped field for each headquarter.
The error that i'm getting;
The property "headquarter" in class "AppBundle\Entity\SurveyManager"
can be defined with the methods "addHeadquarter()",
"removeHeadquarter()" but the new value must be an array or an
instance of \Traversable, "AppBundle\Entity\HeadQuarterManager" given.
The Form Type implementing the Collection.
class SurveyOptionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('headquarter', CollectionType::class, [
'entry_type' => HeadQuarterType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => 'Sediu',
])
->add('isEnabled', CheckboxType::class, [
'label' => 'Chestionar Activ',
'required' => false,
])
->add('submit', SubmitType::class, [
'label' => 'Salveaza',
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SurveyManager::class
]);
}
}
This is the Collection Form
class HeadQuarterType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('headquarter', EntityType::class, [
'class' => HeadQuarterManager::class,
'label' => 'Sediu',
])
->add('userNumber', TextType::class, [
'label' => 'Numar Utilizatori',
'mapped' => false,
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SurveyManager::class
]);
}
}
UPDATE:
Here is the Entity class stripped away from all the uncesessary data
class SurveyManager
{
/**
* #var int
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\HeadQuarterManager")
* #ORM\JoinTable(name="survey_headquarters",
* joinColumns={#ORM\JoinColumn(name="survey_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="headquarter_id", referencedColumnName="id")}
* )
*/
private $headquarter;
public function __toString()
{
// TODO: Implement __toString() method.
return $this->name;
}
/**
* Constructor
*/
public function __construct()
{
$this->question = new \Doctrine\Common\Collections\ArrayCollection();
$this->headquarter = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add headquarter
*
* #param \AppBundle\Entity\HeadQuarterManager $headquarter
*
* #return SurveyManager
*/
public function addHeadquarter(\AppBundle\Entity\HeadQuarterManager $headquarter)
{
$this->headquarter[] = $headquarter;
return $this;
}
/**
* Remove headquarter
*
* #param \AppBundle\Entity\HeadQuarterManager $headquarter
*/
public function removeHeadquarter(\AppBundle\Entity\HeadQuarterManager $headquarter)
{
$this->headquarter->removeElement($headquarter);
}
/**
* Get headquarter
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getHeadquarter()
{
return $this->headquarter;
}
}
Controller method
$em = $this->getDoctrine()->getManager();
$surveyRepository = $em->getRepository(SurveyManager::class);
//$surveyManager = $surveyRepository->findOneById($surveyId);
$surveyManager = new SurveyManager();
$form = $this->createForm(SurveyOptionType::class, $surveyManager);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($surveyManager);
$em->flush();
return $this->redirectToRoute('admin_survey-manager_survey_options',
[
'id' => $surveyManager->getId()
]);
}
return [
'surveyManager' => $surveyManager,
'form' => $form->createView(),
];
While doing research about this i did a separate demo to test on it to see better the problem.
Here is an updated code simplified that should work really smooth
https://gist.github.com/bogdaniel/a0bcc848e2bd282382f45a2bd15cc0e2
You will find more info about the error in the gist.
I don't really understand what do you want to archive, and what exactly does it mean:
The field that i would like to have multiple times is headquarter with a non mapped field for each headquarter.
But the error is quite self-explaining. Your SurveyManager class has to have some methods to populate data from Form. The flow is following - Form gets data (from User, for example) with an array of headquarters. And then Form looks for the way to pass this array to the object - and doesn't find them because you don't have appropriate methods.
Id doesn't matter if the property is mapped (I believe, you mean mapped to Doctrine), it still has be provided a way to pass data from Form to Object.
How are you rendering your form? I suspect something similar to the issue found here: Error : the new value must be an array or an instance of \Traversable, Entity class given
Other than that, the only issue I can see in your code is that the 'data_class' option from HeadQuarterType should be HeadQuarterManager::class.
I have list developer, not all developer, developer who have some parameters by filter and I need create check box for this some developer and post developer in another action, in this action identification developer who have true checkbox, I try this but have null check develoepr
class SelectByIdentityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', 'entity', array(
'required' => false,
'class' => 'ArtelProfileBundle:Developer',
'property' => 'id',
'property_path' => '[id]', # in square brackets!
'multiple' => true,
'expanded' => true
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
'csrf_protection' => false
));
}
/**
* #return string
*/
public function getName()
{
return 'select';
}
}
and action where I visible my developers
/**
* Finds and displays a Project entity.
*
* #Route("/{id}/show", name="admin_project_show")
* #Method({"GET", "POST"})
* #Template()
* #ParamConverter("entity", class="ArtelProfileBundle:Project")
*/
public function showAction($entity, Request $request)
{
//some logic
$developers = $profileRepository->findBySkillsAndTag($developer, $skill_form);
$form = $this
->createForm(
new SelectByIdentityType(),
$developers
)
->createView()
return array(
'entity' => $entity,
'form' => $form,
'developers' => $developers,
);
}
and if I check developer click SEND and then in the controller action which receives the POSTed data:
/**
* Send email for Developers.
*
* #Route("/{id}/send", name="admin_project_send_email")
* #Method({"GET", "POST"})
* #Template()
* #ParamConverter("entity", class="ArtelProfileBundle:Project")
*/
public function sendAction($entity, Request $request)
{
$em = $this->getDoctrine()->getManager();
$developer = $request->getSession()->get('developer');
$form = $this
->createForm(new SelectByIdentityType())
;
$form->bind($request);
if ($form->isValid()) {
$data = $form->getData();//$data['id'] I have 0
$ids = array();
foreach ($data['id'] as $developer) {
$ids[] = $entity->getId();
}
$request->getSession()->set('admin/foo_list/batch', $ids);
}
Why in $data['id'] I have 0 ??
and my template
{% for entity in developers %}
<tr>
<td>{{ form_widget(form.id[entity.id]) }}</td>
Any ideas ??
You can filter the entities displayed in an entity field by using the query_builder option. That way you can just normally render the whole form and you get a checkbox list of only the entities that your query returns. If you need to display it in a special way, you can override the form templates.
Unfortunately, as far as I know you can't use DQL or repository methods in the query_builder option. You have to write the query-builder code directly into the form definition.
An example:
$builder->add('id', 'entity', array(
'required' => false,
'class' => 'ArtelProfileBundle:Developer',
'multiple' => true,
'expanded' => true
'query_builder' => function($repo)
{
return $repo->createQueryBuilder('d')
->where('x = y');
}
));
I have three entity for a project : Book, Author and Theme. For information, Author and Theme are linked with Book in Many to One/One to Many. A book has one or more author and one or more themes.
And I have generated crud about theses for basics functions (add, edit and delete) and default views/form.
But I need to do some functions search, three exactly:
Search Book with part of the Title
Search Book with one Author
Search Book with one or more Theme.
I coded the three functions and working good but I have a problem about the fields used in search forms.
The first one is correct, it show a input field where we put the title and the book list as result is correct but the two others …
Because of the relationship the field are drop list where the user choose a item and search it, the result is correct but I wish for the second to have an input field for search the author name and the last to display checkbox so I’ve made research but I can’t understand why in my forms type, I can’t change the kind of field I want.
Here my two Forms Type :
Search with one Author : I just want to display an input field and not a list but get error each time
<?php namespace Projet\BibliothequeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class BookByAuthorType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('authors')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Projet\BibliothequeBundle\Entity\Book'
));
}
/**
* #return string
*/
public function getName()
{
return 'authors';
}
}
Search Book with one or more Theme : I tried to display chechbox by link the 'themes' array to result of checkbox but don't work too.
<?php namespace Projet\BibliothequeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class BookByThemeType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $themes = array();
// $themes=$builder->add('themes');
$builder
->add('themes', 'choice', [
'choices' => 'themes',
'multiple' => true,
'expanded' => true
])
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Projet\BibliothequeBundle\Entity\Book'
));
}
/**
* #return string
*/
public function getName()
{
return 'themes';
}
}
UPDATE : My BookController functions maybe it's the origin of my problems.
// AUTHOR SEARCH FUNCTIONS
public function SearchByAuthorAction() {
$entity = new Book();
$form = $this->CreateSearchAuthorForm($entity);
return $this->render('ProjetBibliothequeBundle:Book:searchbyauthor.html.twig', array(
'form' => $form->createView(),
));
}
private function CreateSearchAuthorForm(Book $entity) {
$form = $this->createForm(new BookByAuthorType(), $entity, array(
'action' => $this->generateUrl('book_submit_search_by_author'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Search'));
return $form;
}
public function SubmitSearchByAuthorAction(Request $request) {
$entity = new Book();
$form = $this->CreateSearchAuthorForm($entity);
$form->handleRequest($request);
$author = $form->get('authors')->getData();
$repository = $this->getDoctrine()
->getEntityManager()
->getRepository('ProjetBibliothequeBundle:Book');
$Booklist = $repository->findByAuthor($author);
return $this->render('ProjetBibliothequeBundle:Book:resultbyauthor.html.twig',
array('Booklist' => $Booklist));
}
//THEME SEARCH FUNCTIONS
public function SearchByThemeAction() {
$entity = new Book();
$form = $this->CreateSearchThemeForm($entity);
return $this->render('ProjetBibliothequeBundle:Book:searchbytheme.html.twig', array(
'form' => $form->createView(),
));
}
private function CreateSearchThemeForm(Livre $entity) {
$entities = $this->getDoctrine()->getManager()->getRepository('ProjetBibliothequeBundle:Book')->findAll();
$form = $this->createForm(new BookByThemeType(), $entity, array(
'action' => $this->generateUrl('book_submit_search_by_theme'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Search'));
return $form;
}
public function SubmitSearchByThemeAction(Request $request) {
$entity = new Book();
$form = $this->CreateSearchThemeForm($entity);
$form->handleRequest($request);
$theme = $form->get('themes')->getData();
$repository = $this->getDoctrine()
->getEntityManager()
->getRepository('ProjetBibliothequeBundle:Book');
$Booklist = $repository->findByTheme($theme);
return $this->render('ProjetBibliothequeBundle:Book:resultbytheme.html.twig',
array('Booklist' => $Booklist));
}
There is no need to set data_class for GET operations. Just create a basic form, may be like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('themes', 'choice', [
'choices' => 'themes',
'multiple' => true,
'expanded' => true
])
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(['method' => 'GET', 'csrf_protection' => false]);
}
in your action catch values from URL, pass them to findBy method or manually build you query:
public function someAction(Request $request)
{
...
$form->createForm(new FORM_TYPE, null);
....
$filters = $request->query->get($form->getName());
$result = $entityManager->getRepository(REPOSITORY)->findBy($filters, $orderBy, $limit, $offset);
...
}
I have a Timeslot functionality where the user has to be able to edit the start and end of each slot.
So I would like to create one form that contains alle timeslots. I based my code on this article in symfony docs: http://symfony.com/doc/current/cookbook/form/form_collections.html
The problem is that in this article, there's some kind of parent entity to which the collection instances should be assigned to. In my case, the timeslots are standalone and do not have a relation with another entity (that is relevent at this stage).
So.. my question: How do I create a collection of forms with my timeslots?
Controller.php
/**
* #Route("/settings", name="settings")
* #Template()
*/
public function settingsAction()
{
$timeslots = $this->getDoctrine()->getRepository("ScheduleBundle:Timeslot")->findAll();
$timeslots_form = $this->createFormBuilder()
->add('timeslots', 'collection', array(
'type' => new TimeslotType(),
))
->add('save', 'submit')
->getForm();
return array(
'timeslots' => $timeslots_form->createView()
);
}
TimeslotType.php:
class TimeslotType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('start', 'time', array(
'input' => 'datetime',
'widget' => 'choice',
))
->add('end', 'time', array(
'input' => 'datetime',
'widget' => 'choice',
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Oggi\ScheduleBundle\Entity\Timeslot'
));
}
/**
* #return string
*/
public function getName()
{
return 'timeslot';
}
}
Any thoughts?
thanks in advance!
Pass an array instead of an entity to your form. The form can process either just fine.
$timeslots = $this->getDoctrine()->getRepository("ScheduleBundle:Timeslot")->findAll();
$formData = array('timeslots' => $timeslots);
$timeslots_form = $this->createFormBuilder($formData) ...