Between 2 entities exists OneToMany relationship. First is User Entity second is Domains entities. One user can have multiple domains.
This is Users Entity (removed other fields because unrelated with subject):
class Users extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="\UsersBundle\Entity\UserDomains" , mappedBy="user" , cascade={"all"})
*/
protected $domains;
/**
* Add domain
*
* #param \UsersBundle\Entity\UserDomains $domain
*
* #return Users
*/
public function addDomain(\UsersBundle\Entity\UserDomains $domain)
{
$this->domains[] = $domain;
return $this;
}
/**
* Remove domain
*
* #param \UsersBundle\Entity\UserDomains $domain
*/
public function removeDomain(\UsersBundle\Entity\UserDomains $domain)
{
$this->domains->removeElement($domain);
}
}
This is UserDomains Entity (some fields has been removed):
class UserDomains
{
public function __construct() {
$this->setCreatedAt(new \DateTime());
}
public function __toString()
{
return $this->name;
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="AffiliateBundle\Entity\Users", inversedBy="domains")
* #ORM\JoinColumn(name="user", referencedColumnName="id", onDelete="CASCADE")
*/
private $user;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set user
*
* #param integer $user
*
* #return UserDomains
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return integer
*/
public function getUser()
{
return $this->user;
}
/**
* Set name
*
* #param string $name
*
* #return UserDomains
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
Must be add a domain when register user to system. I have got RegisterType for registration form. This type form class has got DataTransformer for adding domain which register user.
RegisterType class is here:
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fullName', 'text', array('attr' => array('class' => 'form-control'), 'label' => 'user_register.fullname', 'translation_domain' => 'UsersBundle'))
->add('domains', 'text', array('attr' => array('class' => 'form-control select2', 'id' => 'select2_sample1')))
;
$builder->get('domains')->addModelTransformer(new CallbackTransformer(
function(){
return '';
},
function($data){
$arrCollection = new ArrayCollection();
if (strpos($data, ",") !== false) {
$expData = explode(',', $data);
foreach ($expData as $domain) {
$domainObj = new UserDomains();
$domainObj->setName($domain);
$domainObj->setEnabled(true);
$arrCollection->add($domainObj);
}
} else if (!empty($data)) {
$domain = new UserDomains();
$domain->setName($data);
$domain->setEnabled(true);
$arrCollection->add($domain);
}
return $arrCollection;
}
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyBundle\Entity\Users',
'intention' => 'registration',
));
}
public function getParent()
{
return 'fos_user_registration';
// Or for Symfony < 2.8
// return 'fos_user_registration';
}
public function getName()
{
return 'user_registration';
}
}
So anyone fill the register form and submit. User created and adding the domains to user_domains table but doesn't update the user field which must be new user's id. How to do this with automatically? Or have you got any idea to update this user column of user_domains table with the new user's id?
Thanks for helps to all StackoverFlow :)
Add __construct like this to RegistrationType:
class RegistrationType extends AbstractType
{
private $user;
public function __construct(Users $user) {
$this->user = $user;
}
add setUser function to your DataTransform like this:
$domainObj->setUser($this->user);
//and
$domain->setUser($this->user);
finally update your controller like this:
$form = $this->createForm(new RegistrationType($userEntity), $userEntity);
Related
I have a controller ment to give the user the correct form type using variable classnames.
There is a entity named EntityForm which stores data of all forms avaible, with methods to give class name and form type names:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="entity_form")
*/
class EntityForm
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #var string
*
* #ORM\Column(length=12)
*/
protected $url;
/**
* #var string
*
* #ORM\Column
*/
protected $name;
/**
* #var string
*
* #ORM\Column(name="class_name")
*/
protected $className;
/**
* #var string
*
* #ORM\Column(name="description")
*/
protected $description;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set url
*
* #param string $url
*
* #return Form
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* Get url
*
* #return string
*/
public function getUrl()
{
return $this->url;
}
/**
* Set name
*
* #param string $name
*
* #return Form
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set className
*
* #param string $className
*
* #return Form
*/
public function setClassName($className)
{
$this->className = $className;
return $this;
}
/**
* Get className
*
* #return string
*/
public function getClassName()
{
return $this->className;
}
public function createEntity()
{
$entity = sprintf('%s\%s', __NAMESPACE__, $this->getClassName());
return new $entity;
}
public function createType()
{
return sprintf('AppBundle\Form\%sType', $this->getClassName());
}
/**
* Set description
*
* #param string $description
*
* #return Form
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
}
The action mend to create proper forms looks like this:
/**
* #Route("/form/ent/{url}", name="entity_form")
* #Method("GET")
*/
public function formAction(EntityForm $entityForm)
{
$form = $this->createForm($entityForm->createType())
->add('Dodaj!', SubmitType::class, array(
'attr' => array(
'class' => 'btn-info'
)
))
;
return $this->render('default/form.html.twig', [
'form_name' => $entityForm->getName(),
'form_description' => $entityForm->getDescription(),
'form' => $form->createView(),
]);
}
This controller links to a proper form type ie. for the cyclicNewsletter
class CyclicNewsletterType extends AbstractType
{
private $doctrine;
public function __construct($doctrine)
{
$this->doctrine = $doctrine;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$currencies = $this->doctrine->getRepository('AppBundle:Currency')->findAll();
$currenciesFormat = array();
foreach($currencies as $currency){
$currenciesFormat += array($currency->getName() .' ('. $currency->getShortName() . ')' => $currency);
}
$cycles = $this->doctrine->getRepository('AppBundle:Cycle')->findAll();
$cyclesFormat = array();
foreach($cycles as $cycle){
$cyclesFormat += array($cycle->getName() => $cycle);
}
$builder
->add('currency', ChoiceType::class, array(
'label' => 'Waluta',
'choices' => $currenciesFormat,
))
->add('cycle', ChoiceType::class, array(
'label' => 'Cykl',
'choices' => $cyclesFormat,
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => CyclicNewsletter::class,
));
}
}
Now with a simple POST action i can eaisly add new values to the database without writing separate actions for all forms:
/**
* #Route("/form/ent/{url}", name="form_entity_post")
* #Method("POST")
*
*/
public function formPostAction(EntityForm $entityForm, Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $this->createForm($entityForm->createType())
->add('Dodaj!', SubmitType::class)
;
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$formData = $form->getData();
$formData->setUser($this->container->get('security.token_storage')->getToken()->getUser());
$em->persist($formData);
$em->flush();
return $this->redirectToRoute('form_complete', ['url' => $entityForm->getUrl(), 'entityId' => $formData->getId()]);
}
var_dump('problem ' . $form->getErrors());
exit;
}
However
I cant seem to get the method to work for injecting an already created object into the form builder for editing.
What i mean is write a form create with variables classnames that would also store one instance of entity, which values will be already set in the form.
Is such way possible in symfony?
You will need to add the id of the entity you are editing into your url, and make it optional.
* #Route("/form/ent/{url}/{id}", name="form_entity_post", defaults={"id" = null})
In your controller, when you are building the form you need to get that object from the database and pass it to createForm method as the second argument.
/**
* #Route("/form/ent/{url}/{id}", name="form_entity_post", defaults={"id" = null})
* #Method("POST")
*
*/
public function formPostAction(EntityForm $entityForm, Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = null;
if ($id) {
$entity = $em->getRepository($entityForm->getClassName)->find($id);
}
$form = $this->createForm($entityForm->createType(), $entity)
->add('Dodaj!', SubmitType::class)
;
I need help with Symfony2 form One to One relation update. I have to entities: User and UserInfo
User entity:
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Group")
* #ORM\JoinTable(name="fos_user_user_group",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
/**
* #ORM\OneToOne(targetEntity="UserInfo", mappedBy="user", cascade={"all"})
*/
private $info;
/**
* #ORM\OneToMany(targetEntity="Log", mappedBy="user")
*/
private $logs;
public function __construct()
{
parent::__construct();
$this->logs = new ArrayCollection();
}
/**
* Set info
*
* #param \AppBundle\Entity\UserInfo $info
* #return User
*/
public function setInfo(\AppBundle\Entity\UserInfo $info = null)
{
$this->info = $info;
$info->setUser($this);
return $this;
}
/**
* Get info
*
* #return \AppBundle\Entity\UserInfo
*/
public function getInfo()
{
return $this->info;
}
/**
* Add logs
*
* #param \AppBundle\Entity\Log $logs
* #return User
*/
public function addLog(\AppBundle\Entity\Log $logs)
{
$this->logs[] = $logs;
return $this;
}
/**
* Remove logs
*
* #param \AppBundle\Entity\Log $logs
*/
public function removeLog(\AppBundle\Entity\Log $logs)
{
$this->logs->removeElement($logs);
}
/**
* Get logs
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLogs()
{
return $this->logs;
}
}
UserInfo entity:
class UserInfo
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="position", type="string", length=255)
*/
private $position;
/**
* #var string
*
* #ORM\Column(name="info", type="text")
*/
private $info;
/**
* #var string
*
* #ORM\Column(name="fullname", type="string", length=255)
*/
private $fullname;
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="info")
*/
private $user;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set position
*
* #param string $position
* #return UserInfo
*/
public function setPosition($position)
{
$this->position = $position;
return $this;
}
/**
* Get position
*
* #return string
*/
public function getPosition()
{
return $this->position;
}
/**
* Set info
*
* #param string $info
* #return UserInfo
*/
public function setInfo($info)
{
$this->info = $info;
return $this;
}
/**
* Get info
*
* #return string
*/
public function getInfo()
{
return $this->info;
}
/**
* Set fullname
*
* #param string $fullname
* #return UserInfo
*/
public function setFullname($fullname)
{
$this->fullname = $fullname;
return $this;
}
/**
* Get fullname
*
* #return string
*/
public function getFullname()
{
return $this->fullname;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
* #return UserInfo
*/
public function setUser(\AppBundle\Entity\User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
}
These are their types:
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username')
->add('email')
->add('password')
->add('info', new UserInfoType(), [
'by_reference' => false,
])
->add('Submit', 'submit')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\User',
]);
}
public function getName()
{
return 'user';
}
}
and
class UserInfoType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('position')
->add('info')
->add('fullname')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\UserInfo',
'allow_delete' => true,
]);
}
public function getName()
{
return 'userinfo';
}
}
New record inserts without any problem, but when I want to update this record with this action:
public function editAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$userRepository = $em->getRepository('AppBundle:User');
$user = $userRepository->findOneBy(['id' => $id]);
$form = $this->createForm(new UserType(), $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($user);
$em->flush();
return $this->redirectToRoute('user_list');
}
return $this->render('#App/User/form.html.twig', [
'form' => $form->createView(),
]);
}
I get this error:
An exception occurred while executing 'INSERT INTO user_info (position, info, fullname, user_id) VALUES (?, ?, ?, ?)' with params ["321", "321", "321", 14]:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '14' for key 'UNIQ_B1087D9EA76ED395'
Ok, I understand that he wants to insert new record, but what should I do to make him UPDATE it or DELETE existing one and INSERT new.
Try making User the owning side and UserInfo the inverse one (see docs).
class User extends BaseUser
{
/**
* #ORM\OneToOne(targetEntity="UserInfo", inversedBy="user", cascade={"all"})
*/
private $info;
}
class UserInfo
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="User", mappedBy="info")
*/
private $user;
}
Also related to this SO question.
Edit: To remove old, unreferenced UserInfo records after the update, set the orphanRemoval option in the info property of the User class.
class User extends BaseUser
{
/**
* #ORM\OneToOne(targetEntity="UserInfo", inversedBy="user", cascade={"all"}, orphanRemoval=true)
*/
private $info;
}
You need to use JoinColumn annotation in your User class:
/**
* #ORM\OneToOne(targetEntity="UserInfo", inversedBy="user", cascade={"all"})
* #ORM\JoinColumn(name="info", referencedColumnName="id")
*/
private $info;
UserInfo:
/**
* #ORM\OneToOne(targetEntity="User", mappedBy="info")
*/
private $user;
I having some troubles with symfony forms.
I have a boolean variable in my entity. I cant find a way to set that value in my form when i'm editing it.
Entity
namespace AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="pages")
*/
class Page
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="text")
*/
protected $slug;
/**
* #ORM\Column(type="text")
*/
protected $title;
/**
* #ORM\Column(type="text")
*/
protected $content;
/**
* #ORM\Column(name="creation_date", type="datetime")
*/
protected $creationDate;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
protected $isActive = true;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set slug
*
* #param string $slug
*
* #return Page
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set title
*
* #param string $title
*
* #return Page
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Page
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set creationDate
*
* #param \DateTime $creationDate
*
* #return Page
*/
public function setCreationDate($creationDate)
{
$this->creationDate = $creationDate;
return $this;
}
/**
* Get creationDate
*
* #return \DateTime
*/
public function getCreationDate()
{
return $this->creationDate;
}
/**
* Set isActive
*
* #param boolean $isActive
*
* #return Page
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
}
Controller function
public function editAction(Request $request, $pageId)
{
$em = $this->getDoctrine()->getManager();
$page = $em->getRepository('AdminBundle:Page')->find($pageId);
$form = $this->createForm('app_page', $page);
$form->handleRequest($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($page);
$em->flush();
return $this->redirectToRoute('admin_pages');
}
return $this->render('AdminBundle::editPage.html.twig', array('page' => $page, 'form' => $form->createView()));
}
PageFormType
namespace AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class PageFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text');
$builder->add('slug', 'text');
$builder->add('isActive', 'choice', array('choices' => array(
'true' => 'Active',
'false' => 'Inactive'
)));
$builder->add('content', 'textarea', array('required' => false));
}
public function getName()
{
return 'app_page';
}
}
As you can see i wrote my self in FormType class what choices it has. And now it is always displaying "Active" instead of displaying Entity's value. How should i do that?
Thanks!
Try to change your isActive in the Entity like
protected $isActive ;
and if you want to make a default true you can make it at the construct like :
public function __construct() {
$this->setIsActive = true ;
}
$builder->add('isActive', ChoiceType::class, [
'choices' => [
'Active' => true,
'Inactive' => false
],
'choices_as_values' => true,
'multiple' => true,
'expanded' => true,
'data' => array(true)
]);
A 2021 Solution: setting form choice default value (data) from entity
Sometimes your choice field values exist in another entity table, which means you cannot populate them using default form data_class. You only get the string value that saved in the form entity, resulting to error: ...must be an object or null, string given...
protected EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
// In your form
->add('membershipType', EntityType::class, [
'class' => MembershipType::class,
'label' => 'Enter membership type here',
'data' => $this->em->getRepository(MembershipType::class)->findOneBy([
'name' => $your_value_here
])
])
I was following the symfony2 tutorial on how to create an embedded form collection but wasn't able to implement it since it only creates a junction table.
According to doctrine2 documentation:
"Why are many-to-many associations less common? Because frequently you want to associate additional attributes with an association, in which case you introduce an association class. Consequently, the direct many-to-many association disappears and is replaced by one-to-many/many-to-one associations between the 3 participating classes."
Here are some snippets of my code:
src/AppBundle/Entity/Ingredient.php
/**
* Defines the properties of the Ingredient entity to represent the portal ingredients.
*
* #author furious_snail
*
* #ORM\Entity()
* #ORM\Table(name="ingredients")
* #UniqueEntity("name")
*/
class Ingredient
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="IngredientCategory")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=true)
*/
private $category;
/**
* #ORM\Column(type="string", unique=true)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="IngredientNutrient", mappedBy="ingredient", cascade={"persist", "remove"})
*/
private $nutrientsPer100G;
public function __construct()
{
$this->substitute = new ArrayCollection();
$this->nutrientsPer100G = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
/**
* #param mixed $nutrientsPer100G
*/
public function setNutrientsPer100G($nutrientsPer100G)
{
$this->nutrientsPer100G = $nutrientsPer100G;
}
/**
* #return array
*/
public function getNutrientsPer100G()
{
return $this->nutrientsPer100G;
}
}
src/AppBundle/Entity/IngredientNutrient.php
/**
* #ORM\Entity()
* #ORM\Table(name="ingredient_nutrient")
*/
class IngredientNutrient
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="Ingredient", inversedBy="nutrientsPer100G")
* #ORM\JoinColumn(name="ingredient_id", referencedColumnName="id", nullable=true)
*/
protected $ingredient;
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="Nutrient")
* #ORM\JoinColumn(name="nutrient_id", referencedColumnName="id", nullable=true)
*/
protected $nutrient;
/**
* #ORM\Column(type="float")
*/
private $quantity;
/**
* #ORM\ManyToOne(targetEntity="Unit")
* #ORM\JoinColumn(name="unit_id", referencedColumnName="id", nullable=true)
*/
private $unit;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getIngredient()
{
return $this->ingredient;
}
/**
* #param mixed $ingredient
*/
public function setIngredient($ingredient)
{
$this->ingredient = $ingredient;
}
/**
* #return mixed
*/
public function getNutrient()
{
return $this->nutrient;
}
/**
* #param mixed $nutrient
*/
public function setNutrient($nutrient)
{
$this->nutrient = $nutrient;
}
/**
* #return mixed
*/
public function getQuantity()
{
return $this->quantity;
}
/**
* #param mixed $quantity
*/
public function setQuantity($quantity)
{
$this->quantity = $quantity;
}
/**
* #return mixed
*/
public function getUnit()
{
return $this->unit;
}
/**
* #param mixed $unit
*/
public function setUnit($unit)
{
$this->unit = $unit;
}
}
src/AppBundle/Form/Type/IngredientNutrientType.php
class IngredientNutrientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nutrient', 'entity', array(
'class' => 'AppBundle\Entity\Nutrient',
'choice_label' => 'name',
'label' => 'Nutrient',
))
->add('quantity', null, array('label' => 'Cantitate'))
->add('unit', 'entity', array(
'class' => 'AppBundle\Entity\Unit',
'choice_label' => 'unit',
'label' => 'Unitate de masura'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\IngredientNutrient',
));
}
public function getName()
{
return 'app_ingredient_nutrient';
}
}
src/AppBundle/Form/Type/IngredientType.php
class IngredientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', null, array('label' => 'Name'))
->add('nutrients_per_100_g', 'collection', array(
'type' => new IngredientNutrientType(),
'allow_add' => true,
'label' => 'Nutrient quantity per 100g',
'options' => array('label' => false),
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Ingredient',
));
}
public function getName()
{
return 'app_ingredient';
}
}
This works, I do get an embedded form collection but the issue is that the ingredient_id in the ingredient_nutrient table is null. How do I make it to fill the table with the right ID?
These are the fields I get on the page:
Name:
Nutrient:
Quantity:
Unit:
The idea is that if I have IngredientNutrient form tied with Ingredient form the user shouldn't have to specify ingredient name twice.
Thank you.
To start with, you need to "cross reference" your entities:
public function setNutrientsPer100G($nutrientsPer100G)
{
$this->nutrientsPer100G = $nutrientsPer100G;
$nutrientsPer100G->setIngrediant($this);
}
Make sure both sides of the relation are being set. That will take care of the null id issues.
The other problem is that you are using collections in your Ingrediant/Nutrient entities but your set methods are not using the array operators.
I'll explain what I'm trying to do.
I have the following entities, Dealers Brands and Types.
1 Dealer can have many brands associated and each relation will have one type associated as well, that's why is a manyToMany relationship with an intermediate table.
I want to create a Form to Add dealers to our system and when doing so they can choose which brands are associated (there are few brands so we want to display them as checkboxes) and of what type, but trying to show this form with the brands is been tricky until now.
Brand Entity
/**
* Brand
*
* #ORM\Table()
* #ORM\Entity
*/
class Brand
{
/**
* #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\ManyToMany(targetEntity="TypeBrand")
* #ORM\JoinTable(name="brand_type",
* joinColumns={#ORM\JoinColumn(name="brand_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="type_id", referencedColumnName="id")}
* )
*/
private $type;
/**
* #ORM\OneToMany(targetEntity="DealerBrand", mappedBy="brand")
*/
private $dealerBrand;
public function __construct()
{
$this->type = new ArrayCollection();
$this->dealerBrand = new ArrayCollection();
}
public function __toString()
{
return $this->getName();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Brand
*/
public function setName($name)
{
$this->name = $name;
$this->setSlug($name);
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function addType(TypeBrand $type)
{
$this->type[] = $type;
}
public function getType()
{
return $this->type;
}
public function addDealerBrand($dealerBrand)
{
$this->dealerBrand[] = $dealerBrand;
}
public function getDealerBrand()
{
return $this->dealerBrand;
}
}
Dealer Entity
/**
* Dealer
*
* #ORM\Table("dealer")
* #ORM\Entity(repositoryClass="Project\DealersBundle\Entity\Repository\DealerRepository")
* #Gedmo\Loggable
*/
class Dealer
{
/**
* #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, unique=true)
* #Assert\NotBlank()
* #Assert\Length(min = "10")
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="DealerBrand", mappedBy="dealer")
*
*/
private $dealerBrand;
public function __construct()
{
$this->dealerBrand = new ArrayCollection();
}
public function __toString()
{
return $this->getName();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Dealer
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function addDealerBrand($dealerBrand)
{
$this->dealerBrand[] = $dealerBrand;
}
public function getDealerBrand()
{
return $this->dealerBrand;
}
}
DealerBrand Entity
/**
* DealerBrand
*
* #ORM\Table("dealer_brand")
* #ORM\Entity
*/
class DealerBrand
{
/**
* #var integer
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Dealer", inversedBy="dealerBrand")
*/
private $dealer;
/**
* #var integer
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Brand", inversedBy="dealerBrand")
*/
private $brand;
/**
* Set dealer
*
* #param integer $dealer
* #return DealerBrand
*/
public function setDealer($dealer)
{
$this->dealer = $dealer;
return $this;
}
/**
* Get dealer
*
* #return integer
*/
public function getDealer()
{
return $this->dealer;
}
/**
* Set brand
*
* #param integer $brand
* #return DealerBrand
*/
public function setBrand($brand)
{
$this->brand = $brand;
return $this;
}
/**
* Get brand
*
* #return integer
*/
public function getBrand()
{
return $this->brand;
}
}
Now this are my formtypes
DealerBrandType
class DealerBrandType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('brand', 'entity', array(
'class' => 'Project\DealersBundle\Entity\Brand',
'property' => 'name',
'multiple' => true,
'expanded' => true,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Project\DealersBundle\Entity\DealerBrand'
)
);
}
public function getName()
{
return 'DealerBrand';
}
}
DealerType
class DealerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('dealerBrand', 'collection', array(
'type' => new DealerBrandType(),
'allow_add' => true,
'allow_delete' => true,
))
->add('Save', 'submit');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Project\DealersBundle\Entity\Dealer'
)
);
}
public function getName()
{
return 'dealerType';
}
}
And this is my controller
public function addAction()
{
$dealer = new Dealer();
$form = $this->createForm(new DealerType(), $dealer);
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$this->getDoctrine()->getManager()->persist($dealer);
$this->getDoctrine()->getManager()->flush();
return $this->redirect($this->generateUrl('dealers_list'));
}
return $this->render(
'ProjectDealersBundle:Dealers:AddDealer.html.twig',
array(
'form' => $form->createView()
)
);
}
If this is not the correct approach please tell me, tell me also if you see bad code, that way I can improve
* EDIT *
This is the result i need
http://tinypic.com/r/24pkzdc/8
You can see there the brands and so .. so the idea is that when saving the Dealer you also save the association with the brands
* END EDIT *
Thank you!
I'm not sure this is the best answer ever but you can achieve it by making a custom query in your form :
->add('contact', 'entity', array(
'class' => 'AcmeBundle:Entity'
'query_builder' => function ( \acme\bundle\Entity\entityRepository $c){
$qb = $c->createQueryBuilder('a');
return $qb->orderBy('a.nom', 'ASC')
->join('a.categories', 'c')
->where( $qb->expr()->in ( 'c.id', ':listCategories') )
->setParameter( 'listCategories', array (
7,
));
},
'attr' => array( 'class' => 'other')
))
;
}
by using an 'entity' field type, i can inject custom query with the option 'query_builder' ( for more information : http://symfony.com/doc/current/reference/forms/types/entity.html )
then, inside i declare an anonymous function ( http://php.net/manual/en/functions.anonymous.php ) to create my custom query with the createQueryBuilder.
With the createQueryBuilder , you create your custom query ( here i had almost like you a many to many relationship and i wanted to get only some of them with a filtering array ( the set parameter ) .
here is the doc for custom query : http://symfony.com/doc/current/book/doctrine.html
the results of the query, if not null , will be displayed in your form
/** ALTERNATIVE ANSWER **/
if you want to display your dealers AND brands with a tree in your select then your have to :
1) make a query to return an object container your dealers and brands
2) make an array with depth that the select will display as a tree :
here is an example to illustrate :
->add('contact', 'entity', array(
'class' => 'AcmeBundle:Entity'
'query_builder' => function ( \acme\bundle\Entity\entityRepository $c){
$dealers = $c->yourFunctionThatReturnesDealers('d');
$dealersGroupedByBrands = array();
foreach ( $dealers as $dealer) {
foreach ($dealers->getBrands() as $brand) {
$dealersGroupedByBrands[$brand->getName()][] = $dealer;
}
return $dealersGroupedByBrands;
},
'attr' => array( 'class' => 'other')
))
;
}
pretty cool , no ?