Doctrine doesn't link to associations - php

I'm trying to add entries to a MySQL database by using a REST endpoint in a Symfony 3.1 app (and PHP 7.1).
I have two entities: Role and Permission, in a many to many relationship.
/**
* Role
*
* #ORM\Table(name="role")
* #ORM\Entity(repositoryClass="DruideBundle\Entity\Repository\RoleRepository")
*
*/
class Role
{
/**
* #var string
* #ORM\Column(name="titre", type="string", length=100, nullable=false)
*/
private $titre = '';
/**
* #var integer
*
* #ORM\Column(name="id_role", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idRole;
/**
* #var \Doctrine\Common\Collections\Collection
* #Type("array<DruideBundle\Entity\Permission>")
* #ORM\ManyToMany(targetEntity="DruideBundle\Entity\Permission", mappedBy="idRole", cascade={"persist", "remove"})
*/
private $idPermission;
/**
* #return string
*/
public function getTitre() {
return $this->titre;
}
/**
* #param string $titre
*/
public function metsTitre($titre) {
$this->titre = $titre;
}
/**
* #param string $titre
*/
public function setTitre($titre) {
$this->titre = $titre;
}
public function getId() {
return $this->idRole;
}
public function addIdPermission($permission) {
$this->idPermission->add($permission);
}
public function removeIdPermission($permission) {
$this->idPermission->remove($permission);
}
public function setIdPermission($permission) {
$this->idPermission = $permission;
}
public function getIdPermission() {
return $this->idPermission;
}
/**
* Constructor
*/
public function __construct()
{
$this->idPermission = new \Doctrine\Common\Collections\ArrayCollection();
}
}
/**
* Permission
*
* #ORM\Table(name="permission", uniqueConstraints={#ORM\UniqueConstraint(name="valeur", columns={"cle"})})
* #ORM\Entity(repositoryClass="DruideBundle\Entity\Repository\PermissionRepository")
*/
class Permission
{
/**
* #var string
*
* #ORM\Column(name="cle", type="string", length=100, nullable=true)
*/
private $cle;
/**
* #return string
*/
public function getCle() {
return $this->cle;
}
/**
* #param string $cle
*/
public function metsCle($cle) {
$this->cle = $cle;
}
/**
* #param string $cle
*/
public function setCle($cle) {
$this->cle = $cle;
}
/**
* #return string
*/
public function getDescription() {
return $this->description;
}
/**
* #param string $description
*/
public function metsDescription($description) {
$this->description = $description;
}
/**
* #param string $description
*/
public function setDescription($description) {
$this->description = $description;
}
public function getId() {
return $this->idPermission;
}
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255, nullable=true)
*/
private $description;
/**
* #var integer
*
* #ORM\Column(name="id_permission", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idPermission;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="DruideBundle\Entity\Role", inversedBy="idPermission")
* #ORM\JoinTable(name="role_permission",
* joinColumns={
* #ORM\JoinColumn(name="id_permission", referencedColumnName="id_permission")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_role", referencedColumnName="id_role")
* }
* )
*/
private $idRole;
/**
* Constructor
*/
public function __construct()
{
$this->idRole = new \Doctrine\Common\Collections\ArrayCollection();
}
}
class RoleType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('titre', TextType::class)->
add('idPermission', CollectionType::class, array('entry_type' => PermissionType::class));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'DruideBundle\Entity\Role', 'allow_extra_fields' => true
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'druidebundle_role';
}
}
class RoleController extends FOSRestController implements ClassResourceInterface
{
public function postAction(Request $request)
{
$form = $this->createForm(RoleType::class, null, [
'csrf_protection' => false,
]);
$form->submit($request->request->all());
if (!$form->isValid()) {
return View::create($form->getErrors(true), Response::HTTP_BAD_REQUEST);
}
/**
* #var $role Role
*/
$role = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($role);
$em->flush();
$routeOptions = [
'id' => $role->getId(),
'_format' => $request->get('_format'),
];
$reponse = $this->routeRedirectView('get_roles', $routeOptions, Response::HTTP_CREATED);
return $this->handleView($reponse);
}
Now, if I send this JSON to create a new role:
{ "titre": "Test Pascal", "permissions": [ { "id": 1 }, { "id": 3 } ] }
I would expect it to create a new Role, and make links to the two existing permissions. Sadly, Doctrine is only creating the Role entity:
[2017-12-04 21:02:08] doctrine.DEBUG: "START TRANSACTION" [] []
[2017-12-04 21:02:08] doctrine.DEBUG: INSERT INTO role (titre) VALUES (?) {"1":"Test Pascal"} []
[2017-12-04 21:02:08] doctrine.DEBUG: "COMMIT" [] []
What is missing?

Related

Symfony 3 - expected argument of type array given

I have found all subjects on google and did not get the solution.
I have this error:
Expected argument of type "PL\PlatformBundle\Entity\Module",
"Doctrine\Common\Collections\ArrayCollection" given
I want to select multiple Modules in the list but when I click on save I have the error.
My Form:
<?php
namespace PL\AppAccueilBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Doctrine\ORM\EntityRepository;
class AppAccueilModuleType extends AbstractType {
private $idCompany;
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$this->idCompany = $options['idCompany'];
$builder
->add('company', CollectionType::class, array(
'class' => 'PLUserBundle:Company',
'choice_label' => 'Name',
'multiple' => false
))
->remove('company')
->add('module', EntityType::class, array(
'class' => 'PLPlatformBundle:Module',
'choice_label' => function ($allChoices) {
if ($allChoices->getRef() != null)
return $allChoices->getRef() . ' - ' . $allChoices->getTitre() . " (" . $allChoices->getSupport()->getTitre() . ")";
else
return $allChoices->getTitre() . " (" . $allChoices->getSupport()->getTitre() . ")";
},
'multiple' => true,
'expanded' => false,
'query_builder' => function (EntityRepository $er) {
return $er->getModulesListForCompany($this->idCompany);
},
))
->add('save', SubmitType::class);;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'PL\AppAccueilBundle\Entity\AppAccueilModule',
'idCompany' => null
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix() {
return 'pl_appaccueilbundle_appaccueilmodule';
}
}
My Entity:
<?php
namespace PL\PlatformBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use PL\AppAccueilBundle\Entity\ParcoursModule;
use PL\AppChallengeBundle\Entity\ChallengeModule;
use PL\UserBundle\Entity\Company;
use PL\UserBundle\Entity\Logo;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Module
* #ORM\Table(name="pl_module")
* #ORM\Entity(repositoryClass="PL\PlatformBundle\Repositor\ModuleRepository")
*/
class Module {
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* #var string
* #ORM\Column(name="ref", type="string", length=255, nullable=true)
*/
private $ref;
/**
* #ORM\ManyToOne(targetEntity="PL\PlatformBundle\Entity\Support")
* #ORM\JoinColumn(nullable=false)
*/
private $support;
/**
* #ORM\ManyToOne(targetEntity="PL\PlatformBundle\Entity\Theme")
* #ORM\JoinColumn(nullable=false)
*/
private $theme;
/**
* #ORM\ManyToOne(targetEntity="PL\UserBundle\Entity\Company")
* #ORM\JoinColumn(nullable=false)
*/
private $company;
/**
* #ORM\OneToOne(targetEntity="PL\UserBundle\Entity\Logo", cascade="persist")
* #ORM\JoinTable(name="pl_logo")
* #Assert\Valid()
*/
private $logo;
/**
* #ORM\ManyToMany(targetEntity="PL\PlatformBundle\Entity\Document", cascade="persist")
* #ORM\JoinTable(name="pl_module_document")
*/
private $documents;
/**
* #ORM\ManyToMany(targetEntity="PL\PlatformBundle\Entity\ChasseZone", cascade="persist")
* #ORM\JoinTable(name="pl_module_chasse_zone")
*/
private $chasseZone;
/**
* #ORM\ManyToMany(targetEntity="PL\PlatformBundle\Entity\Question", cascade="persist")
* #ORM\JoinTable(name="pl_module_quiz")
* #ORM\OrderBy({"position" = "ASC"})
*/
private $question;
/**
* #var string
* #ORM\Column(name="code_iframe", type="string", length=4095, nullable=true)
*/
private $codeIframe;
/**
* #ORM\OneToOne(targetEntity="PL\PlatformBundle\Entity\ChasseImage", cascade="persist")
* #ORM\JoinTable(name="pl_module_chasse_image")
* #Assert\Valid()
*/
private $chasseImage;
/**
* #ORM\OneToMany(targetEntity="PL\AppAccueilBundle\Entity\ParcoursModule", mappedBy="module", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
private $parcours;
/**
* #ORM\OneToMany(targetEntity="PL\AppChallengeBundle\Entity\ChallengeModule", mappedBy="module", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
private $challenges;
/**
* Get id
* #return int
*/
public function getId() {
return $this->id;
}
/**
* Set titre
* #param string $titre
* #return module
*/
public function setTitre($titre) {
$this->titre = $titre;
return $this;
}
/**
* Get titre
* #return string
*/
public function getTitre() {
return $this->titre;
}
/**
* Set ref
* #param string $ref
* #return module
*/
public function setRef($ref) {
$this->ref = $ref;
return $this;
}
/**
* Get ref
* #return string
*/
public function getRef() {
return $this->ref;
}
/**
* Set support
* #param Support $support
* #return Module
*/
public function setSupport(Support $support) {
$this->support = $support;
return $this;
}
/**
* Get support
* #return Support
*/
public function getSupport() {
return $this->support;
}
/**
* Set theme
* #param Theme $theme
* #return Module
*/
public function setTheme(Theme $theme) {
$this->theme = $theme;
return $this;
}
/**
* Get theme
* #return Theme
*/
public function getTheme() {
return $this->theme;
}
/**
* Set company
*
* #param Company $company
* #return Module
*/
public function setCompany(Company $company) {
$this->company = $company;
return $this;
}
/**
* Get company
* #return Company
*/
public function getCompany() {
return $this->company;
}
/**
* Set logo
* #param Logo $logo
* #return Module
*/
public function setLogo(Logo $logo = null) {
$this->logo = $logo;
return $this;
}
/**
* Get logo
* #return Logo
*/
public function getLogo() {
return $this->logo;
}
/**
* Constructor
*/
public function __construct() {
$this->documents = new ArrayCollection();
$this->chasseZone = new ArrayCollection();
$this->question = new ArrayCollection();
$this->parcours = new ArrayCollection();
$this->challenges = new ArrayCollection();
}
/**
* Add document
* #param Document $document
* #return Module
*/
public function addDocument(Document $document) {
$this->documents[] = $document;
return $this;
}
/**
* Remove document
* #param Document $document
*/
public function removeDocument(Document $document) {
$this->documents->removeElement($document);
}
/**
* Get documents
* #return \Doctrine\Common\Collections\Collection
*/
public function getDocuments() {
return $this->documents;
}
/**
* Set codeIframe
* #param string $codeIframe
* #return Module
*/
public function setCodeIframe($codeIframe) {
$this->codeIframe = $codeIframe;
return $this;
}
/**
* Get codeIframe
* #return string
*/
public function getCodeIframe() {
return $this->codeIframe;
}
/**
* Set chasseImage
* #param ChasseImage $chasseImage
* #return Module
*/
public function setChasseImage(ChasseImage $chasseImage = null) {
$this->chasseImage = $chasseImage;
return $this;
}
/**
* Get chasseImage
* #return ChasseImage
*/
public function getChasseImage() {
return $this->chasseImage;
}
/**
* Remove chasseZone
* #param ChasseZone $ch
*/
public function removeChasseZone(ChasseZone $ch) {
$this->chasseZone->removeElement($ch);
}
/**
* Get chasseZone
* #return \Doctrine\Common\Collections\Collection
*/
public function getChasseZone() {
return $this->chasseZone;
}
/**
* Add chasseZone
* #param \PL\PlatformBundle\Entity\ChasseZone $chasseZone
* #return Module
*/
public function addChasseZone(ChasseZone $chasseZone) {
$this->chasseZone[] = $chasseZone;
return $this;
}
/**
* Get questions
* #return \Doctrine\Common\Collections\Collection
*/
public function getQuestions() {
return $this->question;
}
/**
* Add question
* #param \PL\PlatformBundle\Entity\Question $question
* #return Module
*/
public function addQuestion(Question $question) {
$this->question[] = $question;
return $this;
}
/**
* Remove question
* #param Question $question
*/
public function removeQuestion(Question $question) {
$this->question->removeElement($question);
}
/**
* Get parcours
* #return \Doctrine\Common\Collections\Collection
*/
public function getParcours() {
return $this->parcours;
}
/**
* Add parcours
* #param ParcoursModule $parcours
* #return Module
*/
public function addParcours(ParcoursModule $parcours) {
$this->parcours[] = $parcours;
$parcours->setModule($this);
return $this;
}
/**
* Remove parcours
* #param ParcoursModule $parcours
*/
public function removeParcours(ParcoursModule $parcours) {
$this->parcours->removeElement($parcours);
}
/**
* Get Challenges
* #return ArrayCollection
*/
public function getChallenges() {
return $this->challenges;
}
/**
* Add challenge module
* #param ChallengeModule $challenge
* #return Module
*/
public function addChallenge(ChallengeModule $challenge) {
$this->challenges[] = $challenge;
$challenge->setModule($this);
return $this;
}
/**
* Remove challenges module
* #param ChallengeModule $challenge
*/
public function removeChallenges(ChallengeModule $challenge) {
$this->challenges->removeElement($challenge);
}
}
Thinks

ManyToOne Doctrine Database Relation with Extra Field Symfony 3

I'm just new in Symfony so please bear with me.
I want to create a database relationship in Symfony, let say..
I have Entity Currency, AddRate and AddRateCurrency
These entities would accomplish 3 simple things.
The User will gonna add different types of currencies using Currency entity.
The User will gonna choose which branch these currencies must be added using AddRate entity.
The User will gonna update the rates of each currency using AddRateCurrency.
The problem that I encountered is
Expected value of type "Doctrine\Common\Collections\Collection|array"
for association field "MontealBundle\Entity\AddRate#$currency", got
"MontealBundle\Entity\Currency" instead.
On adding rate AddRateForm I used EntityType to embed my form.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('branch', EntityType::class, array(
'label' => 'Branch',
'class'=>'MontealBundle\Entity\Branch',
'query_builder' => function (BranchRepository $er) {
return $er->findAllActiveBranches();
},
'choice_label' => 'name',
'placeholder' => 'Choose a Branch',
'empty_data' => null,
'required' => true,
'constraints' => array(
new NotBlank(array("message" => 'Branch is required.'))
)
))
->add('currency', EntityType::class, array(
'label' => 'Currency',
'class'=>'MontealBundle\Entity\Currency',
'query_builder' => function (CurrencyRepository $er) {
return $er->findAllActiveCurrencies();
},
'choice_label' => 'currency',
'placeholder' => 'Choose a Currency',
'empty_data' => null,
'multiple' => true,
// 'expanded' => true,
'required' => true,
'constraints' => array(
new NotBlank(array("message" => 'Currency is required.'))
)
))
;
}
On Currency Entity
/**
* #ORM\Entity
* #ORM\Table(name="currency")
* #ORM\Entity(repositoryClass="MontealBundle\Repository\CurrencyRepository")
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity(fields={"code"}, message="Code is already used.")
* #UniqueEntity(fields={"currency"}, message="Currency is already used.")
*/
class Currency
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Administrator")
* #ORM\JoinColumn(name="administrator_id", referencedColumnName="id")
*/
private $administrator;
/**
* #ORM\Column(type="string", unique=true)
*/
private $currency;
/**
* #ORM\Column(type="string", unique=true)
*/
private $code;
/**
* #ORM\Column(type="integer")
*/
private $status;
/**
* #ORM\Column(type="date")
*/
private $createdAt;
/**
* #ORM\Column(type="date", nullable=true)
*/
private $updatedAt;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getAdministrator()
{
return $this->administrator;
}
/**
* #param mixed $administrator
*/
public function setAdministrator($administrator)
{
$this->administrator = $administrator;
}
/**
* #return mixed
*/
public function getCurrency()
{
return $this->currency;
}
/**
* #param mixed $currency
*/
public function setCurrency($currency)
{
$this->currency = $currency;
}
/**
* #return mixed
*/
public function getCode()
{
return $this->code;
}
/**
* #param mixed $code
*/
public function setCode($code)
{
$this->code = $code;
}
/**
* #return mixed
*/
public function getStatus()
{
return $this->status;
}
/**
* #param mixed $status
*/
public function setStatus($status)
{
$this->status = $status;
}
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* #ORM\PrePersist
*/
public function setCreatedAt()
{
$this->createdAt = new \DateTime();
}
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* #ORM\PreUpdate()
*/
public function setUpdatedAt()
{
$this->updatedAt = new \DateTime();
}
}
On AddRate Entity
/**
* #ORM\Entity
* #ORM\Table(name="add_rate")
* #ORM\Entity(repositoryClass="MontealBundle\Repository\AddRateRepository")
* #ORM\HasLifecycleCallbacks()
*/
class AddRate
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Branch")
* #ORM\JoinColumn(name="branch_id", referencedColumnName="id")
*/
private $branch;
/**
* #ORM\OneToMany(targetEntity="AddRateCurrency", mappedBy="add_rate")
*/
private $currency;
public function __construct()
{
$this->currency = new ArrayCollection();
}
/**
* #ORM\ManyToOne(targetEntity="Administrator")
* #ORM\JoinColumn(name="administrator_id", referencedColumnName="id")
*/
private $administrator;
/**
* #ORM\Column(type="date")
*/
private $createdAt;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getBranch()
{
return $this->branch;
}
/**
* #param mixed $branch
*/
public function setBranch($branch)
{
$this->branch = $branch;
}
/**
* #return mixed
*/
public function getCurrency()
{
return $this->currency;
}
/**
* #param mixed $currency
*/
public function setCurrency($currency)
{
$this->currency = $currency;
}
public function addCurrency(Currency $currency) {
$this->currency[] = $currency;
}
/**
* #return mixed
*/
public function getAdministrator()
{
return $this->administrator;
}
/**
* #param mixed $administrator
*/
public function setAdministrator($administrator)
{
$this->administrator = $administrator;
}
/**
* #ORM\PrePersist
*/
public function setCreatedAt()
{
$this->createdAt = new \DateTime();
}
public function getCreatedAt()
{
return $this->createdAt;
}
}
On AddRateCurrency Entity
/**
* #ORM\Entity
* #ORM\Table(name="add_rate_currency")
*/
class AddRateCurrency
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AddRate", inversedBy="AddRateCurrency")
* #ORM\JoinColumn(nullable=false)
*/
private $currency;
/**
* #ORM\Column(type="string")
*/
private $rate;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getCurrency()
{
return $this->currency;
}
/**
* #param mixed $currency
*/
public function setCurrency($currency)
{
$this->currency = $currency;
}
/**
* #return mixed
*/
public function getRate()
{
return $this->rate;
}
/**
* #param mixed $rate
*/
public function setRate($rate)
{
$this->rate = $rate;
}
}
In your AddRate entity, remove the setCurrency function. Symfony will then (automatically) use your addCurrency function instead since $currency is an ArrayCollection.
Your choice of variable names adds to the confusion somewhat. In AddRate, you may want to consider renaming $currency to $currencies.

