Getter for a object from the JSON-Field - php

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;
}
....
}

Related

Is it considered a bad practice to add fields to Symfony entity in controller?

Is it considered a bad practice to add fields to Symfony entity in controller? For example lets say that I have a simple entity:
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
}
And then in UserController.php I want to do the following:
foreach($users as $user){
$user->postsCount = someMethodThatWillCountPosts();
}
So later that postsCount can be displayed in Twig. Is it a bad practice?
Edit:
It's important to count posts on side of mysql database, there will be more than 50.000 elements to count for each user.
Edit2:
Please take a note that this questions is not about some particular problem but rather about good and bad practices in object oriented programming in Symfony.
As #Rooneyl explained that if you have relation between user and post then you can get count easily in your controller, refer this for the same. But if you are looking to constructing and using more complex queries from inside a controller. In order to isolate, reuse and test these queries, it's a good practice to create a custom repository class for your entity.Methods containing your query logic can then be stored in this class.
To do this, add the repository class name to your entity's mapping definition:
// src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
*/
class Product
{
//...
}
Doctrine can generate empty repository classes for all the entities in your application via the same command used earlier to generate the missing getter and setter methods:
$ php bin/console doctrine:generate:entities AppBundle
If you opt to create the repository classes yourself, they must extend
Doctrine\ORM\EntityRepository.
More Deatils
Updated Answer
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. where posts can be commented, you always have to assume that a post draws hundreds of comments. In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This can lead to pretty serious performance problems, if your associations contain several hundreds or thousands of entities.
With Doctrine 2.1 a feature called Extra Lazy is introduced for associations. Associations are marked as Lazy by default, which means the whole collection object for an association is populated the first time its accessed. If you mark an association as extra lazy the following methods on collections can be called without triggering a full load of the collection: SOURCE
"rather about good and bad practices in object oriented programming"
If that's the case then you really shouldn't have any business logic in controller, you should move this to services.
So if you need to do something with entities before passing them to twig template you might want to do that in specific service or have a custom repository class that does that (maybe using some other service class) before returning the results.
i.e. then your controller's action could look more like that:
public function someAction()
{
//using custom repository
$users = $this->usersRepo->getWithPostCount()
//or using some other service
//$users = $this->usersFormatter->getWithPostCount(x)
return $this->render('SomeBundle:Default:index.html.twig', [
users => $users
]);
}
It's really up to you how you're going to do it, the main point to take here is that best practices rather discourage from having any biz logic in controller. Just imagine you'll need to do the same thing in another controller, or yet some other service. If you don't encapsulate it in it's own service then you'll need to write it every single time.
btw. have a read there:
http://symfony.com/doc/current/best_practices/index.html

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.

How to get the instance of Kernel in the Entity class in Symfony2

The title explains the question pretty well. I am in the lifecycle callback of the Doctrine Entity class and want to do some extra DB entries. For this I need to get an instance of the Kernel. How can I do this?
Needing the container/kernel in an entity is most of the time, wrong. An entity shouldn't be aware of any services. Why is that?
Basically, an entity is an object which represents a thing. An entity is mostly used in a relationnal database, but you can at any time use this entity for other matters (serialize it, instanciate it from an HTTP layer...).
You want your entity to be unit-testable, this means you need to be able to instanciate your entity easily, without anything around, mostly, without any piece of business logic.
You should move your logic into another layer, the one that will instanciate your entity.
For your use case, I think, the most easy way is to use a doctrine event.
services.yml
services:
acme_foo.bar_listener:
class: Acme\FooBundle\Bar\BarListener
arguments:
- #kernel
tags:
- { name: doctrine.event_listener, event: postLoad }
Acme\FooBundle\Bar\BarListener
use Symfony\Component\HttpKernel\KernelInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\FooBundle\Entity\Bar;
class BarListener
{
protected $kernel;
/**
* Constructor
*
* #param KernelInterface $kernel A kernel instance
*/
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
/**
* On Post Load
* This method will be trigerred once an entity gets loaded
*
* #param LifecycleEventArgs $args Doctrine event
*/
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!($entity instanceof Bar)) {
return;
}
$entity->setEnvironment($this->kernel->getEnvironment());
}
}
And there you go, your entity remains flat without dependencies, and you can easily unit test your event listener
if you have to use some service, you shouldn't use whole container or kernel instance especially.
use the services itself - always try to inject single service, not whole container
your case looks like you should use doctrine events

