Reputation: 1523
I have these entities with one to many relationships:
freightOrder has many shipments
shipment has many shipmentLines
The freightOrder entity looks like this:
<?php
namespace Fraktportalen\Bundle\ShipmentBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
* @ExclusionPolicy("all")
*/
class FreightOrder
{
/**
* @Expose
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @Expose
*
* @ORM\OneToMany(
* targetEntity="Fraktportalen\Bundle\ShipmentBundle\Entity\Shipment",
* mappedBy="freightOrder",
* cascade={"persist","remove"}
* )
*/
private $shipments;
/**
* @Expose
*
* @ORM\ManyToOne(targetEntity="Fraktportalen\Bundle\AccountBundle\Entity\Account")
* @ORM\JoinColumn(name="freighter_id", referencedColumnName="id")
*/
private $freighter;
/**
* Order constructor.
*/
public function __construct()
{
$this->shipments = new ArrayCollection();
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @return mixed
*/
public function getShipments()
{
return $this->shipments;
}
/**
* @param mixed $shipment
*/
public function addShipment(Shipment $shipment)
{
$shipment->setFreightOrder($this);
$this->shipments->add($shipment);
}
public function removeShipment(Shipment $shipment)
{
$this->shipments->removeElement($shipment);
}
/**
* @return mixed
*/
public function getFreighter()
{
return $this->freighter;
}
/**
* @param mixed $freighter
*/
public function setFreighter($freighter)
{
$this->freighter = $freighter;
}
}
The shipment entity:
<?php
namespace Fraktportalen\Bundle\ShipmentBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Fraktportalen\Bundle\AccountBundle\Entity\Account;
use Fraktportalen\Bundle\AddressBundle\Entity\Address;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
* @ExclusionPolicy("all")
*/
class Shipment
{
/**
* @Expose
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @Expose
*
* @ORM\Column(type="datetime", nullable=true)
*/
private $pickup;
/**
* @Expose
*
* @ORM\Column(type="datetime", nullable=true)
*/
private $delivery;
/**
* @Expose
*
* @ORM\OneToMany(
* targetEntity="Fraktportalen\Bundle\ShipmentBundle\Entity\ShipmentLine",
* mappedBy="shipment",
* fetch="EAGER",
* cascade={"persist"}
* )
*/
private $shipmentLines;
/**
* @Expose
*
* @ORM\ManyToOne(targetEntity="Fraktportalen\Bundle\ShipmentBundle\Entity\FreightOrder", inversedBy="shipments")
* @ORM\JoinColumn(name="freight_order_id", referencedColumnName="id", nullable=false)
*/
private $freightOrder;
/**
* @Expose
*
* @ORM\ManyToOne(targetEntity="Fraktportalen\Bundle\AccountBundle\Entity\Account")
* @ORM\JoinColumn(name="sender_id", referencedColumnName="id")
*/
private $sender;
/**
* @Expose
*
* @ORM\ManyToOne(targetEntity="Fraktportalen\Bundle\AccountBundle\Entity\Account")
* @ORM\JoinColumn(name="receiver_id", referencedColumnName="id")
*/
private $receiver;
/**
* Shipment constructor.
*/
public function __construct()
{
$this->shipmentLines = new ArrayCollection();
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @return mixed
*/
public function getFreightOrder() {
return $this->freightOrder;
}
/**
* @param mixed $freightOrder
*
* @return Shipment
*/
public function setFreightOrder($freightOrder) {
$this->freightOrder = $freightOrder;
$freightOrder->addShipment($this);
return $this;
}
/**
* @return mixed
*/
public function getPickup()
{
return $this->pickup;
}
/**
* @param mixed $pickup
*/
public function setPickup(\DateTime $pickup = null)
{
$this->pickup = $pickup;
}
/**
* @return mixed
*/
public function getDelivery()
{
return $this->delivery;
}
/**
* @param mixed $delivery
*/
public function setDelivery(\DateTime $delivery = null)
{
$this->delivery = $delivery;
}
/**
* @return mixed
*/
public function getShipmentLines()
{
return $this->shipmentLines;
}
/**
* @param mixed $shipmentLine
*/
public function addShipmentLine(ShipmentLine $shipmentLine)
{
$shipmentLine->setShipment($this);
$this->shipmentLines->add($shipmentLine);
}
public function removeShipmentLine(ShipmentLine $shipmentLine)
{
$this->shipmentLines->removeElement($shipmentLine);
}
/**
* @return mixed
*/
public function getSender()
{
return $this->sender;
}
/**
* @param mixed $sender
*/
public function setSender(Account $sender)
{
$this->sender = $sender;
}
/**
* @return mixed
*/
public function getReceiver()
{
return $this->receiver;
}
/**
* @param mixed $receiver
*/
public function setReceiver(Account $receiver)
{
$this->receiver = $receiver;
}
}
The freightOrderType next:
<?php
namespace Fraktportalen\Bundle\ShipmentBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FreightOrderType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('freighter', EntityType::class, array(
'class' => 'AccountBundle:Account',
))
->add('shipments', CollectionType::class, array(
'entry_type' => ShipmentType::class,
'allow_add' => true
))
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Fraktportalen\Bundle\ShipmentBundle\Entity\FreightOrder',
'csrf_protection' => false
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'freightOrder';
}
}
When saving a freightOrder from an Ember app, it sends this json:
{
"freightOrder": {
"freighter": "3",
"shipments": [{
"pickup": "2017-03-22 12:32:00",
"delivery": "2017-03-23 12:32:00",
"sender": "1",
"receiver": "2",
"shipment_lines": [{
"package_weight": 45,
"package_length": 240,
"package_width": 120,
"package_height": 240,
"package_type": "3"
}]
}]
}
}
Everything works as expected except that freightOrder on shipments is not saved. Even though I add it on the freightOrder entity addShipment
function:
/**
* @param mixed $shipment
*/
public function addShipment(Shipment $shipment)
{
$shipment->setFreightOrder($this);
$this->shipments->add($shipment);
}
As you can see in the shipment entity, I have even tried to add shipment to freightOrder there. But nothing works.
Shipment lines are added to shipment though, and that is also a one to many relationship.
Will I have to add a form event listener to the freightOrderType and manually add freightOrder to all shipments? I was under the impression that adding freightOrder to shipments when adding said shipments to freightOrder would solve it. At least that is what I have found when searching SO.
Thanks for your time, Tommy
Upvotes: 2
Views: 675
Reputation: 4880
I feel your pain, I fight with this problem quite often.
It sounds like the setter isnt getting called. You can sometimes fix this by declaring by_reference to false.
try this..
$builder
->add('freighter', EntityType::class, array(
'class' => 'AccountBundle:Account',
))
->add('shipments', CollectionType::class, array(
'entry_type' => ShipmentType::class,
'allow_add' => true,
'by_reference' => false,
))
;
reading the docs:
Similarly, if you're using the CollectionType field where your underlying collection data is an object (like with Doctrine's ArrayCollection), then by_reference must be set to false if you need the adder and remover (e.g. addAuthor() and removeAuthor()) to be called.
Personally, I still think by_reference
is some kind of hacky fix for a bug they had once.. I guess we'll never know..
Upvotes: 4