Can I get the EntityManager from within an entity's class methods? - php

Is there an easy way to get Doctrine's entity manager from within an entity's class method?
<?php
/** #Entity */
class MyEntity {
/** #Id #GeneratedValue #Column(type="integer") */
protected $id;
[...]
public function someFunction() {
// Is there any way to get Doctrine's EntityManager in here?
}
}

You're really not supposed to. The idea behind a datamapper ORM like Doctrine is that your entities are just plain-old objects that know nothing about the persistence layer. If you find yourself wanting an EntityManager inside your entity, that's a signal that you ought to be creating a service class of some sort.
That said, Doctrine is quite flexible. For example, if you were so inclined, you could use Doctrine as the foundation for an ActiveRecord-style ORM.
However, outside of very specific use-cases, I wouldn't recommend it.

Related

Getter for a object from the JSON-Field

I have 2 entities:
class Opponent
{
...
...
...
}
class Process
{
/**
* #var array
*
* #ORM\Column(name="answers_in_related_questionnaires", type="json", nullable=true)
*/
private $answersInRelatedQuestionnaires = [];
.
.
.
}
I have in the field answersInRelatedQuestionnaires amongst other things the object opponent
"opponent": {
"id":1088,
"name":"Inora Life Versicherung"
}
I want to write a getter in the entity process, that gets not only the both values id and name from opponent, but the whole entity Opponent. Something like this:
private function getOpponent() : Opponent
{
$id = $this->answersInRelatedQuestionnaires['opponent']['id'];
return $entityManager->getRepository(Opponent::class)->find($id)
}
I have read, that using of the entity manager within the entity is not a good idea. Which solutions for my issue are there? Can I use the Process repository in the Process entity?
You should not inject entity manager in an entity, it's a very bad practice and violates the separation of concerns between classes. BUT if you really want you indeed can inject entity manager in your entity.
GOOD PRACTICE:
Create a Model/Process class and include there any functionality that concerns your model. Doctrine entities are not model classes. In Model/Process you can inject the entity manager and any other service, you need.
EDIT: By creating a Model/Process class I mean creating a class named Process inside Model directory in your /src folder. Your path of your class will be: /src/Model/Process. Of course, the name of the directory or the class can by anything, but this is a typical convention. Your Model class should be responsible for all your business logic, such as validation of your model etc. This will indeed make your code structure more complicated but will be a savor in the long run for large scale projects. You will also need a Model/ProcessManager to properly populate Process model in different cases (e.g. when loaded from Database, user form etc.) Of course, in the end it's all a matter of trade-off between complexity and sustainability.
An interesting approach about models in Symfony, mostly applicable in large scale projects, can be found here.
ALTERNATIVES:
If you access the opponent attribute only after an entity has been loaded you can use Doctrine PostLoad LifecycleCallback to properly set opponent attribute. This is not a bad practice:
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks()
*/
class Product
{
// ...
private $opponentObject;
/**
* #ORM\PostLoad
*/
public function onPostLoad(LifecycleEventArgs $args){
$em = $args->getEntityManager();
$id = $this->answersInRelatedQuestionnaires['opponent']['id'];
$this->opponentObject = $em->getRepository(Opponent::class)->find($id);
}
public function getOpponent() {
return $this->opponent;
}
}
Finally if you really really want to inject the entity manager into your entity you can achieve that with dependency injection via autowiring:
use Doctrine\ORM\EntityManagerInterface;
class Process
{
private $em;
public function __contruct(EntityManagerInterface $em)
{
$this->em = $em;
}
....
}

Can a Doctrine entity read itself?