MVC structure with Zend Framework 2 and Doctrine

What is "the right way" to implement business logic Models with ZF2 and Doctrine, while creating a clean OOP and MVC structure, and providing the Models with access to the EntityManager?
Althought I am tempted to use the Doctrine Repositories for all the business logic, I know they should primarily be used to perform complex queries. I also know that a model in ZF2 can be provided with dependencies (EntityManager) in several ways: ZF2: Dependency injection done the proper way http://zend-framework-community.634137.n4.nabble.com/ZF2-Injecting-objects-to-a-controller-or-getting-objects-from-the-service-locator-td4656872.html
To illustrate the problem in real life, how do we create a Model layer for a simple Web shop functionality:
Firstly, we would have the Entities
/**
* #Entity(repositoryClass="App\Repository\ProductRepository")
* #Table(name="products")
*/
class Product { ... }
/**
* #Entity(repositoryClass="App\Repository\ProductgroupRepository")
* #Table(name="productgroups")
*/
class Productgroup { ... }
And the Repositories which give us more detailed query capabilities
class ProductRepository extends EntityRepository {
public function isAvailable() { ... }
public function getQuantity() { ... }
public function getProductImages() { ... }
public function getRelatedProducts() { ... }
...
}
class ProductgroupRepository extends EntityRepository {
public function getAvailableProducts() { ... }
public function getAllProducts() { ... }
...
}
But where do we place the business logic such as this?
Products functionalities:
- createNewProduct( $data ), eg. update prices, retrieve new quantities, generate a new image gallery, notify the administrator, ...
- deleteProduct(), eg. delete corresponding files and existing associations, put product into archive
- ...
Productgroups functionalities:
- removeProductFromProductgroup( $product), eg. update ordering in group, remove associations, ...
- addProductToProductgroup( $product ), eg. update ordering in group, create associations, ...
- deleteProductgroup(), eg. delete productgroup, set all it's products as uncategorized
- ...
Should the Productgroup business model be created, for example, as a class which is injected with the EntityManager at the service level? --
class Productgroup implements ServiceLocatorAwareInterface
{
public function removeProductFromProductgroup( $productgroup, $product) { }
public function addProductToProductgroup( $productgroup, $product) { }
}
Or should it maybe also extend the original entity so as to have access to its internal structure? --
class Productgroup extends \Application\Entity\Productgroup
implements ServiceLocatorAwareInterface
{
public function removeProductFromProductgroup( $productgroup, $product) { }
public function addProductToProductgroup( $productgroup, $product) { }
}
And if so, should it also have some kind of a set state method ?
public function set( $product ) {
$this->populate( $product );
}
About where to have Business Logic, it is obviously Models. I would not recommend extending your Entities because any logic which has nothing to do with setting/getting your entity object should be outside your entity.
So the answer is yes, I would be injecting the Entity Manager into the models. But whether you use the Service Locator or some other method for DI is upto you.
The questions you are pondering over is really about OOP, DI, DiC, etc. Please go through some of the best blog posts on DI from the gurus. I have listed them below:
Inversion of Control
Martin Fowler's Inversion of Control Containers and the Dependency Injection pattern
Learning About Dependency Injection and PHP
What is Dependency Injection?
Its really upto you to take your dependency injection to different levels. If you check Martin Fowler's blog, you can find out about constructor injection, setter injection and a DiC doing the injection for you. You have to take the call for your system. What is working for me is, for within module dependency, I do constructor injection (so that the dependency is clearly visible). Anything that is a service, will be called from outside my module will be through a DiC or ServiceLocator.
The only argument against the Service Locator pattern is it hides the dependency injection into its configuration and it is not clearly visible.
I use the Array Collection for tasks like these. This Object has various methods that are pretty usefull like clean(), contains() etc.
I tend to store my oneToOne, oneToMany etc. relations in arrayCollections.
Within your Entity you'll have to use a constructor to initiate the collection.
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\OneToMany(targetEntity="yourModule\Entity\yourEntity", mappedBy="yourRelation", cascade={"persist"})
*/
protected $yourCollection;
public function __construct()
{
$this->yourCollection = new ArrayCollection();
}
//once I have the collection I usually create methods like add, remove, etc methods like so:
public function addToCollection($toAdd) {
$this->yourCollection->add($toAdd);
}
public function removeFromCollection($toRemove) {
$this->yourCollection->removeElement($toRemove);
}
//etc.....

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

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.

Categories