Doctrine MongoDB Referencing and Inheritance - php

I am having problems with Doctrine MongoDB ODM. Scenario is very simple (I think).
I have set of documents with COLLECTION_PER_CLASS inheritance model (Base, Product, etc). This all works well and I can persist objects as usual.
/**
* #ODM\Document(collection="content_base")
* #ODM\InheritanceType("COLLECTION_PER_CLASS")
*/
class Base {
/**
* #ODM\Document(collection="content_products")
*/
class Product extends Base {
Now, I have another Document (Item) and I want to create ReferenceOne to inherited Documents:
/**
* #ODM\ReferenceOne(targetDocument="Base")
*/
private $content;
And this is where I am having problems, because Doctrine ClassLoader complains about class Base being redeclared:
Fatal error: include(): Cannot redeclare class content\documents\types\base in /vendor/composer/ClassLoader.php on line 183
I am a bit confused why would this be a problem, or otherwise what is correct way of doing this?
UPDATE
I have just noticed, if I remove 'targetDocument' from ReferenceOne annotation I get results I wanted. Is this the correct way to do it?
Thanks,
Greg

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

Doctrine2 Entity Manager cannot find Custom Repository Class in namespace

I have Silex setup with Doctrine2 ORM. I am trying to build a pagination class that I can use with my entities. I am well aware of the existing pagination classes that exist within Doctrine2 but because this project is for my school research I am trying to create this component myself.
Below is the fatal error I get when accessing this page:
Fatal error: Class 'PlayGround\Model\Helper\UserRepository' not found in D:\web\playground-solutions\vendor\doctrine\orm\lib\Doctrine\ORM\EntityManager.php on line 689
I have defined an interface called PaginateableInterface with two methods count and paginate. I went on to define a custom EntityRepository class that extends Doctrine\ORM\EntityRepository. Below is my custom EntityRepository.
<?php
namespace PlayGround\Service\Doctrine;
use Doctrine\ORM\EntityRepository as ParentEntityRepository;
class EntityRepository extends ParentEntityRepository{
public function count(){
$em = $this->getEntityManager();
$builder = $em->createQueryBuilder();
/**
* ToDo: #entity
*
* Still need to find a better way of getting entity class name.
*/
$entity = $em->getClassMetadata(get_class(__CLASS__))->getName();
//Dynamically get a count of records on any entity we happen to call this on.
$builder->select($builder->expr()->count('e'))
->from($entity, 'e');
$query = $builder->getQuery();
//Try-Catch block ommitted
return $query->getSingleScalarResult();
}
}
<?php
namespace PlayGround\Model\Helper;
use PlayGround\Service\Doctrine\EntityRepository as CustomRepository;
use PlayGround\Contract\PaginateableInterface as IPaginate;
class UserRepository extends CustomRepository implements IPaginate
{
}
In my understanding this should suffice as the count and paginate methods are sitting within the custom repository.
Inside my Paginator class I call the entity I want to paginate as shown below:
<?php
//Paginator class
$model = $this->getModel($model);
//Count should be inherited from CustomRepository aliased object.
$totalRecords = $model->count();
Below is another pierce of meet with regards to this where I add an annotation to my model to point it to the repository class it is suppose to use.
<?php
namespace Application\Model\Entity;
use Doctrine\ORM\Mapping as ORM;
use Application\Model\Entity\UserGroup;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
* #ORM\Entity(repositoryClass="PlayGround\Model\Helper\UserRepository")
*/
class User{ /* Rest of the code goes here... */ }
Given all this setup what could I have missed in getting this to work? I have even ran two commands on my doctrine console but that didn't help either.
Luyanda.Siko#ZACT-PC301 MINGW64 /d/web/playground-solutions
$ php app/Console/bin/doctrine.php orm:clear-cache:metadata
Clearing ALL Metadata cache entries
Successfully deleted cache entries.
Luyanda.Siko#ZACT-PC301 MINGW64 /d/web/playground-solutions
$ php app/Console/bin/doctrine.php orm:clear-cache:query
Clearing ALL Query cache entries
Successfully deleted cache entries.
EDIT:
Below is my file structure found in D:\web\playground-solutions.
You declare twice #ORM\Entity. Once with the repositoryClass and once without. Remove the one without:
#ORM\Entity
and leave this:
#ORM\Entity(repositoryClass="PlayGround\Model\Helper\UserRepository")
#ORM\HasLifecycleCallbacks should be declared without parentheses ()...
Also make sure that the EntityRepository is in the correct namespace and the corresponding folder:
your namespace is PlayGround\Model\Helper\UserRepository meaning the file should be in folder PlayGround\Model\Helper and the class file name should be UserRepository.php.
Fix and check that and if it still doesn't work leave a comment.
UPDATE:
Your UserRepository is in the wrong module. Is now in app should be in PlayGround
The file should be in:
src/PlayGround/Model/Helper/UserRepository.php
It's all about that problem.
Clearly this has really consumed my thought process. All I was doing was pointing to an incorrect namespace as pointed out by #Witt.
I changed my annotation entry in the User entity and the error went away.
<?php
/** #ORM\Entity(repositoryClass="Application\Model\Helper\UserRepository") */
Thanks you guys.

Symfony2 & PHP Traits with entities relation embedded

In Symfony2, I just try recently to think in terms of traits, to create some sort of behaviors.
Let's say I have an address attribute in an entity. I externalized attributes, getters and setters related to this in an AddressableTrait.
But what if address become an entity? I started to try to define my OneToMany relation in my trait, as if it was in a regular entity :
use Doctrine\ORM\Mapping as ORM;
class AddressableTrait {
/**
* #var
* #ORM\OneToMany(targetEntity="XXXX\GlobalBundle\Entity\Address", inversedBy="What to put here" )
*/
protected $addresses;
/**
* #return ArrayCollection
*/
public function getAddresses()
{
return $this->addresses;
}
/**
* #param ArrayCollection $addresses
*/
public function setAddresses($addresses)
{
$this->addresses = $addresses;
}
}
What to put in the inversedBy? The purpose of the trait if precisely to embed all the behavior feature, so I think that at least using traditionnal annotation/YML/XML,it's not possible to achieve.
I digged a bit into it and found this very interesting link that seems to allow you to defines relation via events, but there is still logic to add to "finish" relations.
UPDATE :
Using the above link, I managed to created dynamic ManyToMany relation. the schema update works when creating, but if I comment the dynamic relation, a schema:update --dump-sql doesn't remove it. It seems to work add-only. Any clue to force the dynamic mapping to stick to the real relations addition/removal?
Thanks a lot for your answers !
Nicolas
I encountered a problem using traits in entities. For regular database values (scalar, DateTime) traits worked fine, but when I tried to define entity relations in traits the doctrine migrations bundle would convert the property to a varchar field.
The only way I could find to fix creating proper entity relation properties was by moving them out of the trait and into the entity class itself.

Doctrine - PHP | How to Override EntityRepository existent methods?

I've created a superclass for my entities:
class superclass{
/** #column(type="string", length=1,
* options={"default":"c"})
*/
protected $status;
/** ...more things **/
}
The next step is create my own repository, so it replace EntityRepository. The aim is set the status value depending of:
Entity is removed - 'd'
Entity is modified - 'm'
Entity is created - 'c'
So I need to override methods from EntityRepository: find(), findBy(), delete(),...
I didn't found any work related. Could it not be good idea?
Any clues?
Thanks in advance
Exists a project with similar dataflow. https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/softdeleteable.md
With simple annotations it creates and manages the deleted fields and extract the not deleted fields using the defaults EntityManager methods.
You can create your own filters for the Doctrine's operations:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-associations.html

Symfony: Convert two entities that extend the same class

Maybe there are already answers regarding this topic but I cannot find any information. If there are answers already, just post any links.
My question is:
I work with Symfony 2.5 and doctrine 2
A common problem I have is that I have an entity that I want to persist to different tables in the database. For this I usually create a superclass and extend it in two concrete entities that I persist to the database.
Now I have two entities deriving from the same base class. Is it possible to convert from one to another. Of course I have to persist them to the database separately.
One example. I have two classes: "quote" and "order" which are basically the same and extend the superclass "project"
/**
* #ORM\MappedSuperclass
*/
abstract class project {
/**
* #ORM\Column(type="text", nullable=true)
*/
private $description;
/**
* #ORM\ManyToOne(targetEntity="Bundle\Customer")
*/
private $customer;
/**
* #ORM\ManyToOne(targetEntity="Bundle\CustomerContact")
*/
private $customercontact;
... (getters/setters etc.)
}
class quote extends project { ... }
class order extends project { ... }
Now once a quote is confirmed I want to "convert" it to an order and keep all the base data (stored in the "project" part). Also I want to keep the quote itself so I kind of want to create a new order from the quote instead of using the "project" directly and just set a status there.
Of course I could write a constructor or something but I am searching for a way to clone that part of an object.
Maybe there is an alternative way where the class is not extended but the "project"-part is used within the class itself. Not sure but it seems to me like a lot of people could have run into such a problem already and there might be a smart solution for this kind of situation.
I'm happy with any links to docs too if applicable.

Categories