just curious .. I have some code like so:
//$em is EntityManager of Doctrine
//$className is a type that uses polymorphism (subtype of a parent type)
$pricing = $em->getRepository($className)->findOneBy(array(
'active' => true,
'product_id' => (int) $this->id
));
//gets serialization of certain variables of $className
return $pricing->getSerialization();
But ... instead of calling findOneBy outside of $className, can I move getSerialization() method inside the Entity (which is $className), and return class-parameters from there?
I imagine it is not possible, since Entity cannot read itself. Correct?
The problem I am trying to solve is. ... In the example above Entity is populated via Doctrine and then it returns data. Therefore I have to use another class to populate the entity. Without Doctrine I know it's possible to do things such as read data from inside the Entity i.e. via mysqli, and then return properties directly or via a method. In other words, do I absolutely need another location (class/function/method outside of Entity) to populate the entity?
Sample Entity looks like so
class Pricing
{
function getSerialization(){}
/**
* #var integer #Column(name="id", type="integer", nullable=false)
* #Id
* #GeneratedValue(strategy="IDENTITY")
*/
protected $id;
//etc, a typical Doctrine Entity
}
Yes, the instance of an entity class can read itself.
But I guess your question should have been: "Can a Doctrine entity load and read itself?". The answer to that is no...
Loading of entities is managed by doctrine internals. If you would want the entity classes to load themselves it would mean injecting an EntityManager into the entity class.
This is a bad idea, I quote #BryanM. his answer on another stackoverflow question that covers this nicely:
It is not a good idea to allow an entity object to rely on the entity manager. It ties the entity to the persistence layer, which was a problem Doctrine 2 was specifically trying to solve. The biggest hassle in relying on the entity manager is that it makes your model hard to test in isolation, away from the database.
You should probably be relying on service objects to handle the operations that rely on the entity manager.
It means you need to take care of loading entities externally. I still don't see the problem with getSerialization. It can be inside the Entity class and can be used after the entity is loaded right?
If you want to do loading and serializing at once I would suggest making a PricingService in which you inject the repository or entity manager and where you define a public methods that does all that. For example:
<?php
use Application\Entity\Pricing;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
class PricingService
{
/**
* #var EntityManager
*/
protected $entityManager;
/**
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* #return EntityRepository;
*/
protected function getRepository()
{
return $this->entityManager->getRepository(`Application\Entity\Pricing`);
}
/**
* #param $params
* #return array
*/
public function findSerializedBy($params)
{
$pricing = $this->getRepository()->findOneBy($params);
return $pricing->getSerialization();
}
}
Now you can work with your PricingService directly:
$serializedPricing = $pricingService->findSerializedBy(array(
'active' => true,
'product_id' => (int) $this->id
));
You can of course generalize your service by adding another parameter with the $classname.

Map subclass as its extended parent

I have created the following abstract class, which use single table inheritance and maps subclasses on the DiscriminatorColumn model.
/**
* #Entity
* #Table(name="entity")
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="model", type="string")
* #DiscriminatorMap({
* "green" = "model\GreenEntity",
* "blue" = "model\BlueEntity"
* })
*/
abstract class AbstractEntity
{
/** #Id #Column(type="string") */
protected $entity_id;
}
Let's say I extend the abstract class AbstractEntity by some classes:
class GreenEntity extends AbstractEntity {}
class BlueEntity extends AbstractEntity {}
And extend these by some more subclasses
class GreenEntityChildOne extends GreenEntity {}
class GreenEntityChildTwo extends GreenEntity {}
class BlueEntityChildOne extends BlueEntity {}
class BlueEntityChildTwo extends BlueEntity {}
Now, for example, when I instantiate GreenEntityChildOne and persist it to the database, it will throw an exception that I don't have a mapping for it.
What I'm trying to do is get GreenEntityChildOne to be mapped as GreenEntity (or rather, every class which extends a class below AbstractEntity to be mapped as the class which extends the upper abstract class).
Is this at all possible?
It's not possible with pure annotations
Yes, the mapping you are trying to achieve is possible. However, not with pure annotations. The important thing is that Doctrine needs to know all sub classes at runtime. If you do not want to state them explicitly in the annotations of the mapped superclass, you will need to dynamically provide them.
Doctrine event system to the rescue
There is a great blog post on dynamic mapping with Doctrine, which explains how you can use Doctrine event listeners to programmatically change the loaded ClassMetadata.
To dynamically add subclasses to the discriminator map you can implement a Doctrine event listener like the following:
class DynamicDiscriminatorMapSubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return array(Events::loadClassMetadata);
}
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$metadata = $eventArgs->getClassMetadata();
$metadata->addDiscriminatorMapClass("GreenEntityChildOne", GreenEntityChildOne::class);
}
}
Register your subscriber
Now you only need to register the event subscriber with Doctrine. Ideally, you inject the classes you want to add based on your configuration to the event subscriber.
// create dynamic subscriber based on your config which contains the classes to be mapped
$subscriber = new DynamicDiscriminatorMapSubscriber($config);
$entityManager->getEventManager()->addEventSubscriber($subscriber);
Further reading
Also, have a look at the PHP mapping section in the Doctrine manual and the more informative API docs for the ClassMetadataBuilder.
Answer is possibly on the Doctrine Docs:
"All entity classes that is part of the mapped entity hierarchy (including the topmost class) should be specified in the #DiscriminatorMap"
http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html
You've only specified GreenEntity and BlueEntity.
I don't know what I'm talking about. This is the first thing I've ever read about Doctrine...

