I have a table stock which have relation 1:m with a table called warranty. I made this entity for stock table:
<?php
namespace StockBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
use CompanyBundle\Entity\Company;
use StockBundle\Entity\NStockStatus;
use ProductBundle\Entity\NLength;
use ProductBundle\Entity\NWeight;
/**
* #ORM\Table(name="stock")
* #ORM\Entity(repositoryClass="StockBundle\Entity\Repository\KStockRepository")
* #Gedmo\SoftDeleteable(fieldName="deletedAt")
*/
class KStock {
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Product", inversedBy="stocks" )
* #ORM\JoinColumn(name="product", referencedColumnName="upc")
*/
protected $product;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="CompanyBundle\Entity\Company", inversedBy="companyHasStock" )
* #ORM\JoinColumn(name="company", referencedColumnName="id")
*/
protected $company;
/**
*
* #ORM\ManyToOne(targetEntity="StockBundle\Entity\NCondition", inversedBy="stocks" )
* #ORM\JoinColumn(name="kcondition", referencedColumnName="id")
*/
protected $condition;
/**
*
* #ORM\ManyToOne(targetEntity="StockBundle\Entity\NStockStatus", inversedBy="stocks" )
* #ORM\JoinColumn(name="status", referencedColumnName="id")
*/
protected $status;
/**
*
* #ORM\ManyToOne(targetEntity="StockBundle\Entity\Warranty", inversedBy="stocks" )
* #ORM\JoinColumn(name="warranty", referencedColumnName="id")
*/
// protected $warranty;
/**
* #ORM\Column(type="string", length=255, name="sku")
*/
protected $sku;
/**
* #ORM\Column(type="integer")
*/
protected $availability;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
*
* #ORM\Column(type="decimal", precision=19, scale=4)
*/
protected $price;
/**
* #ORM\ManyToOne(targetEntity="StockBundle\Entity\NUnit")
* #ORM\JoinColumn(name="unit", referencedColumnName="id")
*/
protected $unit;
/**
*
* #ORM\Column(type="decimal", precision=4, scale=2)
*/
protected $width;
/**
*
* #ORM\Column(type="decimal", precision=4, scale=2)
*/
protected $height;
/**
*
* #ORM\Column(type="decimal", precision=4, scale=2)
*/
protected $weight;
/**
*
* #ORM\Column(type="decimal", precision=4, scale=2)
*/
protected $length;
/**
*
* #ORM\Column(type="integer")
*/
protected $amount;
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\NWeight")
* #ORM\JoinColumn(name="nweight", referencedColumnName="id")
*/
protected $nweight;
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\NLength")
* #ORM\JoinColumn(name="nlength", referencedColumnName="id")
*/
protected $nlength;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\Column(name="deletedAt", type="datetime", nullable=true)
*/
protected $deletedAt;
public function setProduct(\ProductBundle\Entity\Product $param) {
$this->product = $param;
}
public function getProduct() {
return $this->product;
}
public function setCompany(\CompanyBundle\Entity\Company $param) {
$this->company = $param;
}
public function getCompany() {
return $this->company;
}
public function setCondition(\StockBundle\Entity\NCondition $condition) {
$this->condition = $condition;
}
public function getCondition() {
return $this->condition;
}
public function setStatus(\StockBundle\Entity\NStockStatus $param) {
$this->status = $param;
}
public function getStatus() {
return $this->status;
}
public function setSku($param) {
$this->sku = $param;
}
public function getSku() {
return $this->sku;
}
public function setAvailability($param) {
$this->availability = $param;
}
public function getAvailability() {
return $this->availability;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setPrice($param) {
$this->price = $param;
}
public function getPrice() {
return $this->price;
}
public function setUnit(\StockBundle\Entity\NUnit $unit) {
$this->unit = $unit;
}
public function getUnit() {
return $this->unit;
}
public function setWidth($width) {
$this->width = $width;
}
public function getWidth() {
return $this->width;
}
public function setHeight($height) {
$this->height = $height;
}
public function getHeight() {
return $this->height;
}
public function setWeight($weight) {
$this->weight = $weight;
}
public function getWeight() {
return $this->weight;
}
public function setLength($length) {
$this->length = $length;
}
public function getLength() {
return $this->length;
}
public function setAmount($amount) {
$this->amount = $amount;
}
public function getAmount() {
return $this->amount;
}
public function setCreated($created) {
$this->created = $created;
}
public function getCreated() {
return $this->created;
}
public function setModified($modified) {
$this->modified = $modified;
}
public function getModified() {
return $this->modified;
}
public function getDeletedAt() {
return $this->deletedAt;
}
public function setDeletedAt($deletedAt) {
$this->deletedAt = $deletedAt;
}
public function setNWeight(\ProductBundle\Entity\NWeight $nweight) {
$this->nweight = $nweight;
}
public function getNWeight() {
return $this->nweight;
}
public function setNLength(\ProductBundle\Entity\NLength $nlength) {
$this->nlength = $nlength;
}
public function getNLength() {
return $this->nlength;
}
public function __toString() {
return $this->company . ' -- ' . $this->product;
}
}
And this entity for Warranty table:
<?php
namespace StockBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ORM\Entity
* #ORM\Table(name="warranty")
* #Gedmo\SoftDeleteable(fieldName="deletedAt")
*/
class Warranty {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Warranty")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Product")
* #ORM\JoinColumn(name="product", referencedColumnName="upc")
*/
protected $product;
/**
* #ORM\ManyToOne(targetEntity="StockBundle\Entity\NCondition")
* #ORM\JoinColumn(name="kcondition", referencedColumnName="id")
*/
protected $kcondition;
/**
* #ORM\ManyToOne(targetEntity="CompanyBundle\Entity\Company")
* #ORM\JoinColumn(name="company", referencedColumnName="id")
*/
protected $company;
/**
*
* #ORM\Column(type="date")
*/
protected $valid_time;
/**
*
* #ORM\Column(type="text")
*/
protected $description;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\Column(name="deletedAt", type="datetime", nullable=true)
*/
protected $deletedAt;
/**
* #ORM\OneToMany(targetEntity="StockBundle\Entity\KStock")
*/
protected $stocks;
public function getId() {
return $this->id;
}
public function setParent(Warranty $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setCreated($created) {
$this->created = $created;
}
public function getCreated() {
return $this->created;
}
public function setModified($modified) {
$this->modified = $modified;
}
public function getModified() {
return $this->modified;
}
public function getDeletedAt() {
return $this->deletedAt;
}
public function setDeletedAt($deletedAt) {
$this->deletedAt = $deletedAt;
}
public function setProduct(\ProductBundle\Entity\Product $product) {
$this->product = $product;
}
public function getProduct() {
return $this->product;
}
public function setCompany(\CompanyBundle\Entity\Company $company) {
$this->company = $company;
}
public function getCompany() {
return $this->company;
}
public function setKCondition(\StockBundle\Entity\NCondition $condition) {
$this->kcondition = $condition;
}
public function getKCondition() {
return $this->kcondition;
}
public function setValidTime($valid_time) {
$this->valid_time = $valid_time;
}
public function getValidTime() {
return $this->valid_time;
}
}
Then I create my KStockType.php with this code:
<?php
namespace StockBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use CatalogBundle\Form\KCatalogType;
class KStockType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('sku', 'text', array('required' => true, 'label' => 'SKU (Número de Referencia)'))
->add('price', 'money', array('label' => 'Precio', 'currency' => 'VEF'))
->add('unit', 'entity', array('label' => 'Moneda', 'class' => 'StockBundle:NUnit', 'property' => 'name', 'required' => true, 'multiple' => false, 'expanded' => false))
->add('amount', 'integer', array('label' => 'Cantidad'))
->add('status', 'entity', array('label' => 'Estado', 'class' => 'StockBundle:NStockStatus', 'property' => 'name', 'required' => true, 'multiple' => false, 'expanded' => false))
->add('condition', 'entity', array('label' => 'Condición del producto', 'class' => 'ProductBundle:NCondition', 'property' => 'name', 'required' => true, 'multiple' => false, 'expanded' => false))
->add('width', 'integer', array('required' => true, 'label' => 'Ancho'))
->add('height', 'integer', array('required' => true, 'label' => 'Alto'))
->add('length', 'integer', array('required' => true, 'label' => 'Largo'))
->add('nlength', 'entity', array('label' => 'Unidad de Medida', 'class' => 'ProductBundle:NLength', 'property' => 'name', 'required' => true, 'multiple' => false, 'expanded' => false))
->add('weight', 'integer', array('required' => true, 'label' => 'Peso'))
->add('nweight', 'entity', array('label' => 'Unidad de Peso', 'class' => 'ProductBundle:NWeight', 'property' => 'name', 'required' => true, 'multiple' => false, 'expanded' => false))
->add('description', 'textarea', array('label' => 'Descripción'))
->add('start_date', 'date', array('label' => 'Fecha (Inicio de la Publicación)', 'widget' => 'single_text', 'format' => 'yyyy-MM-dd', 'attr' => array('class' => 'dpicker')))
->add('warranty', 'textarea', array('required' => true, 'label' => 'Condiciones de Garantía'))
->add('valid_time', 'date', array('label' => 'Tiempo de Validez de la Garantía', 'widget' => 'single_text', 'format' => 'yyyy-MM-dd', 'attr' => array('class' => 'dpicker')));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'StockBundle\Entity\KStock'
));
}
/**
* #return string
*/
public function getName() {
return 'stockbundle_kstock';
}
}
As you may notice I have some fields on that form that belongs to warranty. Then in my controller I have this method for edit action:
/**
* Stock
*
* #Route("/edit/{company_id}/{product_id}", name="stock_edit")
* #Method("GET")
*/
public function editAction($company_id, $product_id) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('StockBundle:KStock')->findOneBy(array('company' => $company_id, 'product' => $product_id));
$response = array();
$response["response"] = true;
if (!$entity) {
$response['response'] = FALSE;
$response['message'] = 'Unable to find Stock entity.';
return new JsonResponse($response);
}
$product = $em->getRepository('ProductBundle:Product')->find($product_id);
$company = $em->getRepository('CompanyBundle:Company')->find($company_id);
if (!$product || !$company) {
$response['response'] = FALSE;
$response['message'] = 'Error not found product or company';
return new JsonResponse($response);
}
$editForm = $this->createForm(new KStockType(), $entity);
return $this->render("StockBundle:Stock:edit.html.twig", array('entity' => $entity, 'edit_form' => $editForm->createView()));
}
But when I call it I got this error:
Neither the property "start_date" nor one of the methods
"getStartDate()", "isStartDate()", "hasStartDate()", "_get()" or
"_call()" exist and have public access in class
"StockBundle\Entity\KStock".
What I am missing?
Take a look at this page: Symfony documentation
A little brief from that:
In more complex examples, you can embed entire forms, which is useful when
creating forms that expose one-to-many relationships
I think you have two possible aproaches:
1 - Look how to make forms with relational entities
2 - Make a form without a class and manage all information in your controller ( I wouldn't use this)
Try to replace your line about start_date in your form by:
->add('start_date', 'date', array('label' => 'Fecha (Inicio de la Publicación)', 'widget' => 'single_text', 'format' => 'yyyy-MM-dd', 'attr' => array('class' => 'dpicker'), 'mapped' => false))
Related
I created a datatransformer to store dates on unixtime on the database but show it as datetime on the form
this is the transformer
class IntegerToTimestampTransformer implements DataTransformerInterface
{
public function transform($timestamp)
{
/**
* This if sentenceis because when eidt $timestamp is a timestamp(unix)
* but when create is a DateTime ¿¿???
*/
if($timestamp instanceof \DateTime){
return $timestamp;
}
return (new \DateTime())->setTimestamp($timestamp);
}
public function reverseTransform($datetime)
{
if ($datetime === null) {
return $datetime;
}
return $datetime->getTimestamp();
}
}
this is the entity
/**
* Popup
*
* #ORM\Table(name="popup")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PopUpRepository")
*/
class Popup
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #Gedmo\Translatable()
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #Gedmo\Translatable()
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var int
*
* #ORM\Column(name="activation_date", type="integer", nullable=true)
*/
private $activationDate;
/**
* #var int
*
* #ORM\Column(name="deactivation_date", type="integer", nullable=true)
*/
private $deactivationDate;
/**
* #var boolean
*
* #ORM\Column(name="active", type="boolean")
*/
private $active;
/**
* #var Collection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Role")
* #ORM\JoinTable(name="popup_role", joinColumns={#ORM\JoinColumn(name="popup_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")})
*/
private $roles;
public function __construct()
{
$this->roles = new ArrayCollection();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getTitle():? string
{
return $this->title;
}
/**
* #param string $title
*/
public function setTitle(string $title): void
{
$this->title = $title;
}
/**
* #return string
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* #param string $description
*/
public function setDescription(string $description): void
{
$this->description = $description;
}
/**
* #return int
*/
public function getActivationDate(): ?int
{
return $this->activationDate;
}
/**
* #param int $activationDate
*/
public function setActivationDate(int $activationDate): void
{
$this->activationDate = $activationDate;
}
/**
* #return int
*/
public function getDeactivationDate(): ?int
{
return $this->deactivationDate;
}
/**
* #param int $deactivationDate
*/
public function setDeactivationDate(int $deactivationDate): void
{
$this->deactivationDate = $deactivationDate;
}
/**
* #return bool
*/
public function isActive(): ?bool
{
return $this->active;
}
/**
* #param bool $active
*/
public function setActive(bool $active): void
{
$this->active = $active;
}
/**
* Add role
*
* #param Role $role
*
* #return Role
*/
public function addRole(Role $role)
{
$this->roles[] = $role;
return $this;
}
/**
* Remove role
*
* #param Role $role
*/
public function removeRole(Role $role)
{
$this->roles->removeElement($role);
}
/**
* Get role
*
* #return Collection
*/
public function getRoles()
{
return $this->roles;
}
}
and the formtype
class PopupType extends AbstractType
{
private $tranformer;
public function __construct(IntegerToTimestampTransformer $tranformer){
$this->tranformer = $tranformer;
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$date_array = [
'label_attr' => [
'class' => 'font-weight-bold'
],
'required' => false
];
if(!$options['edit']){
$date_array['data'] = new \DateTime();
}
$builder
->add('title', TextType::class, [
'label_attr' => [
'class' => 'font-weight-bold'
],
])
->add('description', TextareaType::class, [
'label_attr' => [
'class' => 'font-weight-bold'
],
'required' => false
])
->add('activation_date', DateTimeType::class, $date_array)
->add('deactivation_date', DateTimeType::class,$date_array)
->add('active', CheckboxType::class, [
'label_attr' => [
'class' => 'font-weight-bold'
],
'required' => false
])
->add('roles', EntityType::class, [
'class' => Role::class,
'expanded' => true,
'multiple' => true,
'required' => true,
'label_attr' => [
'class' => 'font-weight-bold'
],
])
;
$builder->get('activation_date')->addModelTransformer($this->tranformer);
$builder->get('deactivation_date')->addModelTransformer($this->tranformer);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Popup::class,
'edit' => false
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_popup';
}
}
I debugged and I checked that in the transform function when I try to create on the crud the parameter passed to transform is a Datetime the there is no need to transformate the data but when I try to edit the parameter passed to transform is a unixtime integer the the transformation is needed, I don't understant that behaviour.
We have an error when trying to create a relationship within 2 tables like this
Llamadas -||--|<- LlamadaDerivada ->|--||- PersonaDerivada
And we are trying to create an only one create form with the "LlamadaDerivada" into it.
Inside Llamada entity
<?php
namespace xxxxBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
class Llamada {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="llamada_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string
* #Assert\Length(
* max = 50,
* maxMessage = "Your first name cannot be longer than {{ limit }} characters",
* )
* #ORM\Column(name="nombre", type="string", length=150, nullable=false)
*/
private $nombre;
/**
*
* #ORM\OneToMany(targetEntity="LlamadaDerivado", mappedBy="llamada")
*/
private $derivados;
function __construct() {
$this->derivados = new ArrayCollection();
}
function getId() {
return $this->id;
}
function getNombre() {
return $this->nombre;
}
function setId($id) {
$this->id = $id;
}
function setNombre($nombre) {
$this->nombre = $nombre;
}
function getDerivados(){
return $this->derivados;
}
function setDerivados($derivados){
$this->derivados = $derivados;
}
}
Then inside LlamadaDerivado Entity we have this
<?php
namespace xxxBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* LlamadaDerivado
*
* #ORM\Table(name="llamada_derivado")
* #ORM\Entity
*/
class LlamadaDerivado
{
/**
* #var \AgendaBundle\Entity\Llamada
*
* #ORM\ManyToOne(targetEntity="AgendaBundle\Entity\Llamada",inversedBy="derivados",cascade={"persist"})
* #ORM\Id
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_llamada", referencedColumnName="id")
* })
*/
private $llamada;
/**
* #var \AgendaBundle\Entity\PersonaDerivado
*
* #ORM\ManyToOne(targetEntity="AgendaBundle\Entity\PersonaDerivado",inversedBy="llamadas",cascade={"persist"})
* #ORM\Id
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_derivado", referencedColumnName="id")
* })
*/
private $derivado;
/**
* #var DateTime
*
* #ORM\Column(name="fecha_derivacion", type="date", nullable=false)
*/
private $fechaDerivacion;
function getLlamada(){
return $this->llamada;
}
function getDerivado(){
return $this->derivado;
}
function getFechaDerivacion() {
return $this->fechaDerivacion;
}
function setLlamada( $llamada) {
$this->llamada = $llamada;
}
function setDerivado( $derivado) {
$this->derivado = $derivado;
}
function setFechaDerivacion($fechaDerivacion) {
$this->fechaDerivacion = $fechaDerivacion;
}
}
And inside PersonaDerivado entity
<?php
namespace xxxBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* ReunionLugar
*
* #ORM\Table(name="persona_derivado")
* #ORM\Entity
*/
class PersonaDerivado
{
public function __construct() {
$this->llamadas = new ArrayCollection();
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="reunion_lugar_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nombre", type="string", length=150, nullable=false)
*/
private $nombre;
/**
* #ORM\OneToMany(targetEntity="LlamadaDerivado", mappedBy="derivado")
*/
private $llamadas;
function getId() {
return $this->id;
}
function getNombre() {
return $this->nombre;
}
function setId($id) {
$this->id = $id;
}
function setNombre($nombre) {
$this->nombre = $nombre;
}
function setLlamadas($llamadas) {
$this->llamadas = $llamadas;
}
}
And the LlamadaType is
class LlamadaDto extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$disabled = $options['disabled'];
$builder
->add('id', HiddenType::class)
->add('nombre', TextType::class, array(
'disabled' => $disabled,
'attr' => ['maxlength'=>'50']
))->add('apellido', TextType::class, array(
'disabled' => $disabled,
'attr' => ['maxlength'=>'50']
))->add('fecha', DateType::class, array(
'format' => 'dd/MM/yyyy',
'disabled' => $disabled,
'widget' => 'single_text',
'attr' => ['class' => 'datepicker']
))->add('hora', TimeType::class, array(
'disabled' => $disabled
))->add('motivo', TextareaType::class, array(
'disabled' => $disabled,
'attr' => ['maxlength'=>'400']
))->add('telefonoContacto', TextType::class, array(
'disabled' => $disabled,
'attr' => ['maxlength'=>'9']
))->add('derivados', EntityType::class, array(
'class' => 'AgendaBundle:PersonaDerivado',
'choice_label' => 'apellidoNombre',
'placeholder' => 'Seleccionar un derivado',
'multiple' => true,
));
}
public function configureOptions(OptionsResolver$resolver) {
$resolver->setDefaults(array('data_class' => Llamada::class));
}
}
Inside the controller we have this code
<?php
/**
* #Route("/Llamada/save",name="saveLlamada")
*/
public function saveLlamadaAction(Request $request) {
$llamadaService = $this->get('llamadaService');
$derivadoService = $this->get('derivadoService');
$form = $this->createForm(LlamadaDto::class);
$form->handleRequest($request);
$editar = TRUE;
$llamada = $form->getData();
$derivados = $request->request->get("llamada_dto")["derivados"];
$derivadosActuales = $derivadoService->getLlamadaDerivados($llamada->getId());
foreach ($derivados as $key1 => $d) {
foreach ($derivadosActuales as $key2 => $da) {
if($da->getDerivado()->getId()==$d){
array_splice($derivados, array_search($d, $derivados),1);
}
}
}
if ($llamadaService->saveLlamada($llamada)) {
$this->addFlash(
'exitoLlamada', 'Datos de llamada guardados exitosamente'
);
$derivadoService->saveDerivados($derivados,$llamada);
} else {
$this->addFlash(
'errorLlamada', 'Disculpe, hubo un error en el registro de la llamada'
);
}
return new RedirectResponse($this->generateUrl('listaLlamadas', array(), UrlGeneratorInterface::RELATIVE_PATH));
}
And the services called are this ones:
public function saveLlamada($llamada ){
try{
if($llamada->getId()){
$this->em->merge($llamada);
}else{
$this->em->persist($llamada);
}
$this->em->flush();
return TRUE;
} catch (Exception $ex){
return FALSE;
}
}
public function saveDerivados($derivados,$llamada){
foreach ($derivados as $key => $derivado) {
$llamadaDerivado = new LlamadaDerivado();
$personaLlamada = $this->getDerivado($derivado);
$llamadaDerivado->setLlamada($llamada);
$llamadaDerivado->setDerivado($personaLlamada);
$llamadaDerivado->setFechaDerivacion(new \DateTime('now', (new \DateTimeZone('America/Argentina/Ushuaia'))));
$this->em->persist($llamadaDerivado);
$this->em->flush();
}
}
This is the error that we are getting:
Uncaught PHP Exception Doctrine\ORM\ORMInvalidArgumentException: "Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "xxxBundle\Entity\Llamada#$derivados", got "xxxBundle\Entity\PersonaDerivado" instead." at project\vendor\doctrine\orm\lib\Doctrine\ORM\ORMInvalidArgumentException.php line 206
We've been 1 week with this.
Many thanks in advance
You can try to use a CollectionType instead of EntityType in your formtype, although I have a piece of code in front of me that works just fine with EntityType and the multiple flag for a OneToMany relationship.
look at Llamada entity, $derivados is an array collection of LlamadaDerivado and in your LlamadaType you make it entityType of AgendaBundle:PersonaDerivado, that is why you got this error. Think to use Collection Type to add everytime all the object LlamadaDerivado to well respect the mapping.
i trying to make admin page for Product which has relationship 1:1 with image.
Product
/**
* #ORM\Entity
* #ORM\Table(name="products")
class Product
{
/**
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #ORM\Id
* #var int
*/
private $id = 0;
/**
* #ORM\OneToOne(targetEntity="Image", mappedBy="product")
*/
private $image;
/**
* #return Image
*/
public function getImage(): ?Image
{
return $this->image;
}
/**
* #param Image $image
*/
public function setImage(Image $image)
{
$this->image = $image;
return $this;
}
}
Image
/**
* #ORM\Entity
* #ORM\Table(name="images")
*/
class Image
{
/**
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #ORM\Id
* #var int
*/
private $id = 0;
/**
* #ORM\OneToOne(targetEntity="Product", inversedBy="image")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
/**
* #return mixed
*/
public function getProduct()
{
return $this->product;
}
public function setProduct(Product $product)
{
$this->product = $product;
}
}
ProductAdmin
class ProductAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('image', 'sonata_type_admin', array('label' => 'Okładka', 'by_reference' => false,));
}
ImageAdmin
class ImageAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('file', 'file', array('label' => 'Okładka', 'required' => false))
->add('path', 'text', array('label' => 'Scieżka do pliku', 'required' => false));
}
I setuped services correctly, but i can't edit product and after saving new one i geting error
unable to find the object with id : 0
Try to not initialize your $id
private $id = 0; // =====> this is a private $id;
You have several mistakes. Let's try to correct your code.
Just follow the tutorials and put right annotation for $id:
/**
* #var integer $id
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
Hope, that this is just a typo with "?Image":
/**
* #return Image
*/
public function getImage() : Image
{
return $this->image;
}
And finally.
/**
* Class ProductAdmin
*/
class ProductAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('image', 'sonata_type_model_list', [
'btn_add' => true, //Or you can specify a custom label
'btn_list' => 'list button!', //which will be translated
'btn_delete' => false, //or hide the button.
'btn_catalogue' => 'messages', //Custom translation domain for buttons
'label' => 'My image',
], [
'placeholder' => $this->trans('messages.no_images_message'),
'edit' => 'standard',
'inline' => 'standard',
'sortable' => 'id',
])
;
}
}
I have to entities Project and Image. They are linked by OneToOne relation.
Here is the property definition in the project:
/**
* #Exclude
* #ORM\OneToOne(targetEntity="SensoBundle\Entity\Image", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $image;
Here is the image entity
<?php
namespace SensoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Image
*
* #ORM\Table(name="image")
* #ORM\HasLifecycleCallbacks()
* #ORM\Entity(repositoryClass="SensoBundle\Repository\ImageRepository")
*/
class Image
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="fileName", type="string", length=255)
*/
private $fileName;
/**
* #var string
*
* #ORM\Column(name="parentType", type="string", length=255, nullable=true)
*/
private $parentType;
private $tempFilename;
private $file;
private $fileNamePrefix;
private $fileExtension;
private $hashForName;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set fileName
*
* #param string $fileName
* #return Image
*/
public function setFileName($fileName)
{
$this->fileName = $fileName;
return $this;
}
/**
* Get fileName
*
* #return string
*/
public function getFileName()
{
return $this->fileName;
}
public function getFile()
{
return $this->file;
}
public function setFile($file = null)
{
$this->file = $file;
if (null !== $this->fileName) {
$this->tempFilename = $this->fileName;
$this->fileName = null;
}
}
public function getUploadDir()
{
return '/uploads/img';
}
public function getUploadRootDir()
{
return __DIR__ . '/../../../web' . $this->getUploadDir();
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null === $this->file) {
return;
}
if (null !== $this->fileName) {
$this->tempFilename = $this->fileName;
}
$this->fileExtension = $this->file->guessExtension();
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
if (null !== $this->tempFilename) {
$oldFile = $this->getUploadRootDir() . '/' . $this->tempFilename;
if (file_exists($oldFile)) {
unlink($oldFile);
}
}
$this->file->move(
$this->getUploadRootDir(),
$this->fileName
);
}
/**
* #ORM\PreRemove()
*/
public function preRemoveUpload()
{
$this->tempFilename = $this->getUploadRootDir() . '/' . $this->fileName;
}
/**
* #ORM\PostRemove()
*/
public function removeUplaod()
{
if (file_exists($this->tempFilename)) {
unlink($this->tempFilename);
}
}
public function setFileNamePrefix($prefix)
{
$this->fileNamePrefix = $prefix;
}
public function setHashForname($hash)
{
$this->hashForName = $hash;
}
/**
* Set parentType
*
* #param string $parentType
* #return Image
*/
public function setParentType($parentType)
{
$this->parentType = $parentType;
return $this;
}
/**
* Get parentType
*
* #return string
*/
public function getParentType()
{
return $this->parentType;
}
public function getFileExtension() {
return $this->fileExtension;
}
public function getFilenamePrefix() {
return $this->fileNamePrefix;
}
}
Here is the part of controller which is concerned :
/**
* #Route("/project/{id}/presentation/edit", name="project_presentation_edit")
* #Method({"GET", "POST"})
*/
public function editPresentationAction(Request $request, $id)
{
$entity = $this->get('creasenso.project_repo')->find($id);
if (!$entity) {
throw $this->createNotFoundException('The project does not exist');
}
$this->denyAccessUnlessGranted('editProject', $entity);
$form = $this->createForm('CreasensoBundle\Form\Project\ProjectPresentationType', $entity);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
$this->get('session')->getFlashBag()->add('success', 'project.presentation.feedback');
$redirectRoute = ($request->request->get('nextSubmit')) ? 'project_details_edit' : 'project_presentation_edit';
return $this->redirectToRoute($redirectRoute, array('id' => $id));
}
return $this->render('CreasensoBundle:project:editPresentation.html.twig', array(
'form' => $form->createView(),
'entity' => $entity
));
}
Here is the form called in the controller above
<?php
// src/Infinite/InvoiceBundle/Form/Type/InvoiceType.php
namespace CreasensoBundle\Form\Project;
use Infinite\FormBundle\Form\Type\PolyCollectionType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProjectPresentationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('image', 'SensoBundle\Form\Image\ImageSimpleEditType', array(
'label' => 'Cover',
'attr' => array(
'help_text' => 'project.cover.help'
),
'display_image_format' => 'projectCoverSmall',
'imagesConstraints' => array(
'minWidth' => 300
)
))
->add('trans_name_fr', 'SensoBundle\Form\Type\FieldTranslationType', array(
'mapped' => false,
'cascade_validation' => true,
'fieldName' => 'name',
'fieldLocale' => 'fr',
'fieldOptions' => array(
'label' => 'French name',
'required' => true
)
))
->add('trans_name', 'SensoBundle\Form\Type\FieldTranslationType', array(
'mapped' => false,
'cascade_validation' => true,
'fieldName' => 'name',
'fieldLocale' => 'en',
'fieldOptions' => array(
'label' => 'English name',
'required' => true
)
))
;
}
public function getBlockPrefix()
{
return 'projectContent';
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'CreasensoBundle\Entity\Project'
));
}
}
Here is my generic Image Entity Field
<?php
namespace SensoBundle\Form\Image;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Validator\Constraints\Image as ImageValidator;
use SensoBundle\Entity\Image;
class ImageSimpleEditType extends AbstractType
{
private $imageUrl = null;
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::POST_SET_DATA, function ($event) use ($builder, $options) {
$form = $event->getForm();
$imageFieldParams = array(
'label' => false,
'display_image_format' => $options['display_image_format'],
'required' => false
);
if (!empty($options['imagesConstraints'])) {
// Image format
$constraints = $options['imagesConstraints'];
$validatorParams = array();
if (!empty($constraints['format'])) {
switch ($constraints['format']) {
case 'square':
$squareErrMsg = "image.edit.squareError";
$validatorParams['allowSquare'] = true;
$validatorParams['allowLandscape'] = false;
$validatorParams['allowPortrait'] = false;
$validatorParams['allowLandscapeMessage'] = $squareErrMsg;
$validatorParams['allowPortraitMessage'] = $squareErrMsg;
break;
case 'landscape':
$landscapeErrMsg = "image.edit.landscapeError";
$validatorParams['allowSquare'] = false;
$validatorParams['allowLandscape'] = true;
$validatorParams['allowPortrait'] = false;
$validatorParams['allowLandscapeMessage'] = $landscapeErrMsg;
$validatorParams['allowPortraitMessage'] = $landscapeErrMsg;
break;
case 'portrait':
$portraitErrMsg = "image.edit.portraitError";
$validatorParams['allowSquare'] = false;
$validatorParams['allowLandscape'] = false;
$validatorParams['allowPortrait'] = true;
$validatorParams['allowLandscapeMessage'] = $portraitErrMsg;
$validatorParams['allowPortraitMessage'] = $portraitErrMsg;
break;
}
// Min width
if (!empty($constraints['minWidth'])) {
$validatorParams['minWidth'] = $constraints['minWidth'];
}
}
$imageFieldParams['constraints'] = new ImageValidator($validatorParams);
}
$form->add('file', 'SensoBundle\Form\Image\ImageInputType', $imageFieldParams);
});
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SensoBundle\Entity\Image',
'display_image_format' => 'original',
'imagesConstraints' => null
));
}
public function getBlockPrefix()
{
return 'sensoImage';
}
}
And finally, here is my generic image file input (used in the image entity form)
<?php
namespace SensoBundle\Form\Image;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use SensoBundle\Entity\Image;
use Symfony\Component\Validator\Constraints\Image as ImageValidator;
class ImageInputType extends AbstractType
{
private $imageManager;
public function __construct($imageManager)
{
$this->imageManager = $imageManager;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
}
//
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['image_url'] = null;
$imageEntity = $form->getParent()->getData();
if ($imageEntity) {
$this->imageManager->setEntity($imageEntity);
$view->vars['image_url'] = $this->imageManager->getRelativeUrl();
}
$view->vars['display_image_format'] = $options['display_image_format'];
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'display_image_format' => 'original',
'format' => null
));
}
public function getBlockPrefix()
{
return 'sensoImageInput';
}
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\FileType';
}
}
My form structure is like that
-- Project form (class ProjectPresentationType)
---- Image entity field (class ImageSimpleEditType)
------ Image input (class ImageInputType)
My problem is an image entity is created even if no file is uploaded.
What I would like is to be able to create a new project with this "features":
Image upload is not required
If image field is empty, don't create an Image entity
Thank you for your help.
I have this Entities:
<?php
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use ProductBundle\DBAL\Types\StatusType;
use ProductBundle\DBAL\Types\FieldType;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="product_detail")
*/
class ProductDetail {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="ProductDetail")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #ORM\Column(type="string", length=255)
*/
protected $label;
/**
* #var string $field_type
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\FieldType")
* #ORM\Column(name="field_type", type="FieldType", nullable=false)
*/
protected $field_type;
/**
* #ORM\Column(name="values_text", type="string")
*/
protected $values_text;
/**
* #ORM\Column(type="string", length=255)
*/
protected $measure_unit;
/**
* #var string $status
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\StatusType")
* #ORM\Column(name="status", type="StatusType", nullable=false)
*/
protected $status;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail")
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail")
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="group", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $detail_group;
public function __construct() {
$this->detail_group = new \Doctrine\Common\Collections\ArrayCollection();
$this->category = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(ProductDetail $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setLabel($label) {
$this->label = $label;
}
public function getLabel() {
return $this->label;
}
public function setFieldType($field_type) {
$this->field_type = $field_type;
}
public function getFieldType() {
return $this->field_type;
}
public function setValuesText($values_text) {
$this->values_text = $values_text;
}
public function getValuesText() {
return $this->values_text;
}
public function setMeasureUnit($measure_unit) {
$this->measure_unit = $measure_unit;
}
public function getMeasureUnit() {
return $this->measure_unit;
}
public function setStatus($status) {
$this->status = $status;
}
public function getStatus() {
return $this->status;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setCategory(Category $category) {
$this->category[] = $category;
}
public function getCategory() {
return $this->category;
}
public function setDetailGroup(DetailGroup $detailGroup) {
$this->detail_group[] = $detailGroup;
}
public function getDetailGroup() {
return $this->detail_group;
}
}
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="detail_group")
*/
class DetailGroup {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="DetailGroup")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\ProductDetail", mappedBy="detail_group", cascade={"all"})
*/
protected $productDetail;
public function __construct() {
$this->productDetail = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(DetailGroup $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setProductDetail(ProductDetail $productDetail) {
$this->productDetail[] = $productDetail;
}
public function getProductDetail() {
return $this->productDetail;
}
}
This is my ProductDetailType.php form:
<?php
namespace ProductBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProductDetailType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('category', 'entity', array('class' => 'CategoryBundle:Category', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('detail_group', 'entity', array('class' => 'ProductBundle:DetailGroup', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('parent', 'entity', array('class' => 'ProductBundle:ProductDetail', 'property' => 'label', 'required' => false))
->add('description', 'text', array('required' => false))
->add('label')
->add('field_type', 'choice', ['choices' => \ProductBundle\DBAL\Types\FieldType::getChoices()])
->add('values_text', 'collection', array('type' => 'text', 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false))
->add('measure_unit', 'text', array('required' => false))
->add('status', 'choice', ['choices' => \ProductBundle\DBAL\Types\StatusType::getChoices()]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'ProductBundle\Entity\ProductDetail'
));
}
public function getName() {
return 'prod_detail_create';
}
}
And this is my createAction():
/**
* Handle product details creation
*
* #Route("/product_detail/create", name="product_detail_create")
* #Method("POST")
* #Template("ProductBundle:ProductDetail:new.html.twig")
*/
public function createAction(Request $request) {
$entity = new ProductDetail();
$form = $this->createForm(new ProductDetailType(), $entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity->setValuesText(serialize($form->get('values_text')->getData()));
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('product_detail_list'));
}
return $this->render('ProductBundle:ProductDetail:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
When I try to insert new records I get this errors:
An exception occurred while executing 'INSERT INTO
product_detail_has_category (category, detail) VALUES (?, ?)' with
params [7, 2]:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(product_detail_has_category, CONSTRAINT
fk_product_detail_has_category_product_detail1 FOREIGN KEY
(detail) REFERENCES product_detail (id) ON UPDATE CASCADE)
I check the logs and see this:
DEBUG - "START TRANSACTION"
DEBUG - INSERT INTO product_detail (description, label, field_type, values_text, measure_unit, status, created, modified, parent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
DEBUG - INSERT INTO product_detail_has_category (category, detail) VALUES (?, ?)
DEBUG - "ROLLBACK"
I don't know why it doesn't insert product_detail and the tries to create the relation, what is wrong?
You can either:
set on $category field set cascade={"PERSIST"} in #ManyToMany.
This requires no chnages to controller itself but it depends on whether you like defining cascades.
persist Category object manually before persisting ProductDetail.
$em = ...; // your entity manager
// first, manually persist any category that is found
foreach ( $productDetail->getCategory() as $c ){
$em->persist($c);
}
// then, persist top-level object
$em->persist($productDetail);
// finally, flush everything
$em->flush();
Hope this helps a bit...
I've found where the errors was, see the changes below in annotations:
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail", cascade={"persist"})
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail", cascade={"persist"})
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="kgroup", referencedColumnName="id")}
* )
*/
protected $detail_group;
I've inverted the relations in joinColumns and inversedJoinColumns and all works fine, also has to rename group to kgroup due to MySQL query errors!!