Doctrine 2 OneToOne Unidirectional doesn't work - php

I have the following OneToOne relation inside MyEntity:
/**
* #ORM\OneToOne(targetEntity="StatusHistory")
* #ORM\JoinColumn(name="lastest_status_id", referencedColumnName="id")
*/
protected $lastestStatus;
The entity StatusHistory works. The migrations:diff command creates the files prefectly, and the database have the correct column.
The problem is, when I do die("c: " . $this->lastestStatus) inside MyEntity (trying to debug a getter), it returns Namespace\MyEntity rather than Namespace\StatusHistory.
What am I doing wrong?

Turns out I forgot to add the ->join('myEntity.lastestStatus', 'lastestStatus') clause on the query builder...

Related

Doctrine prevent object deletion

In my project I have two entities: planifications and selections.
There is a relation between these two objects: A planification MUST contain ONE selection. The same selection can be used by multiple planifications.
The generated code looks like this:
// Planification.php - class Planification
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Selection", inversedBy="planifications")
* #ORM\JoinColumn(name="selection_id", referencedColumnName="id")
*/
private $selection;
// Selection.php - class Selection
/**
* #ORM\OneToMany(targetEntity="App\Entity\Planification", mappedBy="selection")
*/
private $planifications;
What I would like to do is not allow a selection to be deleted if it is referenced by a planification. In other words, if a planification contains a selection - that selection can not be deleted. What happens to me is if I try to delete a selection that is in a planification, the operation completes successfully, and the $selection member in the Planification class contains NULL.
Would fixing this be possible in doctrine? I have tried adding nullable=false (on the $selection member) and onDelete="NO ACTION", and both solutions don't work.
The correct Doctrine annotation to disallow Planification::$selection to be null, would be:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Selection", inversedBy="planifications")
* #ORM\JoinColumn(name="selection_id", nullable=false)
*/
private $selection;
(You do not need the referencedColumnName setting, since it defaults to id, and nullable=false goes in the #JoinColumn annotation).
Having the annotation will not update the DB to fit this particular definition.
Execute bin/console doctrine:schema:update --dump-sql to see the needed SQL to update your table definition, and run the resultant appropriate SQL statements against your DB to update the DB schema.

Doctrine merging entity with unidirectional OneToMany not clearing database entries

First off, I use Doctrine v2.6.2 with Symfony v4.1.7.
I have an entity Product which (among others) has a unidirectional one-to-many relation with another entity AlternativeDuration. Following Doctrine's documentation, the mapping in my product class looks like this:
/**
* #ORM\ManyToMany(
* targetEntity="AlternativeDuration",
* cascade={"persist", "merge", "remove"},
* orphanRemoval=true
* )
* #ORM\JoinTable(name="ProductAlternativeDurations",
* joinColumns={#ORM\JoinColumn(name="product_id", referencedColumnName="id", onDelete="CASCADE", nullable=false)},
* inverseJoinColumns={#ORM\JoinColumn(name="alternative_duration_id", referencedColumnName="id", unique=true, onDelete="CASCADE", nullable=false)}
* )
*/
protected $alternativeDurations;
My application recently started using React, this means I now submit a JSON representation of my product (along with an array of alternative durations) which I need to deserialize into the Product entity in the back-end. I use the JMS serializer with default configuration for this.
Now the problem I'm having happens when editing an existing product, the product already has an alternative duration which I delete. The submitted JSON looks like this:
{
"id": 1, # the ID of the existing product here
"alternativeDurations": [] # empty array because the entry is removed
}
In the back-end I successfully deserialize the JSON string:
$serializedProduct = $this->serializer->deserialize($jsonString, Product::class, 'json');
I verified here that the $serializedProduct has no alternative durations. Then I follow with a merge + flush. I expect the merge to fetch the existing product and supplement it with the $serializedProduct.
$em->merge($serializedProduct); # $em being the EntityManager.
$em->flush();
Now I would expect the AlternativeDuration entry, along with the ProductAlternativeDurations join table entry being removed. The result, however, is that the entry in ProductAlternativeDurations is removed but the AlternativeDuration is still there.
I'm at a loss now, anyone can give some pointers on why the AlternativeDuration entry is not deleted?
EDIT 19-11-2018:
It seems this is a known bug in Doctrine: #2542
Also merge will be removed in Doctrine3 so I will probably need to rethink this approach in general.

Doctrine get confused when generating plural method of a field that ends with a letter "s"

I have an rntity called Account, which can have many phone numbers (or Dnis, as I have named the related Entity ).
The definition for Account using yml is:
models\Account:
type: entity
table: account
oneToMany:
dnis:
targetEntity: models\Dnis
mappedBy: account
The problem is when I generate the entities classes with the following command:
doctrine orm:generate:entities
Since it is a OneToMany relation, the Account entity has a dnis collection, the problem is that the "add" method gets named as "addDni".
/**
* Add dni
*
* #param \application\models\Dnis $dni
*
* #return CreditAccount
*/
public function addDni(\application\models\Dnis $dni)
{
$this->dnis[] = $dni;
return $this;
}
/**
* Remove dni
*
* #param \application\models\Dnis $dni
*/
public function removeDni(\application\models\Dnis $dni)
{
$this->dnis->removeElement($dni);
}
I guess doctrine get confused because it thinks that the property "dnis" is a plural just because ends with a letter "s".
How can I let doctrine know that "dnis" is the actual name of the property? Or am I missing something here in the entity definition?
Thanks in advance.
You are defining a OneToMany association.
That means that one Account can have many Dnis.
This is why Dnis is considered as plural, because it represents the Many side of the OneToMany association, also it's normal that doctrine generates a addDni method that add a given Dnis to the collection of Dnis, same for the removeDni that remove a given Dnis and for the getDnis that fetches the whole collection of Dnis.
If you need that an account can have one Dnis, define it as OneToOne and keep it as plural. You'll have a getDnis() and setDnis().
If you doesn't like the name of your variables (i.e. Dnis $dni), just change it (i.e. Dnis $dnis).
EDIT
I found a quick way to avoid this unexpected behavior and I submitted a pull request.
I'll let you know in case of the PR is merged, for now you can use my fix.

ManyToOne relationship + How to use in both directions

I have 2 tables like this:
Users
- UserID
- Username
- Password
- ...
Players
- playerID
- playerName
- ...
- User
The relation is ManyToOne (see the picture) and it's not required.
I've generated my entities automatically with Doctrine. In my player Entity I have:
/**
* #var \NV\VolleyScoutBundle\Entity\Users
*
* #ORM\ManyToOne(targetEntity="NV\VolleyScoutBundle\Entity\Users")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
* })
*/
protected $user;
But I don't have a $player variable in my Users Entity. In what way can I add this? Tried to do this but gave me different errors. What I'm trying to do is add a player form to my register form.
So in my RegisterType (=form) I woud like to add ->add('player', new PlayerType()). But that's not possible without a $player variable in my Users entity.
What type of relation do I need to setting for $player in my Users entity?
You have to add annotation in user entity.
In Player Enity
#ORM\OneToMany(targetEntity="Players", mappedBy="user")
protected $player;
In User Entity:
#ORM\ManyToOne(targetEntity="Users", inversedBy="player")
#ORM\JoinColumn(name="user_id", referendecColumn="UserId")
proteced $user;
This is only a draft. You have to write full namespaces for target entities, and correct errors if there are some.
A lot of setails you can find here: Doctrine documentation - working with association
Try to use small caps for table names, because MySQL under linux doesn't like uppercaps.
$player must be instance of ArrayCollection

Doctrine2 Check if related entity exists

I'm really tired to figuring out how I can check in Doctrine 2 if related entity record exists in DB. Help me please.
For example I have two entities. One is the order status of certain delivery company. Another one is order.
Order.php
/**
* #ORM\OneToOne(targetEntity="Application\DeliveryBundle\Entity\DpdOrderStatus", mappedBy="order")
* #var DpdOrderStatus
*/
$dpdOrderStatus;
DpdOrderStatus.php
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="\Application\FrontendBundle\Entity\Order", inversedBy="dpdOrderStatus")
* #ORM\JoinColumn(onDelete="CASCADE")
* #var Order
*/
$order;
Order entity sometimes doesn't have status and I need to check if it has.
AFAIK if I will try to use is_null($order->getDpdOrderStatus()) it will always be false because Doctrine always create Proxy objects for its entities if EAGER mode is not specified.
So what is the most proper way to check if my status entity exists in database?
Add a method that checks if the order has an order status:
Order.php
public function hasOrderStatus(){
return ! is_null($this->dpdOrderStatus);
}
More information: Techniques to check if relationship exists in Doctrine2
This worked for me.
public function hasOrderStatus() {
return !is_null($this->dpdOrderStatus) && (bool) $this->dpdOrderStatus->getId();
}

Categories