I have a Question entity with a property called code. I want to set the value of code equal to id on default with Doctrine annotations.
This is how I tried but I'm getting an error:
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $code = $id;
Or:
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer", options = {"default": $id})
*/
private $code;
Thanks.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html
I don't know what is your need but maybe postPersist event is what you want :
postPersist - The postPersist event occurs for an entity after the
entity has been made persistent. It will be invoked after the database
insert operations. Generated primary key values are available in the
postPersist event.
Then in your entity :
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $code;
/** #PostPersist */
public function doStuffOnPostPersist()
{
$this->code = $this->id;
}
It is important to understand that your doctrine entity will have an ID only after having been persisted, thus after the post persist event.
Related
I'm building a simple web-service using Symfony 3, Doctrine 2.5 and stuck at ORM relations described below in simplified structure.
I have an Action entity containing many actions with ManyToOne relation...
class Action
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Status")
* #ORM\JoinColumn(referencedColumnName="code", nullable=false)
*/
private $status;
and the Status Entity with a few statuses.
class Status
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="integer", unique=true)
*/
private $code;
I cannot get proper way to set referencedColumnName="code" column (not 'Id' as usual) for Action entity.
Configured this way repo throws wxception at persist moment with "Notice: Undefined index: code";
I guess that it is mappedBy or inversedBy annotation parameter... but can't figure out "how".
Unfortunately it's not supported in Doctrine (reference).
You may edit your Status entity like this (ensure that code is set before persist):
class Status
{
/**
* #ORM\Column(name="code", type="integer", unique=true)
* #ORM\Id
*/
private $code;
}
If autoincremented field is your requirement you can take a look on this answer for possible solutions.
Just thought I'd add you can still use the non-primary keys as many to many, by using the entity itself as the join table. This will work but you still need to set your relationship keys correctly.
Example:
/**
* #ORM\Entity
*/
class Car {
/**
* #var integer
*
* #ORM\Column(name="id", type="bigint", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\Column(name="registration_code", type="text", length=128, nullable=false)
* #var string
*/
public $registrationCode;
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Registration", mappedBy="Cars")
* #ORM\JoinTable(name="car",
* joinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="registration_code", referencedColumnName="registration_code")}
* )
*/
public $Registrations;
public function __construct() {
$this->Cars = new ArrayCollection();
}
}
/**
* #ORM\Entity
*/
class Registration {
/**
* #var integer
*
* #ORM\Column(name="id", type="bigint", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\Column(name="registration_code", type="text", length=128, nullable=false)
* #var string
*/
public $registrationCode;
/**
* #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Car", mappedBy="Registrations")
* #ORM\JoinTable(name="car",
* joinColumns={#ORM\JoinColumn(name="registration_code", referencedColumnName="registration_code")},
* inverseJoinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")}
* )
*/
public $Cars;
public function __construct() {
$this->Cars = new ArrayCollection();
}
}
The upside is that it works fine as a workaround.
Keep in mind a few things:
it's a collection not a single instance;
column has to be managed manually on your end;
you must set up constraints correctly (indexes, keys, etc);
check your queries still perform!
I've got a Place entity and a Distance one, like so:
class Place
{
/** #ORM\Id #ORM\Column(type="integer") #ORM\GeneratedValue(strategy="AUTO") */
private $id;
/** #ORM\Column(type="string", length=62, nullable=false) */
private $name;
/** #ORM\OneToMany(targetEntity="Distance", mappedBy="origin") */
protected $distancesTo;
/** #ORM\OneToMany(targetEntity="Distance", mappedBy="destination") */
protected $distancesFrom;
}
class Distance
{
/** #ORM\Id #ORM\Column(type="integer") #ORM\GeneratedValue(strategy="AUTO") */
private $id;
/** #ORM\ManyToOne(targetEntity="Place", inversedBy="distancesTo") */
protected $origin;
/** #ORM\ManyToOne(targetEntity="Place", inversedBy="distancesFrom") */
protected $destination;
/** #ORM\Column(type="integer") */
private $miles;
}
I want that every time a new Distance (from Place_A to Place_B) is saved, the reverse distance (from Place_B to Place_A) gets inserted too in the DB. How can I do that?
You need to create listener and listen for persisting on Distance entity. While it persists you can create new Distance with reverse route.
I have related entities. Many to Many relation and the annotation exists on only one entity:
/**
* #ORM\ManyToMany(targetEntity="Event")
* #ORM\JoinTable(name="viewed_events",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="event_id", referencedColumnName="id")}
* )
**/
protected $viewedEvents;
The problem is when I try to delete Event entity I get Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails error. How do I solve this? I tried adding orphanRemoval=true like that: #ORM\ManyToMany(targetEntity="Event", orphanRemoval=true) and also tried adding cascade="delete" and cascade="all" instead with no success.
I give you one simple example so that you can work out what you need to add/change in your application.
Assume that there is a M-N relationship between Student and Course entities.
STUDENT
class Student
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="StudentCourse", mappedBy="studentInverse", cascade={"persist", "remove"})
*/
protected $studentInverse;
public function __construct()
{
$this->studentInverse = new \Doctrine\Common\Collections\ArrayCollection();
}
}
COURSE
class Course
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="StudentCourse", mappedBy="courseMap", cascade={"persist", "remove"})
*/
protected $courseInverse;
public function __construct()
{
$this->courseInverse = new \Doctrine\Common\Collections\ArrayCollection();
}
}
STUDENTCOURSE (this the one you're more interested in)
class StudentCourse
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Course", inversedBy="courseInverse")
* #ORM\JoinColumn(name="course", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
protected $courseMap;
/**
* #ORM\ManyToOne(targetEntity="Student", inversedBy="studentInverse")
* #ORM\JoinColumn(name="student", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
protected $studentMap;
}
onDelete="CASCADE"
On Yours JoinColumn.
There are three entities: Customer, Messages, Attachments.
The relationship between these entities is straight forward: A customer can have many messages and a message can have many attachments. Both relations are "one-to-many".
I told doctrine to be lazy when loading the messages for the Customer entity. So $customer->getMessages() results in an additional SQL statement. That's fine.
But I also defined an "EAGER" loading for the attachments for the Message entity.
Now I would have expected that the messages I get by calling $customer->getMessages() are already loaded with all their attachments. But $message->getAttachments() still causes one SQL statement per message.
Is this behavior expected?
Just for reference, excepts from my classes:
Customer.php
class Customer
{
/**
* #ORM\OneToMany(targetEntity="Message", mappedBy="customer")
* #ORM\OrderBy({"createdOn" = "DESC"})
*/
private $messages;
Message.php
class Message
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="messages")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
**/
private $customer;
/**
* #ORM\OneToMany(targetEntity="Attachment", mappedBy="message", fetch="EAGER")
**/
private $attachments;
Attachment.php:
class Attachment
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Message", inversedBy="attachments")
* #ORM\JoinColumn(name="message_id", referencedColumnName="id")
**/
private $message;
It sounds like expected behavior to me. The doctrine documentation seems to imply that eager fetching is only one level deep.
According to the docs:
Whenever you query for an entity that has persistent associations and
these associations are mapped as EAGER, they will automatically be
loaded together with the entity being queried and is thus immediately
available to your application.
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
The entity being queried in your case is customer and customer has eager on messages so messages are populated. Messages, however are not the object being queried, so attachments do not get loaded.
the correct code example might be like the following:
NOTE: fetch message with lazy loading, i.e. get messages with additional query proactively; as long as the messages are fetched from database, the corresponding attachments referenced to each message will be loaded automatically.
Customer
class Customer
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Message", mappedBy="customer", fetch="LAZY")
* #ORM\OrderBy({"createdOn" = "DESC"})
*/
private $messages;
Message
class Message
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="messages")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customer;
/**
* #ORM\OneToMany(targetEntity="Attchment", mappedBy="message", fetch="EAGER")
*/
private $attachments;
Attachment
class Attachment
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToONe(targetEntity="Message", inversedBy="attachments")
* #ORM\JoinColumn(name="message_id", referencedColumnName="id")
*/
private $message;
I have a form that shows entity:
class Event
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\OneToMany(targetEntity="EventAttendee", mappedBy="event", cascade={"all"})
*/
private $attendees;
}
and a collection within it:
class EventAttendee
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="Event", inversedBy="attendees")
* #ORM\JoinColumn(name="event_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $event;
/**
*
* #ORM\OneToOne(targetEntity="Employee")
* #ORM\JoinColumn(name="employee_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $employee;
}
If I delete an employee from the collection and add it again, I'm getting integrity constraint violation. This is because Doctrine's UnitOfWork first executes Inserts and then Deletes. Therefore, when it inserts a new record db still has the old one with the same employee.
Doctrine2 developers did not provide any working solution for Symfony2 users (here is the thread: http://www.doctrine-project.org/jira/browse/DDC-601).
And thus, I'm asking the question here: is it anyhow possible to avoid this issue?
EDIT:
My current workaround is:
find all not-persisted colletion items ready to insert
remove them from the collection and save to a variable
remove all the items that were really deleted in the form
call flush()
add all the items for insert back to the collection
call flush()
This works for me, however doesn't look good. Maybe someone has a better solution.