Symfony2 form update one-to-one relation

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;

Multiple file upload with VlabsMediaBundle in Symfony2

i'm trying to make a multiple upload file on an entity with the vlabsmediaBundle.
So i have an advert who can have many images but each can be linked with only one advert.
I followed the tutorial(tuto) of the bundle but get an error.
Here:
Entity Image.php
namespace MDB\PlatformBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Vlabs\MediaBundle\Entity\BaseFile as VlabsFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Image
*
* #ORM\Table()
* #ORM\Entity
*/
class Image extends VlabsFile {
/**
* #var string
*
* #ORM\Column(name="url", type="string", length=255)
*/
private $url;
/**
* #var string
*
* #ORM\Column(name="alt", type="string", length=255)
*/
private $alt;
/**
* #ORM\ManyToOne(targetEntity="MDB\AnnonceBundle\Entity\Annonce", inversedBy="images")
* #ORM\JoinColumn(nullable=true)
*/
private $annonce;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set url
*
* #param string $url
* #return Image
*/
public function setUrl($url) {
$this->url = $url;
return $this;
}
/**
* Get url
*
* #return string
*/
public function getUrl() {
return $this->url;
}
/**
* Set alt
*
* #param string $alt
* #return Image
*/
public function setAlt($alt) {
$this->alt = $alt;
return $this;
}
/**
* Get alt
*
* #return string
*/
public function getAlt() {
return $this->alt;
}
public function getAnnonce() {
return $this->annonce;
}
public function setAnnonce($annonce) {
$this->annonce = $annonce;
}
/**
* #var string $path
*
* #ORM\Column(name="path", type="string", length=255)
* #Assert\Image()
*/
private $path;
/**
* Set path
*
* #param string $path
* #return Image
*/
public function setPath($path) {
$this->path = $path;
return $this;
}
/**
/**
* Get path
*
* #return string
*/
public function getPath() {
return $this->path;
}
}
Annonce.php (advert enity)
<?php
namespace MDB\AnnonceBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Vlabs\MediaBundle\Annotation\Vlabs;
/**
* Annonce
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="MDB\AnnonceBundle\Entity\AnnonceRepository")
*/
class Annonce {
public function __construct() {
$this->date = new \Datetime();
$this->categories = new ArrayCollection();
$this->images = new ArrayCollection();
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/**
* #ORM\Column(name="date", type="date")
*/
private $date;
/**
* #var float
*
* #ORM\Column(name="prix", type="float")
*/
private $prix;
/**
* #ORM\ManyToOne(targetEntity="MDB\AdresseBundle\Entity\Ville", inversedBy="annonces")
*/
private $ville;
/**
* #ORM\ManyToMany(targetEntity="MDB\AnnonceBundle\Entity\Category", cascade={"persist"})
*/
private $categories;
/**
* #ORM\ManyToMany(targetEntity="MDB\UserBundle\Entity\User")
*
*/
private $wishlist;
/**
* #var boolean
*
* #ORM\Column(name="telAppear", type="boolean")
*/
private $telAppear;
/**
* #ORM\ManyToOne(targetEntity="MDB\UserBundle\Entity\User", inversedBy="annonces")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #var VlabsFile
* #ORM\OneToMany(targetEntity="MDB\PlatformBundle\Entity\Image", mappedBy="annonce")
* #Vlabs\Media(identifier="image_entity", upload_dir="files/images")
* #Assert\Valid()
*/
private $images;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set titre
*
* #param string $titre
* #return Annonce
*/
public function setTitre($titre) {
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* #return string
*/
public function getTitre() {
return $this->titre;
}
/**
* Set description
*
* #param string $description
* #return Annonce
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription() {
return $this->description;
}
/**
* Set prix
*
* #param float $prix
* #return Annonce
*/
public function setPrix($prix) {
$this->prix = $prix;
return $this;
}
/**
* Get prix
*
* #return float
*/
public function getPrix() {
return $this->prix;
}
public function addCategory(Category $category) {
// Ici, on utilise l'ArrayCollection vraiment comme un tableau
$this->categories[] = $category;
return $this;
}
public function removeCategory(Category $category) {
$this->categories->removeElement($category);
}
public function getCategories() {
return $this->categories;
}
public function getDate() {
return $this->date;
}
public function setDate($date) {
$this->date = $date;
}
public function getWishlist() {
return $this->wishlist;
}
public function setWishlist($wishlist) {
$this->wishlist = $wishlist;
}
public function getVille() {
return $this->ville;
}
public function setVille($ville) {
$this->ville = $ville;
}
public function getTelAppear() {
return $this->telAppear;
}
public function setTelAppear($telAppear) {
$this->telAppear = $telAppear;
}
public function getUser() {
return $this->user;
}
public function setUser($user) {
$this->user = $user;
}
public function addImage(Image $image) {
$this->images[] = $image;
$image->setUser($this);
return $this;
}
public function removeImage(Image $image) {
$this->images->removeElement($image);
}
public function getImages() {
return $this->images;
}
}
config.yml
vlabs_media:
image_cache:
cache_dir: files/c
mapping:
image_entity:
class: MDB\PlatformBundle\Entity\Image
AnnonceSellType.php
<?php
namespace MDB\AnnonceBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use MDB\PlatformBundle\Form\ImageType;
class AnnonceSellType extends AbstractType {
private $arrayListCat;
public function __construct( $arrayListCat)
{
$this->arrayListCat = $arrayListCat;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->remove('wishlist')
->remove('date')
->remove('images')
->add('titre')
->add('description', 'textarea')
->add('prix')
->add('categories', 'choice', array(
'choices' => $this->arrayListCat,
'multiple' => true,
'mapped'=>false,
))
//->add('images', 'collection', array('type' => new ImageType()))
->add('image', 'vlabs_file', array(
'required' => false
))
;
}
/**
* #return string
*/
public function getName() {
return 'mdb_annoncebundle_annonce_sell';
}
public function getParent() {
return new AnnonceType();
}
}
And i have the following error : Catchable Fatal Error: Argument 1 passed to Vlabs\MediaBundle\EventListener\BaseFileListener::preSetData() must be an instance of Symfony\Component\Form\Event\DataEvent, instance of Symfony\Component\Form\FormEvent given in C:\wamp\www\Mdb\vendor\vlabs\media-bundle\Vlabs\MediaBundle\EventListener\BaseFileListener.php line 59
i tried to change this line :
->add('image', 'vlabs_file', array(
'required' => false
))
with :
->add('images', 'collection', array('type' => 'vlabs_file'))
but i just have the image label who appear in this case
if someone have an idea ?
I was a little bit investigating about your problem and Vlabs Media Bundle seems to be unmaintained. There is form listener on PRE_SET_DATA event (Vlabs\MediaBundle\EventListener\BaseFileListener::preSetData()) that is not compatible with new version of Symfony (it expects DataEvent as parametr instead FormEvent). They fixed it but after 1.1.1 release (you can compare dates from links). If really wanna give a try, change composer.json and run composer update. I can't guarantee there will not be another possible issues.
composer.json
"vlabs/media-bundle": "dev-master"
Library update
composer update vlabs/media-bundle

Combine Questionnaire with User entity in form - symfony2

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

Categories