I got a question. I have been waiting on the DiscriminatorColumn annotation in Doctrine 2 but now that I got it via Update of Doctrine I am not able to find the Hibernate's DiscriminatorValue annotation equivalent in Doctrine. FYI, my Doctrine version is "doctrine/orm": "^2.5.6" and "doctrine/doctrine-bundle": "~1.6" but I cannot find such annotation.
My basic desire here is to set the Discriminator Column value per child class not in the main class in the DiscriminatorMap.
Like my comment stated, I had this issue a while back as well, where I wanted to "declare" new DiscriminatorMap entries on the child-classes. The short answer is: do not declare a map at all. Doctrine takes care of it.
Have a read of my full answer. This works for me using Class Table Inheritance (CTI), the docs state that it should work the same for Single Table Inheritance (STI).
The basic code setup to let Doctrine handle it for you is:
<?php
namespace My\Namespace\Entity;
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr", type="string")
* // NOTE: No DiscriminatorMap!!!
*/
class Person
{
// ...
}
<?php
namespace My\Other\Namespace\Entity;
/** #Entity */
class Employee extends \My\Namespace\Entity\Person
{
// ...
}
Related
I'm facing a problem while using $em->flush ();
the object $education is persisted throw $em->persist ($education).
The location is an entity in my project and is related to the education entity throw a many-to-one relation.
The error box Contain:
A new entity was found through the relationship 'XBundle\Entity\Education#location' that was not configured to cascade persist operations for entity: NewYork. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
How can i solve this issue?
Use cascade={"persist"} on the relation. E.g.:
/**
* #ORM\OneToOne(targetEntity="Foo\Bundle\Entity\User", mappedBy="bar", cascade={"persist"})
*/
protected $foo;
In Doctrine 2, cascade persistence doesn't happen automatically. Instead, you need to explicitly indicate that you want it. If you are using docblock annotations to specify your DB schema, that's achieved by adding the cascade attribute to your #ManyToOne association:
<?php
namespace XBundle\Entity;
/**
* #Entity
*/
class Education
{
//...
/**
* #ManyToOne(targetEntity="Location", cascade={"persist"})
*/
protected $location;
//...
}
I'm using Symfony with Doctrine.
To resolve my problem I want to:
or When I'm extending Entity class I want the doctrine to ignore parent's class #entity annotation (to see it as #MappedSuperclass)
or (this one is more preferable) When I'm extending Entity class add to child class something like #MappedChildclass just to know that this class is an Entity, but actual implementation and mappings in the parent class
Lets see concrete problem:
I have 3 bundles:
AppBridgeBundle
UserBundle
ProfileBundle
UserBundle and UserProfile must be decoupled. AppBridgeBundle is a bridge between 2 bundles, it will couple both.
UserBundle has UserEntity:
/**
* #ORM\Entity
**/
class UserEntity {
/**
* #var \Acme\UserBundle\Interfaces\ProfileData
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Interfaces\ProfileData",
cascade={"persist"})
* )
*/
private $profileData;
// ...
}
UserBundle has its own interface ProfileData (decoupled, we only need to inject implementation of this interface).
ProfileBundle has ProfileEntity:
/**
* #ORM\Entity
**/
class ProfileEntity implements Acme\ProfileEntity\Interfaces\Profile {
// properties ...
}
ProfileBundle has its own interface Profile (decoupled).
Basically ProfileData and Profile interfaces are the same.
Now AppBridgeBundle introduces Adapter of Profile and ProfileData interfaces to adapt UserBundle with ProfileBundle.
class ProfileAdapter extends \Acme\ProfileBundle\Entity\ProfileEntity
implements \Acme\UserBundle\Interfaces\ProfileData,
\Acme\ProfileBundle\Interfaces\Profile {
}
Next, we inject our interface implementation in the config.yml app configuration:
orm:
resolve_target_entities:
Acme\UserBundle\Interfaces\ProfileData: Acme\AppBridgeBundle\Entity\ProfileAdapter
Now, the problem is that when I update doctrine schema it throws me an error that Acme\AppBridgeBundle\Entity\ProfileAdapter is not Entity.
If I mark ProfileAdapter with #Entity then it will create 2 separate tables - I don't need it
If I mark ProfileAdapter with #Entity with the same name as ProfileEntity - #Table('profileentity') then it throws me an error that table profile already exists
If I mark ProfileEntity with #ORM\MappedSuperclass and remove #Entity annotation from it - I'll loose default implementation of the ProfileEntity class (so it cant work without bridge anymore).
if I mark ProfileEntity with #InheritanceType("SINGLE_TABLE") it will add to the tables unnecessary discriminator field to my table.
Any suggestions?
I think the trick is to use the dispatched doctrine "loadClassMetadata" event.
You can track this event in a subscriber which is aware of ProfileEntity inheritance, and inject the "#Entity" or the "#MappedSuperclass" annotation.
You can see an implementation here : https://github.com/victoire/CmsBundle/blob/master/EventSubscriber/LoadMetadataSubscriber.php
In this example, I dynamically inject enabled widgets to the discriminatorMap of my Widget entity.
I let you adapt this code to your needs.
Had the same problem, what I did using your example:
move \Acme\ProfileBundle\Entity\ProfileEntity to \Acme\ProfileBundle\Model\ProfileEntity
redeclare ProfileEntity class to abstract but leave all the ORM instructions intact
redeclare ProfileAdapter to extend new ProfileEntity model
mark ProfileAdapter as #ORM\Entity only, all remaining #ORM* instructions leave in the model class
Pros:
no additional tables
no additional fields
can work without the bridge
Cons:
it is necessary to create entity extending abstract model for each new Bundle installation
I'm having trouble finding a way to appropriately extend an Entity across bundles in Symfony2 using Doctrine2 as the ORM.
Currently there are three methods that I've found to extending entities in Symfony2 using Doctrine2 as the ORM. Mapped Superclass, Single Table Inheritance and Class Table Inheritance. None of these work for what I'm looking to do.
I have two Bundles: UserBundle and BlogBundle. I want to be able to use the UserBundle in projects that do not have the BlogBundle, but the BlogBundle will always be used in projects that have the User Bundle. It's ok if the BlogBundle has dependencies on the UserBundle, but not the other way around.
I have two entities:
BlogBundle\Entity\Post and
UserBundle\Entity\User
Relationship:
There needs to be a One to Many relationship between Users and Blog Posts. This is achieved through a Author_ID property (column) on the BlogBundle\Entity\Post object (table) which is mapped to UserBundle\Entity\User.id
The Problem:
I can call the UserBundle\Entity\User entity directly from within the BlogBundle and achieve what I'm looking for using a Uni-Directional mapping. This does not allow me to access all posts by a user from within a User object. I can access the data via custom queries but this is not as clean as accessing posts by a user through the user object.
What I'd like to do is extend the UserBundle\Entity\User object from within the BlogBundle, and add the methods and properties to this object that establish the One to Many mapping used within the BlogBundle. None of this is persisted, it simply defines the relationship and allows me to logically access all posts created by a user in an application that implements both the BlogBundle and UserBundle by adding needed functionality to the User object within the blog bundle (thus avoiding a dependency from the UserBundle to the BlogBundle).
When I create a BlogBundle\Entity\User object and extend UserBundle\Entity\User I must declare #ORM\Table(name="usertablename"). If I don't, any attempt to access the BlogBundle\Entity\User object will fail to access the database. Since none of the additions in the extended object persist, this works fine across bundles. The issue with this is when I call "php app/console doctrine:schema:update --force", there is a conflict since two entities try to map to & create the same table. I have tried using the ResolveTargetEntityListener feature that was recently implemented but this, along with Mapped Superclas, STI and CTI all force a dependency on the BlogBundle from the UserBundle.
Below are my objects to help illustrate my my setup. They have been abbreviated for clarity. I realize some of the semantics aren't correct but it's intended to communicate the ideas & configuration.
UserBundle\Entity\User
#ORM\Table(name="app_user")
#ORM\Entity
class User implements UserInterface
{
...
}
BlogBundle\Entity\Post
#ORM\Table(name="app_post")
#ORM\Entity
class Post
{
...
#ORM\Column(name="author_id", type="integer")
protected $author_id;
#ORM\ManyToOne(targetEntity="\App\BlogBundle\Entity\User", inversedBy="posts")
#ORM\JoinColumn(name="author_id", referencedColumnName="id")
protected $author;
}
BlogBundle\Entity\User
use App\UserBundle\Entity\User as BaseUser
#ORM\Entity
#ORM\table(name="app_user")
class User extends BaseUser
{
....
#ORM\OneToMany(targetEntity="App\BlogBundle\Entity\Post", mappedBy="author")
protected $posts;
public function __construct()
{
parent::_construct();
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
}
....
/* Getters & Setters, nothing that defines #ORM\Column, nothing persisted */
}
This works but the problem is that I'm mapping two entities in the project to the same table. The extended object doesn't grab the #ORM\Table(name="app_user") from it's parent so it must be defined in BlogBundle\Entity\User. If not any reference to this object from a controller will not access the database. Since nothing is persisted from the extended object nothing is broken except for when I try to update the database schema from the console.
I can use a unidirectional relationship, but this limits how I can access the data from within a controller.
You can see in this link to know about inheritance: http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html#single-table-inheritance
You must declare in UserBundle\Entity\User:
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"baseuser" = "UserBundle\Entity\User", "blogUser" = "BlogBundle\Entity\User"})
*/
class User implements UserInterface
{
...
}
And BlogBundle\Entity\User
use App\UserBundle\Entity\User as BaseUser;
/**
* #ORM\Entity
*/
class User extends BaseUser
{
....
}
Goodluck!
I think you could find this Bundle interesting:
https://github.com/mmoreram/SimpleDoctrineMapping
It allows you to define by parameters what files are mapping your entities, allowing to override every entity of your generic bundles.
For example:
parameters:
#
# Mapping information
#
test_bundle.entity.user.class: "TestBundle\Entity\User"
test_bundle.entity.user.mapping_file_path: "#TestBundle/Mapping/Class.orm.yml"
test_bundle.entity.user.entity_manager: default
test_bundle.entity.user.enable: true
The only contra I see is that you have to define all the next entities the same way cause you disabled auto_mapping...
My doctrine entity has a datetime property called updated. This should get the current time everytime (changed) values of the object are written to the DB. I know how to do this in MySql, but I'm looking for a doctrine/symfony solution.
Is there a way to
hook into something before an INSERT/UPDATE of an instance is sent to the DB.
update the updated property to the current time and make sure it's written to the DB without triggering a second UPDATE statement.
See the Timestampable behavior of DoctrineExtensions. There is StofDoctrineExtensionsBundle to intergrate it into Symfony.
You don't need any extensions, just use Lifecycle Callbacks.
Basically, mark your entity that it has event callbacks configured with the HasLifecycleCallbacks annotation:
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
* ...
*/
class MyEntityClass {
...
And then mark instance methods to be run on specific events occuring, e.g. to set updated:
...
class MyEntityClass {
...
/**
* #ORM\PreUpdate
*/
public function onPreUpdate() {
$this->updated = new \DateTime();
}
...
I'm building a small website with Symfony2 and Doctrine2. There are blog posts, events and press releases. Each of these is so similar that I decided to use single table inheritance (STI) with a parent table called 'Node'.
Nodes have:
a 'published' field, which is boolean
a 'locale' field, which is a string and says 'this is only to be shown in this locale'. (The locale is passed in via the request).
By default, I only want to display published nodes that are from the current locale.
Obviously, I could create lots of queries in the repository that look something like:
$this->createQueryBuilder('Event')
->where('Node.published = 1')
->where('Node.locale = :locale')
but this doesn't seem very DRY.
So how do I build a default query which other queries can 'inherit' from? This should include default Doctrine queries based on relations.
Inheritance is probably overkill.
Why not just create a little factory method that gives you a preconfigured queryBuilder?
class NodeRepository extends Doctrine\ORM\EntityRepository {
public function getLocalizedNodeQueryBuilder($type,$locale){
return $this->getQueryBuilder($type)
->where('Node.published = 1')
->where('Node.locale = :locale')
->setParameter('locale',$locale);
}
}
You could do the same thing, and simply override getQueryBuilder if you're sure you always want your querybuilder configured that way.
You don't need to build anything like that into your repository classes. If you set up single table inheritance with a "Discriminator Map" you'll end up with seperate classes (Entities). Doctrine will take care of filtering by your "node type" when it interacts with the DBAL.
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/inheritance-mapping.html#single-table-inheritance
For example..
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"node" = "Node", "blogpost" = "Blogpost", "events" = "Events"})
*/
class Node
{
// ...
}
/**
* #Entity
*/
class Blogpost extends Node
{
// ...
}
/**
* #Entity
*/
class Events extends Node
{
// ...
}
// get me all the blogposts
$blogposts = $em->getRepository('Blogpost')->findAll();
// get me all the events
$events = $em->getRepository('Events')->findAll();
This is especially beneficially as you'll be able to encapsulate your "Blogpost" logic into its own entity, rather than trying to represent it with its parent class "Node".