Doctrine association mapping between entities in different bundle or relate entities from two different bundles?

Region:
namespace Acme\RegionBundle\Entity;
class Region
{
private $id;
/**
* #ORM\OneToMany(targetEntity="User")
* #ORM\JoinColumn(name="region_id", referencedColumnName="id")
*/
private $users;
}
User:
namespace Acme\UserBundle\Entity;
class User
{
private $id;
private $region_id;
}
How to relate entities from different bundle without mentioning fully specified entity path i.e. hard coding dependency.
Is there any better approach ?
Can Resolve Target Entity Listener be a solution. I couldn't understand how it could be applied here ?
The resolve target entity listener allows you to re-define associations at runtime. It allows you basically to map something like following:
#ORM\OneToMany(targetEntity="My\Namespace\UserInterface")
As you can see, mapping an interface as target entity does not make much sense. It becomes really useful when you tell that every My\Namespace\UserInterface has to be replaced with a Other\Namespace\User reference.

How can I reuse columns and events in Doctrine 2 Entities?

Suppose I'm writing a blog app: My Posts Entity should have a createddate, modifieddate, name, and slug property (the slug would be generated when I call setName() for the Entity). I want to reuse this code, but I also want to reuse it on-demand. I may, for instance, have a Log Entity which wants to use createddate, but not modifieddate or slug functionality.
Class inheritance, whether via traits (mixins) or abstract classes, seems to be an insufficient, or at least improper, approach to reusing this functionality, as it doesn't pass the is-a test. After all, the Entity has-a createddate, but isn't a createddate itself.. so we should use Composition rather than Inheritance, right?. However, observer doesn't seem to work here, since while I want to use this functionality on-demand, Doctrine's use of annotation and object properties seem to make horizontal injection difficult (and expensive?) without use of Reflection.
To show some code and simplify the question a bit: can I at once inject the following definition and functionality into an Entity without breaking DRY or good OOP (proper use of composition / inheritance) practice?
/**
* #var DateTime $createddate
*
* #ORM\Column(type="datetime")
*/
private $createddate;
/**
* #ORM\PrePersist
*/
public function createddatePrePersist() {
$this->createddate = new \DateTime('now');
}
My personal opinion is that you're trying to find the nail for your hammer. You don't have to use composition, inheritance, traits, or any OOP pattern for every entity.
I tend to consider that the creationDate is an inherent property of my entity, and any entity that requires a creation date has this property. One may argue that's repeating myself, but in my opinion that makes the whole entity much easier to read.
Oh, and if I may, I wouldn't use an ORM-specific method, which is not part of my domain model, to initialise the creation date. This would be done in my constructor:
/**
* #var DateTime $createddate
*
* #ORM\Column(type="datetime")
*/
private $createddate;
public function __construct()
{
$this->createddate = new \DateTime();
// ... and other business rules that need to be enforced in the constructor
}

Categories