I used ZF2, Doctrine
I have several Entity which extends base abstract class.
One of fields in abstract class have relation with other Entity - message
My Entity:
class AbstractChat
{
............
/** ONE-TO-MANY BIDIRECTIONAL, INVERSE SIDE
* #ORM\OneToMany(targetEntity="Chat\Entity\Message", mappedBy="chat")
*/
protected $messages;
}
class Chat extends AbstractChat
{
}
class ChatBuilding extends AbstractChat
{
}
class Message
{
/**
* #var \Chat\Entity\Chat
*
* #ORM\ManyToOne(targetEntity="Chat\Entity\AbstractChat", inversedBy="messages")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="chat_id", referencedColumnName="id")
* })
*/
private $chat;
}
How can I state that the relation is with all entities which extend AbstractChat class?
This mean does not work:
#ORM\ManyToOne(targetEntity="Chat\Entity\AbstractChat", inversedBy="messages")
you can implement your relation with superclass .
https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/inheritance-mapping.html
inheritance has two type . single table and Class Table (table per class in hibernate).look at Class Table Inheritance
Related
I need some help getting my head around a 3 way mapping.
I have an entity Student and an entity Parent, obviously one parent can have many students and vice versa, but i need additional information between each parent and student that will be different for each one.
Perhaps we have the following data:
Student A - Parent A (no responsibility) , Parent B (has responsibility) - even though one parent holds legal responsibility and the other doesn't, they are both still parents of the same student.
Student B - Parent A (has responsibility), Parent B (has responsibility) - in this case another student has the same parents but this time they both have legal responsibility.
To start basic entities i would have:
class Student
{
// normally would have a ManyToMany here to link parents, but i need the 3rd entity
// to hold whether this student's parent has legal responsibility or not
}
class Parent
{
// normally again would have ManyToMany here to link students to the parent
}
class ParentStudent
{
/**
* #var boolean
* #ORM\Column(type="boolean", options={"default":true})
*/
private $responsibility = true;
// it's this part where i link student to parent and vice versa that's becoming confusing
}
This is typical Many to Many relationship with edge data / relationship data use case.
You Actually have relationship data class with extra meta data about their relationship, thus it becomes an Entity and you access it just like any other Entity class. Think this relation in terms of graph, nodes and edges. Your Student and Parent Entities are nodes while relationship between them is an edge with weight true/false (i.e 0/1).
For RDBMS, you solve it by introducing 3 entities:
Student
Parent
ParentStudent or StudentParent whichever suits and rhymes better with application
You can have as much as data to this relationship and also map it's ID to other relevant data to this association as it is an entity now.
Further You have Following relationships with each other which goes from OneToMany with intermediate entity and ManyToOne mapping inverse side, ultimately connecting both entities as ManyToMany via ParentStudent Entity:
<?php
/*
* #ORM\Entity()
*/
class Student {
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\ParentStudent", mappedBy="student", orphanRemoval=true)
*/
private $parentStudent;
}
/*
* #ORM\Entity()
*/
class Parent {
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\ParentStudent", mappedBy="parent", orphanRemoval=true)
*/
private $parentStudent;
}
/*
* #ORM\Entity()
*/
class ParentStudent {
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Parent", inversedBy="parentStudent")
* #ORM\JoinColumn(nullable=false)
*/
private $parent;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Student", inversedBy="parentStudent")
* #ORM\JoinColumn(nullable=false)
*/
private $student;
/**
* #var boolean
* #ORM\Column(type="boolean", options={"default":true})
*/
private $responsibility = true;
}
When defining a relationship, there is a property on the related model (not a DB column), but I would like to sort by it (in the #OrderBy annotation).
I have a base model that is extended using single table inheritance. The property in question is basically an order property that is specified in each child class, but is not saved to the DB.
(I don't want to add an order column to the DB table, since the ordering depends purely on which child class the discriminator is mapped to. There is already a unique constraint so that each child class can be used no more than once in the relationship.)
Here's a really simplified version of my code...
Base entity:
/**
* #ORM\Entity
* #ORM\Table(name="base")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="class_name", type="string")
*
* #ORM\DiscriminatorMap({
* "Base" = "Models\Base",
* "ChildA" = "Models\ChildB",
* "ChildB" = "Models\ChildA"
* })
**/
class Base
{
/** #ORM\Column(type="string") **/
protected $class_name;
/**
* #ORM\ManyToOne(targetEntity="Related", inversedBy="collection")
**/
protected $related;
// this is just a plain ol' property (not in the DB)
protected $order;
public function getClassName()
{
return $this->class_name;
}
}
Children:
/**
* #ORM\Entity
* #ORM\Table(name="child_a")
**/
class ChildA extends Base
{
$order = 1;
}
/**
* #ORM\Entity
* #ORM\Table(name="child_b")
**/
class ChildB extends Base
{
$order = 2;
}
Related entity:
/**
* #ORM\Entity
* #ORM\Table(name="related")
**/
class Related
{
/**
* #ORM\OneToMany(targetEntity="Base", mappedBy="related")
* #ORM\OrderBy({"order"="ASC"})
**/
protected $collection;
public function getCollection()
{
$em = App::make('Doctrine\ORM\EntityManagerInterface');
// map each Base instance to the appropriate child class
return $this->collection->map(function ($base) use ($em) {
$class_name = $base->getClassName();
return $em->find($class_name, $base->getId());
});
}
}
Is it possible to use the order property for ordering the collection relationship? (Ordering based on class_name using a switch-like construct would also be valid, but I haven't found any way to do that either, and it would be harder to maintain.)
Thanks in advance!
The directive beginning with ORM is very much telling Doctrine you're doing referencing a property that has a relationship with a table field. You can't use ORM directives on fields that don't exist. Doctrine annotations: OrderBy
You would have to implement this in a function, best in the model itself (within your getCollection() function), or if you're using a framework like Symfony place it in a function of the repository class for this entity. You'd have to use PHP sorting functions to do this. SQL/DQL won't work either because the property isn't related to a field in the table.
I’m trying to set up a Base-class for my Typo3 Flow projects. It should contain the “created at” and the “updated at” date.
Since Doctrine allows you to use inheritance mapping, I want to make my baseclass a “MappedSuperclass”.
BaseClass.php:
/**
* #Flow\Entity
* #ORM\MappedSuperclass
*/
class BaseClass {
/**
* #var \DateTime
* #ORM\Column(type="datetime")
*/
protected $created;
/**
* #var \DateTime
* #ORM\Column(type="datetime")
*/
protected $updated;
...
Component.php:
/**
* #Flow\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
*/
class Component extends BaseClass{
If i try to use the "flow doctrine:update" command the following error message pops up:
Uncaught Exception
Entity '...\Domain\Model\BaseClass' has no method
'Flow_Aop_Proxy_fixMethodsAndAdvicesArrayForDoctrineProxies' to be
registered as lifecycle callback.
So is it possible to use model inheritance in TYPO3 Flow?
I found out one way to do it.
Just make your BaseClass abstract and add all the additional annotations like this:
/**
* #Flow\Entity
* #ORM\MappedSuperclass
*/
abstract class BaseClass {
And extend your models like that:
/**
* #Flow\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
*/
class SomeModel extends BaseClass{
The table of SomeModel will now have the attributes from the BaseClass. But BaseClass itself is not represented in the database schema.
Maybe you are also able to use traits for more complex solutions.
In my project I have several class table inheritances like this:
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/** #Entity */
class Employee extends Person
{
// ...
}
I have a method which converts entities to arrays based on the fields which have public getters. The problem here is that I lose the inheritance information in my array because the discriminator value isn't stored in a field.
So what I tried was the following, hoping doctrine would automatically set $disc:
class Person
{
// can I automatically populate this field with 'person' or 'employee'?
protected $discr;
public function getDiscr() { return $this->discr; }
public function setDiscr($disc) { $this->discr; }
// ...
}
Is there a way to make this work in doctrine? Or would I need to read the class metadata in my entity-to-array method?
Sadly, there is no documented way to map the discr column to an entity. That's because the discr column is really part of the database and not the entity.
However, it's quite common to just put the discr value directly in your class definition. It's not going to change and you will always get the same class for the same value anyways.
class Person
{
protected $discr = 'person';
class Employee extends Person
{
protected $discr = 'employee';
Here's a small example of what I have in one of my ZF2 projects (using Doctrine MongoDB ODM):
// an instance of your entity
$entity = ...;
/** #var \Doctrine\ODM\MongoDB\DocumentManager $documentManager */
$documentManager = $serviceManager->get('DocumentManager');
/** #var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory $factory */
$factory = $documentManager->getMetadataFactory()
/** #var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $metadata */
$metadata = $factory->getMetadataFor(get_class($object));
if ($metadata->hasDiscriminator()) {
// assuming $data is result of the previous extraction
$data[$metadata->discriminatorField] = $metadata->discriminatorValue;
}
What I have done is I've implemented a custom interface DiscriminatorAwareInterface and I only apply the checks to classes that implement it (in your case it would be the class that all "discriminated" classes extend.
As a result I end up with code that looks like this:
// add value of the discrinimator field to entities that support it
if ($object instanceof DiscriminatorAwareInterface) {
/** #var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $metadata */
$metadata = $factory->getMetadataFor(get_class($object));
if ($metadata->hasDiscriminator()) {
$data[$metadata->discriminatorField] = $metadata->discriminatorValue;
}
}
I'm pretty sure it will be the same if you use the standard ORM, except instead of a document manager you will have entity manager.
Just got this problem and solved it without defining the discriminator as a real member:
abstract class MyEntity {
const TYPE_FOO = 'foo';
const TYPE_BAR = 'bar';
const TYPE_BUZ = 'buz';
...
/**
* #return string
*/
public function getMyDiscriminator()
{
$myDiscriminator = null;
switch (get_class($this)) {
case MyEntityFoo::class:
$myDiscriminator = self::TYPE_FOO;
break;
case MyEntityBar::class:
$myDiscriminator = self::TYPE_BAR;
break;
case MyEntityBuz::class:
$myDiscriminator = self::TYPE_BUZ;
break;
}
return $myDiscriminator;
}
...
}
class MyEntityFoo extends MyEntity {}
class MyEntityBar extends MyEntity {}
class MyEntityBuz extends MyEntity {}
You can use the following solution:
`$`$metadata = \Doctrine\ORM\Mapping\ClassMetadata((string)$entityName);
print_r($metadata->discriminatorValue);`
With class table inheritance,
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
How can I get the type info from the entity? Since discr is a database column but not a property of the entity?
You can create abstract method getKind() in parent class and implement it in subclasses like
public function getKind()
{
return 'employee';
}