I have 2 entities, Exercise and Product, they have a One to One relationship (the owning side is Product), and I have an ExerciseFormType and a ProductFormType.
What I want I to be able to create an exercise, and the product associated with it at the same time.
My idea was to add the the builder of exerciseFormType, 'product' and its type would be a ProductFormType. The problem is that the product needs the exercise_id, and the exercise isn't persisted yet so it doesn't have one. Here's what I have so far (it works only for creating a product when the exercise exists).
class ExerciseType extends AbstractType {
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('name')
->add('content')
->add('language')
->add('level')
->add('skillsRequired')
->add('skillsTargetted')
->add('tags')
->add('product', ProductType::class, array(
'required' => false,
'exercise'=> $builder->getData()
)
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver){
$resolver->setDefaults(array(
'data_class' => 'SchoolBundle\Entity\Exercise'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix(){
return 'schoolbundle_exercise';
}
class ProductType extends AbstractType {
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('visibility')->add('price', MoneyType::class);
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
$field = (!$product || null === $product->getId()) ? 'publicationDate' : 'updateDate';
$form->add($field, DateTimeType::class, array('data' => new \DateTime()));
});
$data = array(
'data' => $options['exercise'],
'class' => Exercise::class,
'choice_label' => 'getName'
);
$builder->add('exercise', EntityType::class, $data);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver){
$resolver->setDefaults(array(
'data_class' => 'MarketBundle\Entity\Product'
));
$resolver->setRequired(array(
'exercise'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix(){
return 'marketbundle_product';
}
}
In exercise I have that
<?php
/**
* exercise
*
* #ORM\Table(name="exercise")
* #ORM\Entity(repositoryClass="SchoolBundle\Repository\ExerciseRepository")
*/
class Exercise {
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
* #ORM\Column(name="content", type="string", length=255)
*/
private $content;
/**
* #var string
* #ORM\Column(name="language", type="string", length=255)
*/
private $language;
/**
* #var int
* #ORM\Column(name="level", type="integer")
*/
private $level;
/**
* #var string
* #ORM\Column(name="skills_required", type="string", length=255)
*/
private $skillsRequired;
/**
* #var string
* #ORM\Column(name="skills_targetted", type="string", length=255)
*/
private $skillsTargetted;
/**
* #var string
* #ORM\Column(name="tags", type="string", length=255)
*/
private $tags;
/**
* #ORM\OneToMany(targetEntity="Session", mappedBy="exercise")
*/
private $sessions;
/**
* #ORM\OneToMany(targetEntity="ExerciseIO", mappedBy="exercise")
*/
private $exerciseIOs;
/**
* #ORM\OneToOne(targetEntity="MarketBundle\Entity\Product", mappedBy="exercise", cascade={"persist"}))
*/
private $product;
/**
* #ORM\ManyToOne(targetEntity="Professor", inversedBy="createdExercises")
* #ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=false)
*/
private $creator;
/**
* #ORM\ManyToMany(targetEntity="Professor", mappedBy="boughtExercises")
*/
private $owners;
public function __construct(){
$this->sessions = new ArrayCollection();
$this->exerciseIOs = new ArrayCollection();
$this->owners = new ArrayCollection();
}
/**
* Get creator
* #return Professor
*/
public function getCreator(){
return $this->creator;
}
/**
* Set creator
* #param Professor $creator
* #return Exercise
*/
public function setCreator($creator){
$this->creator = $creator;
return $this;
}
/**
* Get owner
* #return ArrayCollection
*/
public function getOwners(){
return $this->owners;
}
/**
* Get product
* #return Product
*/
public function getProduct(){
return $this->product;
}
/**
* #param Product $product
* #return Exercise
*/
public function setProduct($product){
$this->product = $product;
return $this;
}
/**
* Get exerciseIOs
* #return ArrayCollection
*/
public function getExerciseIOs(){
return $this->exerciseIOs;
}
/**
* Get sessions
* #return ArrayCollection
*/
public function getSessions(){
return $this->sessions;
}
/**
* Get id
* #return int
*/
public function getId(){
return $this->id;
}
/**
* Set name
* #param string $name
* #return Exercise
*/
public function setName($name){
$this->name = $name;
return $this;
}
/**
* Get name
* #return string
*/
public function getName(){
return $this->name;
}
/**
* Set content
* #param string $content
* #return Exercise
*/
public function setContent($content){
$this->content = $content;
return $this;
}
/**
* Get content
* #return string
*/
public function getContent(){
return $this->content;
}
/**
* Set language
* #param string $language
* #return Exercise
*/
public function setLanguage($language){
$this->language = $language;
return $this;
}
/**
* Get language
* #return string
*/
public function getLanguage(){
return $this->language;
}
/**
* Set level
* #param string $level
* #return Exercise
*/
public function setLevel($level){
$this->level = $level;
return $this;
}
/**
* Get level
* #return string
*/
public function getLevel(){
return $this->level;
}
/**
* Set skillsRequired
* #param string $skillsRequired
* #return Exercise
*/
public function setSkillsRequired($skillsRequired){
$this->skillsRequired = $skillsRequired;
return $this;
}
/**
* Get skillsRequired
* #return string
*/
public function getSkillsRequired(){
return $this->skillsRequired;
}
/**
* Set skillsTargetted
* #param string $skillsTargetted
* #return Exercise
*/
public function setSkillsTargetted($skillsTargetted){
$this->skillsTargetted = $skillsTargetted;
return $this;
}
/**
* Get skillsTargetted
* #return string
*/
public function getSkillsTargetted(){
return $this->skillsTargetted;
}
/**
* Set tags
* #param string $tags
* #return Exercise
*/
public function setTags($tags){
$this->tags = $tags;
return $this;
}
/**
* Get tags
* #return string
*/
public function getTags(){
return $this->tags;
}
}
And in Product
/**
* Product
*
* #ORM\Table(name="product")
* #ORM\Entity(repositoryClass="\MarketBundle\Repository\ProductRepository")
*/
class Product {
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var bool
* #ORM\Column(name="visibility", type="boolean")
*/
private $visibility;
/**
* #var float
* #ORM\Column(name="price", type="float")
*/
private $price;
/**
* #var \DateTime
* #ORM\Column(name="publication_date", type="datetimetz")
*/
private $publicationDate;
/**
* #var \DateTime
* #ORM\Column(name="update_date", type="datetimetz", nullable=true)
*/
private $updateDate;
/**
* #ORM\OneToMany(targetEntity="ProductComment", mappedBy="product")
*/
private $productComments;
/**
* #ORM\OneToOne(targetEntity="\SchoolBundle\Entity\Exercise", inversedBy="product")
* #ORM\JoinColumn(name="exercise_id", referencedColumnName="id", nullable=false)
*/
private $exercise;
public function __construct(){
$this->productComments = new ArrayCollection();
}
/**
* Get exercise
* #return Exercise
*/
public function getExercise(){
return $this->exercise;
}
/**
* Set exercise
* #param Exercise $exercise
* #return Product
*/
public function setExercise($exercise){
$this->exercise = $exercise;
return $this;
}
/**
* Get productComments
* #return ArrayCollection
*/
public function getProductComments(){
return $this->productComments;
}
/**
* Get id
* #return int
*/
public function getId(){
return $this->id;
}
/**
* Set visibility
* #param boolean $visibility
* #return Product
*/
public function setVisibility($visibility){
$this->visibility = $visibility;
return $this;
}
/**
* Get visibility
* #return bool
*/
public function getVisibility(){
return $this->visibility;
}
/**
* Set price
* #param float $price
* #return Product
*/
public function setPrice($price){
$this->price = $price;
return $this;
}
/**
* Get price
* #return float
*/
public function getPrice(){
return $this->price;
}
/**
* Set publicationDate
* #param \DateTime $publicationDate
* #return Product
*/
public function setPublicationDate($publicationDate){
$this->publicationDate = $publicationDate;
return $this;
}
/**
* Get publicationDate
* #return \DateTime
*/
public function getPublicationDate(){
return $this->publicationDate;
}
/**
* Set updateDate
* #param \DateTime $updateDate
* #return Product
*/
public function setUpdateDate($updateDate){
$this->updateDate = $updateDate;
return $this;
}
/**
* Get updateDate
* #return \DateTime
*/
public function getUpdateDate(){
return $this->updateDate;
}
}
I've said it works for an existing exercise (and no product).
When I try to create both at the same time I get
Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
It's probably from the $builder->getData(), because in that case I only passed to the form a "new Exercice()". Here's the controller action
public function newAction(Request $request){
$exercise = new Exercise();
$form = $this->createForm(ExerciseType::class, $exercise);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$exercise->setCreator($this->getUser());
$em = $this->getDoctrine()->getManager();
$em->persist($exercise);
$em->flush();
return $this->redirectToRoute('exercise_show', array('id' => $exercise->getId()));
}
(Also this is another question but when I disable a formType, it is rendered, and the value appears but it's not submitted. I would actually prefer a hiddenFormType but I need the formatting of the DateTimeFormType)
Ok, this is the solution I've made once with Symfony2.8, I hope it can helps you.
/**
* #ORM\Entity
* #ORM\Table(name="picture")
*/
class Picture
{
/**
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Host",
* inversedBy="pictureId"
* )
* #ORM\JoinColumn(name="avatar_host_id", referencedColumnName="id", nullable=true)
*/
private $avatarHostId;
}
/**
* #ORM\Entity
* #ORM\Table(name="host")
*/
class Host
{
/**
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Picture",
* mappedBy="avatarHostId", cascade={"persist", "remove", "merge"}
* )
*/
private $pictureId;
}
class HostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('pictureId', PictureFileType::class);
}
}
class PictureFileType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('assetFile', FileType::class, array(
'label' => 'Picture File',
'required' => false,
'validation_groups' => array('creation')
));
}
}
First, I recommend moving away from using integers as IDs. There are some issues around them, including that you can't really generate one at will in this kind of scenario. I would recommend Uuids along with ramsey/uuid, which also has a Doctrine mapping.
When you've got a situation where you need an ID before it's been persisted and the Uuid generated, generate the Uuid yourself. For instance, in a contrived example
class Order
{
/**
* #var UuidInterface
*
* #ORM\Id
* #ORM\Column(type="uuid")
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private $id;
...
/**
* Constructor.
*
* #param UuidInterface $id
*/
public function __construct(?UuidInterface $id = null)
{
$this->id = $id ?? Uuid::uuid4();
}
...
}
class OrderController
{
...
public function orderProductAction(): JsonResponse
{
...
// We need this ahead of time, because
// maybe this is Async and we need to
// return the order id. This happens a
// lot with commands.
$generatedId = Uuid::uuid4();
// Here it's going to use the given ID,
// if not given, the Order class will
// generate it.
$order = new Order($generatedId, ...);
// Stuff happens, persisted... maybe we
// don't have the persisted Order object...
return $this->json([
'order' => [
'id' => $generatedId->toString(),
],
]);
}
...
}
The meat of it is providing the generated Uuid in the constructor (which alternately generates it if null). Do not add a setter. Here's a more involved example:
public function createAction(Request $request): JsonResponse
{
$request_data = \json_decode($request->getContent(), true);
$tenant_data = $request_data['data'];
// Pre-generate our Uuid for the Tenant.
$generated_id = Uuid::uuid4();
/** #var CreateTenant $create_tenant_cmd */
$create_tenant_cmd = new CreateTenant($generated_id);
$create_tenant_cmd->setName($tenant_data['name']);
$create_tenant_cmd->setDomainName($tenant_data['domain_name']);
$create_tenant_cmd->setOwnerUsername($tenant_data['owner_username']);
$create_tenant_cmd->setOwnerPassword($tenant_data['owner_password']);
$create_tenant_cmd->setOwnerFirstName($tenant_data['owner_first_name']);
$create_tenant_cmd->setOwnerLastName($tenant_data['owner_last_name']);
$create_tenant_cmd->setOwnerEmail($tenant_data['owner_email']);
if (!$this->isGranted('perform', $create_tenant_cmd)) {
throw new AccessDeniedException('Access denied to create tenant.');
}
// Here, we should expect NO side-effects, the command
// can't return anything. So no Tenant object with
// generated Uuid...
$this->get('command_bus')->handle($create_tenant_cmd);
/* #var Serializer $serializer */
$serializer = $this->get('jms_serializer');
$serializer_context = SerializationContext::create()->setGroups([
'tenant_meta',
]);
$tenant = $this->get('app.repository.tenant')->find($generated_id);
return $this->json(\json_decode($serializer->serialize(
$tenant,
'json',
$serializer_context
)));
}
That's the ID question; the second part of your question is now easily resolved, that being giving the Uuid to the FormType instead:
$form = $this->createForm(OrderProductType::class, $product, [
'product' => $product,
'generatedId' => $generatedId,
]);
class OrderProductType extends AbstractType implements DataMapperInterface
{
/** #var UuidInterface */
protected $generatedId;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->generatedId = $options['generatedId'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefaults([
'data_class' => OrderProduct::class,
])
->setRequired(['generatedId'])
;
}
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
$data = new OrderProduct($this->generatedId);
...
}
}
Of course, if you need it as a field, use it with the field type of your choice instead of storing it as a variable, and then get it from that field:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('product_id', HiddenType::class, [
'product_id' => $options['generatedId']->toString(),
])
...
;
}
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
// Note use of Uuid::fromString().
$data = new OrderProduct(Uuid::fromString(
$forms['productId']->getData()
));
...
}
This is one of the downsides of not delegating the process of interaction to a delegate object like a command, instead of generating an entity; you're stuck constructing your object before you need it, in whole, or perverting the design to make it fit. It also makes working with assertions harder as well (you only get one set, the entity's).
Related
I have symfony form with choice field. I want to load choices from my entity class static method. can i use data or CallbackChoiceLoader? what is the best practise?
this is my field:
class CarType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', ChoiceType::class, [
// This choices I would like to load from:
// 'choices' => $car::getTypes(),
'choices' => [
'Critical' => 'critical',
'Medium' => 'medium',
'Info' => 'info',
],
'label' => 'Type'
])
->add('name', TextType::class, [
'label' => 'Name'
])
->add('save', SubmitType::class, [
'label' => 'Save'
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Car'
]);
}
public function getName()
{
return 'car';
}
}
This is my entity:
/**
* #ORM\Entity
* #ORM\Table(name="car")
*/
class Car
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string $type
*
* #Assert\NotBlank()
* #ORM\Column(name="type")
*/
private $type;
/**
* #var string $name
*
* #Assert\NotBlank()
* #ORM\Column(name="name")
*/
private $name;
/**
* #var \DateTime $created
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
private static $types = ['main', 'custom'];
public function __construct()
{
$this->created = new \DateTime('now', new \DateTimeZone('Europe/Ljubljana'));
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Get types
*
* #return array
*/
public function getTypes()
{
return self::$types;
}
/**
* Set type
*
* #param string $type
*
* #return Car
*/
public function setType($type)
{
if (in_array($type, $this->getTypes())) {
$this->type = $type;
}
return $this;
}
/**
* Get type
*
* #return string
*/
public function getType()
{
return $this->type;
}
/**
* Set name
*
* #param string $name
*
* #return Car
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set created
*
* #param \DateTime $created
*
* #return Car
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
How and what can i use to load choices from $car::getTypes() method like i commented in form so that the choices are loaded dynamicly based on values in getTypes entity method?
Edit: This is option 1. Option 2 below more directly answers the question.
The preferred method of creating a choice form field from an entity is to use the EntityType field. Here is an example from an application that requires an ethnicity field. Below that is the Ethnicity entity.
form field:
->add('ethnicity', EntityType::class, array(
'label' => 'Ethnicity:',
'class' => 'AppBundle:Ethnicity',
'choice_label' => 'abbreviation',
'expanded' => false,
'placeholder' => 'Select ethnicity',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('e')
->orderBy('e.abbreviation', 'ASC')
;
},
))
Ethnicity entity:
/**
* Ethnicity.
*
* #ORM\Table(name="ethnicity")
* #ORM\Entity
*/
class Ethnicity
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="ethnicity", type="string", length=45, nullable=true)
*/
protected $ethnicity;
/**
* #var string
*
* #ORM\Column(name="abbr", type="string", length=45, nullable=true)
*/
protected $abbreviation;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Member", mappedBy="ethnicity", cascade={"persist"})
*/
protected $members;
/**
* Constructor.
*/
public function __construct()
{
$this->members = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set ethnicity.
*
* #param string $ethnicity
*
* #return Ethnicity
*/
public function setEthnicity($ethnicity)
{
$this->ethnicity = $ethnicity;
return $this;
}
/**
* Get ethnicity.
*
* #return string
*/
public function getEthnicity()
{
return $this->ethnicity;
}
/**
* Set abbreviation.
*
* #param string $abbreviation
*
* #return Ethnicity
*/
public function setAbbreviation($abbreviation)
{
$this->abbreviation = $abbreviation;
return $this;
}
/**
* Get abbreviation.
*
* #return string
*/
public function getAbbreviation()
{
return $this->abbreviation;
}
/**
* Add members.
*
* #param \AppBundle\Entity\Member $members
*
* #return Ethnicity
*/
public function addMember(\AppBundle\Entity\Member $members)
{
$this->members[] = $members;
return $this;
}
/**
* Remove members.
*
* #param \AppBundle\Entity\Member $members
*/
public function removeMember(\Truckee\ProjectmanaBundle\Entity\Member $members)
{
$this->members->removeElement($members);
}
/**
* Get members.
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getMembers()
{
return $this->members;
}
/**
* #var bool
*
* #ORM\Column(name="enabled", type="boolean", nullable=true)
*/
protected $enabled;
/**
* Set enabled.
*
* #param bool $enabled
*
* #return enabled
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
return $this;
}
/**
* Get enabled.
*
* #return bool
*/
public function getEnabled()
{
return $this->enabled;
}
}
Option 2:
If you really want to do that, then here's an approach:
Modify your static property $types such that the choices are values in an array, e.g., $types = array(1 => 'main', 2 => 'custom')
In your controller, add a use statement for the Car entity.
In controller action:
$car = new Car();
$types = $car->getTypes;
Use the answer to Passing data to buildForm() in Symfony 2.8/3.0 to see how to pass $types to your form.
Symfony makes it all so easy. It sets out the big lines for your project and nothing ever goes wrong. Until something does go wrong. You'll be looking for the right solution for days. At least that's what I'm doing right now.
I'm working on a little test project in which you can add urls as bookmarks and give each url a variety of tags to categorize the urls.
I have used the generate:doctrine:crud command to build my forms. But I am getting a weird error.
An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Object of class Bla\LinkBundle\Entity\Url could not be converted to string") in form_div_layout.html.twig at line 13.
I can solve this issue by adding a __toString() method in my Url entity but I wanna know why.
The problem is with the name property being null. If I return this value as '' using __toString() it works fine. But I do not like this solution. All other values are null as well when I am in the "create url" form, so why is it complaining about the name property?
Form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('url')
->add('flags')
->add('tags', EntityType::class, array(
'class' => 'Bla\LinkBundle\Entity\Tag',
'choice_label' => 'name',
'multiple' => true,
))
;
}
Controller
public function newAction(Request $request)
{
$url = new Url();
$form = $this->createForm('Bla\LinkBundle\Form\UrlType', $url);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($url);
$em->flush();
return $this->redirectToRoute('url_show', array('id' => $url->getId()));
}
return $this->render('url/new.html.twig', array(
'url' => $url,
'form' => $form->createView(),
));
}
Entity
namespace Bla\LinkBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Bla\LinkBundle\Entity\Tag;
/**
* URL
*
* #ORM\Table(name="urls")
* #ORM\Entity
*/
class Url
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="url", type="text", length=65535, nullable=false)
*/
private $url;
/**
* #var integer
*
* #ORM\Column(name="flags", type="integer", nullable=false)
*/
private $flags;
/**
* #var integer
*
* #ORM\Column(name="id", type="bigint")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="\Bla\LinkBundle\Entity\Tag", cascade={"persist"})
* #ORM\JoinTable(name="url_tag",
* joinColumns={#ORM\JoinColumn(name="url_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id", unique=true)}
* )
*/
protected $tags;
public function __construct()
{
$this->tags = new ArrayCollection();
}
/**
* Set name
*
* #param string $name
*
* #return Urls
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set url
*
* #param string $url
*
* #return Urls
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* Get url
*
* #return string
*/
public function getUrl()
{
return $this->url;
}
/**
* Set flags
*
* #param integer $flags
*
* #return Urls
*/
public function setFlags($flags)
{
$this->flags = $flags;
return $this;
}
/**
* Get flags
*
* #return integer
*/
public function getFlags()
{
return $this->flags;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #param \Doctrine\Common\Collections\ArrayCollection $tags
* #return Urls
*/
public function setTags($tags)
{
$this->tags = $tags;
return $this;
}
/**
* #return \Doctrine\Common\Collections\ArrayCollection
*/
public function getTags()
{
return $this->tags;
}
/**
* Add tag
*
* #param Tag $tag
* #return Urls
*/
public function addTag(Tag $tag)
{
$this->tags->add($tag);
return $this;
}
/**
* Remove tag
*
* #param Tags $tag
*/
public function removeTag(Tag $tag)
{
$this->tags->removeElement($tag);
}
}
try to implements this into form class :
public function getName()
{
return 'filter_type_url';
}
public function getParent()
{
return 'multiselectentity';
}
i'm looking for nice way to persist 2 objects to db via doctrine in symfony 2.3
class CatController extends Controller
{
/**
* Creates a new Cat entity.
*
*/
public function createAction(Request $request)
{
$entity = new Cat();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity); <-- Split it or what ?
$em->flush();
return $this->redirect($this->generateUrl('cat_show', array('id' => $entity->getId())));
}
return $this->render('ViszmanCatBundle:Cat:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
when form is validated i can get post data and create 2 objects with that data but i think there should be clearer way to do this, above code is working not as i wanted, it only inserts foreign key to related entity when i do this:
class CatType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text')
->add('meetings','collection',
array('type' => new MeetingType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
)
);
}
class MeetingType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$plDays = array('Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota', 'Niedziela');
$builder
->add('meetingDay', 'choice', array('choices' => $plDays))
->add('meetingTime', 'time',)
->add('cat', 'entity', array('class' => 'ViszmanCatBundle:Cat', 'property' => 'name'))
;
}
entities: Cat
namespace Viszman\CatBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Cat
*
* #ORM\Table()
* #ORM\Entity
*/
class Congregation
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="Viszman\CatBundle\Entity\Member", mappedBy="cat")
*/
private $members;
/**
* #ORM\OneToMany(targetEntity="Viszman\CatBundle\Entity\Meeting", mappedBy="cat", cascade={"persist"})
*/
private $meetings;
public function __construct(){
$this->members = new \Doctrine\Common\Collections\ArrayCollection();
$this->meetings = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Cat
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Add members
*
* #param \Viszman\CatBundle\Entity\Member $members
* #return Cat
*/
public function addMember(\Viszman\CatBundle\Entity\Member $members)
{
$this->members[] = $members;
return $this;
}
/**
* Remove members
*
* #param \Viszman\CatBundle\Entity\Member $members
*/
public function removeMember(\Viszman\CatBundle\Entity\Member $members)
{
$this->members->removeElement($members);
}
/**
* Get members
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getMembers()
{
return $this->members;
}
/**
* Add meetings
*
* #param \Viszman\CationBundle\Entity\Meeting $meetings
* #return Cat
*/
public function addMeeting(\Viszman\CatBundle\Entity\Meeting $meetings)
{
$this->meetings[] = $meetings;
return $this;
}
/**
* Remove meetings
*
* #param \Viszman\CatBundle\Entity\Meeting $meetings
*/
public function removeMeeting(\Viszman\CatBundle\Entity\Meeting $meetings)
{
$this->meetings->removeElement($meetings);
}
/**
* Get meetings
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getMeetings()
{
return $this->meetings;
}
namespace Viszman\CatBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Meeting
*
* #ORM\Table()
* #ORM\Entity
*/
class Meeting
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="meeting_day", type="smallint")
*/
private $meetingDay;
/**
* #var \DateTime
*
* #ORM\Column(name="meeting_time", type="time")
*/
private $meetingTime;
/**
* #ORM\ManyToOne(targetEntity="Viszman\CatBundle\Entity\Cat", inversedBy="meetings")
* #ORM\JoinColumn(name="cat_id", referencedColumnName="id")
*/
private $cat;
public function __construct()
{
$this->created = new \DateTime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set meetingDay
*
* #param integer $meetingDay
* #return Meeting
*/
public function setMeetingDay($meetingDay)
{
$this->meetingDay = $meetingDay;
return $this;
}
/**
* Get meetingDay
*
* #return integer
*/
public function getMeetingDay()
{
return $this->meetingDay;
}
/**
* Set cat
*
* #param \Viszman\CatBundle\Entity\Cat $cat
* #return Member
*/
public function setCat(\Viszman\CatBundle\Entity\Cat $cat = null)
{
$this->cat = $cat;
return $this;
}
/**
* Get cat
*
* #return \stdClass
*/
public function getCat()
{
return $this->cat;
}
/**
* Set meetingTime
*
* #param \DateTime $meetingTime
* #return Meeting
*/
public function setMeetingTime($meetingTime)
{
$this->meetingTime = $meetingTime;
return $this;
}
/**
* Get meetingTime
*
* #return \DateTime
*/
public function getMeetingTime()
{
return $this->meetingTime;
}
this generate embedded form with unwanted data, meaning in Meeting section i need to choice Cat, but i dont want to, what i want is that meeting is on default attached to Cat on create, update. Do i need to change something in Cat or Meeting Entity? I don't know if i'm clear, sorry for my poor english
You need to remove this line in the MeetingType:
->add('cat', 'entity', array('class' => 'ViszmanCatBundle:Cat', 'property' => 'name'))
Then in yout Controller persist your Cat entity and yout Meeting entity (which you can find using cat's getMeetings method).
If you want both to be persisted in one shot, take a look at the cascade operation for Doctrine entities.
I have two entities Store and Category and each Store has it's own categories.
I'd like that when a Store owner's try to add a new category and category_parent, just the categories related to current Store will be displayed.
Right now, all categories are displayed in the select-option.
I'm using Tree Gedmo extension to manage Category entity and I use getChildrenQueryBuilder method to select categories.
How can I modify this method and add my specific constraint ?
$store which is the constraint is declared in the controller action down.
I'd like te set current Category option disabled when he try to add a category_parent, so category and category parent must be differnt.
I hope it's clear
CategoryType.php
<?php
namespace Project\StoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
class CategoryType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//..........
//..........
->add('category', 'entity', array(
'required' => false,
'label' => 'Category parent',
'class' => 'ProjectStoreBundle:Category',
'attr' => array('class' => 'col-sm-8'),
'empty_value' => 'Select one category',
'property' => 'indentedName',
'multiple' => false,
'expanded' => false ,
'query_builder' => function (\Project\StoreBundle\Entity\CategoryRepository $r)
{
return $r->getChildrenQueryBuilder(null, null, 'root', 'asc', false);
}
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\StoreBundle\Entity\Category'
));
}
/**
* #return string
*/
public function getName()
{
return 'project_storebundle_category';
}
}
Entity/Category.php
<?php
namespace Project\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Category
* #Gedmo\Tree(type="nested")
* #ORM\Table()
* #ORM\Entity(repositoryClass="Project\StoreBundle\Entity\CategoryRepository")
* #ORM\HasLifeCycleCallbacks()
*/
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*
*#Assert\NotBlank(message="Please enter the name of categorie.")
*/
private $name;
/**
* #Gedmo\slug(fields={"name"}, unique_base="uniqueBase")
* #ORM\Column(name="slug",length=255, unique=false)
*/
private $slug ;
/**
* #ORM\Column(name="uniqueBase", type="integer")
*/
private $uniqueBase ;
/**
* #ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* #ORM\Column(name="metaDescription", type="string", length=255, nullable=true)
*
* #Assert\Length(
* max=255,
* maxMessage="message"
* )
*/
private $metaDescription;
/**
* #ORM\Column(name="metaKeywords", type="string", length=255, nullable=true)
*
* #Assert\Length(
* max=255,
* maxMessage="message"
* )
*/
private $metaKeywords;
/**
* #ORM\Column(name="enabled", type="boolean", nullable=false)
*/
private $enabled;
/**
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\Column(name="root", type="integer", nullable=true)
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"lft" = "ASC"})
*/
private $children;
/**
*non mapped property
*/
private $indentedName;
/**
*non mapped property
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="Project\StoreBundle\Entity\Store", inversedBy="categories", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $store ;
/**
* Constructor
*/
public function __construct()
{
$this->children = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set slug
*
* #param string $slug
* #return Category
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set uniqueBase
*
* #param integer $uniqueBase
* #return Category
*/
public function setUniqueBase($uniqueBase)
{
$this->uniqueBase = $uniqueBase;
return $this;
}
/**
* Get uniqueBase
*
* #return integer
*/
public function getUniqueBase()
{
return $this->uniqueBase;
}
/**
* Set description
*
* #param string $description
* #return Category
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set metaDescription
*
* #param string $metaDescription
* #return Category
*/
public function setMetaDescription($metaDescription)
{
$this->metaDescription = $metaDescription;
return $this;
}
/**
* Get metaDescription
*
* #return string
*/
public function getMetaDescription()
{
return $this->metaDescription;
}
/**
* Set metaKeywords
*
* #param string $metaKeywords
* #return Category
*/
public function setMetaKeywords($metaKeywords)
{
$this->metaKeywords = $metaKeywords;
return $this;
}
/**
* Get metaKeywords
*
* #return string
*/
public function getMetaKeywords()
{
return $this->metaKeywords;
}
/**
* Set enabled
*
* #param boolean $enabled
* #return Category
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
return $this;
}
/**
* Get enabled
*
* #return boolean
*/
public function getEnabled()
{
return $this->enabled;
}
/**
* Set parent
*
* #param \Project\StoreBundle\Entity\Category $parent
* #return Category
*/
public function setParent(\Project\StoreBundle\Entity\Category $parent = null)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* #return \Project\StoreBundle\Entity\Category
*/
public function getParent()
{
return $this->parent;
}
/**
* Set lft
*
* #param integer $lft
* #return Category
*/
public function setLft($lft)
{
$this->lft = $lft;
return $this;
}
/**
* Get lft
*
* #return integer
*/
public function getLft()
{
return $this->lft;
}
/**
* Set lvl
*
* #param integer $lvl
* #return Category
*/
public function setLvl($lvl)
{
$this->lvl = $lvl;
return $this;
}
/**
* Get lvl
*
* #return integer
*/
public function getLvl()
{
return $this->lvl;
}
/**
* Set rgt
*
* #param integer $rgt
* #return Category
*/
public function setRgt($rgt)
{
$this->rgt = $rgt;
return $this;
}
/**
* Get rgt
*
* #return integer
*/
public function getRgt()
{
return $this->rgt;
}
/**
* Set root
*
* #param integer $root
* #return Category
*/
public function setRoot($root)
{
$this->root = $root;
return $this;
}
/**
* Get root
*
* #return integer
*/
public function getRoot()
{
return $this->root;
}
/**
* Add children
*
* #param \Project\StoreBundle\Entity\Category $children
* #return Category
*/
public function addChild(\Project\StoreBundle\Entity\Category $children)
{
$this->children[] = $children;
return $this;
}
/**
* Remove children
*
* #param \Project\StoreBundle\Entity\Category $children
*/
public function removeChild(\Project\StoreBundle\Entity\Category $children)
{
$this->children->removeElement($children);
}
/**
* Get children
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getChildren()
{
return $this->children;
}
/**
* Get IndentedName
*
*/
public function getIndentedName()
{
return str_repeat("-----", $this->lvl).$this->name;
}
/**
* Get category
*
*/
public function getCategory()
{
return $this->category;
}
/**
* Set store
*
* #param \Project\StoreBundle\Entity\Store $store
* #return Category
*/
public function setStore(\Project\StoreBundle\Entity\Store $store = null)
{
$this->store = $store;
return $this;
}
/**
* Get store
*
* #return \Project\StoreBundle\Entity\Store
*/
public function getStore()
{
return $this->store;
}
}
Controller
/**
* Create a new Category entity.
*
*/
/**
* #ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function newAction(Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$category = new Category();
$category->setStore($store);
$category->setUniqueBase($store->getId());
$form = $this->createForm(new CategoryType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('message', 'Category enregistred');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:new.html.twig',
array(
'form' => $form->createView() ,
'store' =>$store,
));
}
I managed to pass the parameter $store to the form, but I don't know how to use it as a constraint in getChildrenQueryBuilder method.
Should I create a new custom method? I prefer to use getChildrenQueryBuilder if it is possible.
Here is the new code
CategoryType.php
class CategoryType extends AbstractType
{
private $store;
public function __construct($store)
{
$this->store = $store;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$store = $this->store;
$builder
//...........
//...........
->add('parent', 'entity', array(
'required' => false,
'label' => 'Category parent',
'class' => 'ProjectStoreBundle:Category',
'attr' => array('class' => 'col-sm-8'),
'empty_value' => 'Select one category',
'property' => 'indentedName',
'multiple' => false,
'expanded' => false ,
'query_builder' => function (\Project\StoreBundle\Entity\CategoryRepository $r) use ($store)
{
return $r->getChildrenQueryBuilder(null, null, 'root', 'asc', false);
}
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\StoreBundle\Entity\Category'
));
}
/**
* #return string
*/
public function getName()
{
return 'project_storebundle_category';
}
}
Controller
/**
* #ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function newAction(Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$category = new Category();
$category->setStore($store);
$category->setUniqueBase($store->getId());
$form = $this->createForm(new CategoryType($store), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('message', 'Category enregistred');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:new.html.twig',
array(
'form' => $form->createView() ,
'store' =>$store,
));
}
You're not saying which Symfony2 version you're using, but generically speaking, you just need to find a way to pass the current store to your form builder and use it in the method that filters the possible categories.
Take a look at this one, or this one, basically you just need to inject the $store into your form builder, and then you (almost) have it :-)
I need to add questionnaire of multiple choice questions to my registration form. The questions and options are in two entities:
<?php
namespace Me\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Question
*
* #ORM\Table(name="question")
* #ORM\Entity(repositoryClass="Me\UserBundle\Entity\QuestionRepository")
*/
class Question
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="questionText", type="text")
*/
private $questionText;
/**
* #var boolean $expanded
*
* #ORM\Column(name="expanded", type="boolean")
*/
private $expanded;
/**
* #var boolean $multiple
*
* #ORM\Column(name="multiple", type="boolean")
*/
private $multiple;
/**
* #var Questionnaire $questionnaire
*
* #ORM\ManyToOne(targetEntity="Questionnaire", inversedBy="questions")
* #ORM\JoinColumn(name="questionnaire", referencedColumnName="id", onDelete="cascade")
*/
private $questionnaire;
/**
* #var \Doctrine\Common\Collections\ArrayCollection $options
*
* #ORM\OneToMany(targetEntity="Option", mappedBy="question", cascade={"all"})
*/
private $options;
public function __construct()
{
$this->expanded = false;
$this->multiple = false;
$this->options = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set questionText
*
* #param string $questionText
* #return Question
*/
public function setQuestionText($questionText)
{
$this->questionText = $questionText;
return $this;
}
/**
* Get questionText
*
* #return string
*/
public function getQuestionText()
{
return $this->questionText;
}
/**
* #param mixed $options
*/
public function setOptions($options)
{
$this->options[] = $options;
return $this;
}
/**
* #return mixed
*/
public function getOptions()
{
return $this->options;
}
function __toString()
{
return $this->getQuestionText();
}
/**
* #param boolean $expanded
*/
public function setExpanded($expanded)
{
$this->expanded = $expanded;
}
/**
* #return boolean
*/
public function getExpanded()
{
return $this->expanded;
}
/**
* #param boolean $multiple
*/
public function setMultiple($multiple)
{
$this->multiple = $multiple;
}
/**
* #return boolean
*/
public function getMultiple()
{
return $this->multiple;
}
/**
* #param \Me\UserBundle\Entity\Questionnaire $questionnaire
*/
public function setQuestionnaire($questionnaire)
{
$this->questionnaire = $questionnaire;
}
/**
* #return \Me\UserBundle\Entity\Questionnaire
*/
public function getQuestionnaire()
{
return $this->questionnaire;
}
}
and
<?php
namespace Me\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* QuestionOption
*
* #ORM\Table(name="option")
* #ORM\Entity(repositoryClass="Me\UserBundle\Entity\OptionRepository")
*/
class Option
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="questionId", type="integer")
*/
private $questionId;
/**
* #var string
*
* #ORM\Column(name="optionText", type="string", length=255)
*/
private $optionText;
/**
* #ORM\ManyToOne(targetEntity="Question", inversedBy="options")
* #ORM\JoinColumn(name="questionId", referencedColumnName="id", onDelete="cascade")
**/
private $question;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set optionText
*
* #param string $optionText
* #return Option
*/
public function setOptionText($optionText)
{
$this->optionText = $optionText;
return $this;
}
/**
* Get optionText
*
* #return string
*/
public function getOptionText()
{
return $this->optionText;
}
/**
* #return mixed
*/
public function getQuestion()
{
return $this->question;
}
/**
* #param mixed $question
*/
public function setQuestion($question)
{
$this->question = $question;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
function __toString()
{
return $this->getOptionText();
}
}
I also have a questionnaire entity, though I don't think I really need it because users won't be creating questionnaires, only filling the single questionnaire during registration.
My user entity:
<?php
namespace Me\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Me\UserBundle\Entity\UserRepository")
*/
class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="firstName", type="string", length=50)
*/
private $firstName;
/**
* #var string
*
* #ORM\Column(name="middleInitial", type="string", length=50)
*/
private $middleInitial;
/**
* #var string
*
* #ORM\Column(name="lastName", type="string", length=50)
*/
private $lastName;
/**
* #var string
*
* #ORM\Column(name="homePhoneArea", type="string", length=3)
*/
private $homePhoneArea;
/**
* #var string
*
* #ORM\Column(name="homePhoneNumber", type="string", length=7)
*/
private $homePhoneNumber;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=50)
*/
private $email;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*/
public $questions;
public function __construct()
{
$this->questions = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
* #return User
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set middleInitial
*
* #param string $middleInitial
* #return User
*/
public function setMiddleInitial($middleInitial)
{
$this->middleInitial = $middleInitial;
return $this;
}
/**
* Get middleInitial
*
* #return string
*/
public function getMiddleInitial()
{
return $this->middleInitial;
}
/**
* Set lastName
*
* #param string $lastName
* #return User
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
return $this;
}
/**
* Get lastName
*
* #return string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Set homePhoneArea
*
* #param string $homePhoneArea
* #return User
*/
public function setHomePhoneArea($homePhoneArea)
{
$this->homePhoneArea = $homePhoneArea;
return $this;
}
/**
* Get homePhoneArea
*
* #return string
*/
public function getHomePhoneArea()
{
return $this->homePhoneArea;
}
/**
* Set homePhoneNumber
*
* #param string $homePhoneNumber
* #return User
*/
public function setHomePhoneNumber($homePhoneNumber)
{
$this->homePhoneNumber = $homePhoneNumber;
return $this;
}
/**
* Get homePhoneNumber
*
* #return string
*/
public function getHomePhoneNumber()
{
return $this->homePhoneNumber;
}
/**
* Set email
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* #return \Doctrine\Common\Collections\ArrayCollection
*/
public function getQuestions()
{
return $this->questions;
}
}
My user controller:
public function newAction()
{
$user = new User();
$em = $this->getDoctrine()->getManager();
$questions = $em->getRepository('MeUserBundle:Question')->findAll();
if (!$questions) {
throw $this->createNotFoundException('Unable to find Questions.');
}
$builder = $this->createFormBuilder();
$optionEntities = array();
foreach ($questions as $question)
{
$options = array();
foreach ($question->getOptions() as $option)
{
$options[$option->getId()] = $option->getOptionText();
$optionEntities[$option->getId()] = $option;
}
$builder->add('question_'. $question->getId(), 'choice', array(
'label' => $question->getQuestionText(),
'expanded' => $question->getExpanded(),
'multiple' => $question->getMultiple(),
'choices' => $options
));
}
$user->getQuestions()->add($questions);
$form = $this->createForm(new MyFormType(), array('User' => $user));
return $this->render('MeUserBundle:User:new.html.twig', array(
'entity' => $user,
'form' => $form->createView(),
));
}
The form type as it stands today:
<?php
namespace Me\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class MyFormType extends AbstractType
{
protected $questions;
public function __construct( $questions)
{
$this->questions = $questions;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('questions', 'collection', array(
'type' => new QuestionType()
))
->add('firstName')
->add('middleInitial')
->add('lastName')
->add('homePhoneArea')
->add('homePhoneNumber')
->add('email')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
));
}
public function getName()
{
return 'myform_type';
}
}
This setup doesn't work to get the questions and their associated options, and display them in the same user creation form. I've seen instructions and docs for combining forms, but not creating forms with this kind of configuration. Any guidance would be appreciated.
I'm not sure if I've understood your question correctly but maybe this could help.
From what I see you're building the form(for questions) but you're not using it anywhere! The easiest way is to pass the questions(in your case $user->getQuestions() ) to the MyFormType and the add all the questions inside of the type.
So it would look like something like this
$this->createForm(new MyFormType($user->getQuestions()), array('User' => $user));
And inside your type
protected $questions;
public function __construct($questions)
{
$this->questions = $questions;
}
protected $questions;
public function __construct($questions)
{
$this->questions = $questions;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach ($this->questions as $question)
{
$options = array();
foreach ($question->getOptions() as $option)
{
$options[$option->getId()] = $option->getOptionText();
$optionEntities[$option->getId()] = $option;
}
$builder->add('question_'. $question->getId(), 'choice', array(
'label' => $question->getQuestionText(),
'expanded' => $question->getExpanded(),
'multiple' => $question->getMultiple(),
'choices' => $options
));
}
}
Edit 1
Why don't you try the following?
Add the method setQuestionnaire in User and create a type called QuestionnaireType which is responsible to create the whole questionnaire
In the UserType(sorry for the wrong names) add the QuestionnaireType as an embedded form
Once the user submits the data and you call the bind symfony will pass the whole questionnaire object to the created method so you can iterate over it and save the user's aswers!
Your users entity needs a relation to his $answers you should store in an $answers-field in your user entity, (look up "embedding collections")
Then in your controller that digests the form your store the values by $user->setAnswers(value)) and then you'll find the answers values in the users entity's $answers field ($user->getAnswers()).
ANd dont forget to add your getters and setters.
$php app/console doctrine:generate:entities BundleName:Entityname
At first you could add a setQuestions method in user, this method will receive an ArrayCollection.
Then have you got a look at http://symfony.com/doc/current/reference/forms/types/collection.html ?
And maybe you could ask yourself what does this form should display/do
Does it need to embbed a collection or a list of QuestionFormType could be ok?
Each time i have to do embedded form type, i draw sketches and activity diagram for being sure to not dive into a to complex form structure