Standalone Usage of Doctrine\ODM\MongoDB\Query\Builder - php

doeas anybody use the Query\Builder from Doctrine as Standalone Tool ?
It seems like The Query Manager needs a defined
Class of the requested Document in MongoDB.
If you have a defined Class like:
<?php
namespace Documents;
/** #Document */
class User
{
// ...
/** #Field(type="string") */
private $username;
}
Then you can do the following:
<?php
$user = $dm->createQueryBuilder('User')
->field('username')->equals('jwage')
->getQuery()
->getSingleResult();
Is there a way to do use the Query\Builder without defining the Document Classes ?
Thanks in advance for any help.

You can use the QueryBuilder as soon as you have ClassMetaData.
This class metadata is about mapping class properties to internal information, such as data types, associations, ...
You can define this class metadata without having real classes.
There are different approaches, but one of them would be to use the Doctrine\ODM\MongoDB\Tools\DisconnectedClassMetadataFactory class, and feed related information using yaml or xml mapping.
You will have to configure your DocumentManager with the good classMetadataFactoryName option.

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

Dependency injection and Model entities - the right way ?

My symfony2 application has the following structure:
There is a service data_provider, which gets data from various external sources and represents it as entity objects.
Some objects has relations. Currently I am loading relations in controller or helper-services if needed.
It is not very convenient, sometimes I want to get relations from my entity ojbect. To do this I need access to data_provider service.
I want to implement something like doctrine lazy-loading, what is the right way of doing this ?
Some obvious solutions - to inject data_provider in every entity instacne, or to some static property, or to make some static methods in service, or to use evenet dispatcher, but I don't think it is the right way
Made some research of ObjectManagerInterface as Cerad suggested, and found this peace of code: https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Persistence/PersistentObject.php
PersistentObject implements ObjectManagerAware interface, it has private static property where ObjectManager is stored.
So I ended with this:
class DataProvider
{
public function __construct()
{
...
AbstractEntity::setDataProvider($this);
}
}
abstract class AbstractEntity
{
private static $dataProvider;
public static function setDataProvider() {...};
protected static function getDataProvider() {...};
}
The main purpose of services in Symfony (and not only) is exactly this one - to deliver distinguished functionalitys globally over your project.
In this regard, a single service, in your case - dataProvider, should always deliver a single entity. If you have to deal with multiple entities returned from one data source, wrap the data source deliverer into a service itself, and then define one service per each entity with the deliverer injected into it.
Then you can inject the respective entity services into your controllers.

In phpdoc, define class properties in separate file

I am using a framework that allows adding new components to the framework's base class. Is there any way to document these new methods without changing the frameworks files so I can click through to the method in my IDE.
I highly recommend against trying to inject a subclass. If the framework instantiates the class you're extending directly, you'll need to find a way to get it to use your subclass instead.
NetBeans and PhpStorm (and probably many others) will combine elements from multiple definitions of the same class/interface. This allows you to add properties and methods to any existing class without modifying the original source.
/**
* Framework controller base class.
* Provides helpers via __call().
*/
class Framework_Controller { ... }
Now create a file in your code base that you never require containing the same class definition. Your IDE should still parse it and merge its elements with the class above:
if (false) { // Safety first!
/**
* ACME Co. controller base class.
*
* #method ACME_Model_User getUser Load user via authentication helper
*/
class Framework_Controller { /* nothing to add */ }
}
You can try using an interface and declaring the methods normally instead of with #method. That is what we did with Zend_View since it's already an interface. I haven't tried mixing class with interface to see if PhpStorm likes it.
It depends mainly on IDE you are using. I think you should extend base class and add there some new methods/properties with phpdoc comments. Of course changing framework's files is no solution as you already mentioned
You could extend the original class, redefine the function, with new doc, and call the parent function.
e.g.
class newClass extends originalClass
{
/**
* New PHPDoc
*/
function functionName($a)
{
return parent::functionName($a);
}
}
Try to use #see or inline #link directives.

PHP Pattern - how to retrieve collection of objects

Situation:
There is simple User class which is Doctrine entity (I skipped comments to keep the code short):
class User {
protected $iUserId;
protected $sName;
}
Question:
How to retrieve collection of objects of class User from the controller?
Follow up:
Until now, we were creating methods like getUsers() in User class which gets data from DB, creates User class objects and returns it.
Now, I'm wondering if it's not a better solution to create class like UserCollection which would handle data retrieving and creating User objects? Maybe I should make use of \Doctrine\Common\Collections\Collection class somehow?
What I'd like to accomplish is easy way to handles i.e. where clauses. UserCollection class could have QueryBuilder object on which I could operate from controller like this:
$oUserCollection = new UserCollection();
$oUserCollection->setWhere( 'u.iUserId = 1' );
$aUsers = oUserCollection->getUsers();
...
Please share your thoughts on that topic.
Update:
Doctrine provides a concept of repositories of entities which I think maybe the solution I'm looking for.
You have two options:
If your criterias are simple, eg. you just want to filter on a single property, or you don't need filtering at all, you can use the findBy() and findAll() methods, on the EntityRepository:
// find all users
$repository = $em->getRepository('My\\User');
$users = $repository->findAll();
// find users who are marked as active
$users = $repository->findBy(array('active' => true));
// sort users by age
$users = $repository->findBy(array(), array('age' => 'DESC'));
If you have complex(er) requirements, or your finders will be used from multiple places, you will have to create a custom EntityRepository, and group your finder logic there. You have to specify in the mapping that you want to create your own EntityRepository for this entity, the method for this varies depending on what mapping driver you use (annotation, yaml, xml). For example, in yaml you would have to place this line in your mapping:
RepositoryClass: My\Repository\UserRepository
And then create the file:
namespace My\Repository;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository {
public function findVIPUsers() {
$query = $this->_em->createQuery("Your query here");
return $query->getResult();
// you can also use $this->findBy(), or $this->findAll() here
}
}
And then in your controller, when you call $em->getRepository('My\User') it will return the custom repository you just created.

Do I really need to move/write entities myself with Doctrine 2?

I write my entities in let's say models/ folder.
something like:
namespace Organisation\User;
/**
#Entity
*/
class Customer {
/**
* #Column(type="integer") #GeneratedValue
*/
protected $_id;
}
}
So I'll instantiate my entity with $customer = new \Organisation\User\Customer();
Ok, but if I use doctrine orm:generate-entities library/ it will generate it under the following directory :
library/Organisation/User/Customer.php
ANd that's ok, but if I look at the code, there aren't any of my annotation, and therefore when I try to use it, I get doctrine\ORM\Mapping\MappingException: Class Organisation\User\Customer is not a valid entity or mapped super class. because there aren't any annotation.
So what I need to do is to remove the namespace, generate into the same directory as the entities with metadata inforations are, move to my library folder, and add the namespace to work with.
It looks ugly, do I have missed something?
edit: I forgot to tell that orm:generate-entities doesn't work recursively, then, I can't even use my actual structure within my entities metadata
If you've written your entity classes already, why are you trying to generate them?
They typical way to drive a new project is to write your annotated entity classes, and use orm:schema-tool:create to generate your database schema.
Most examples I've seen do stick the Entities under library/, in some nested directory based on the namespaced name of the class, just as you've described. This is generally a good thing, as it works with the default Doctrine2 autoloader setup.
If you're not trying to fit Doctrine2 onto an already existing database schema, I'd recommend that you simply stick all your entity classfiles someplace like library//Entity/.php, and use orm:schema-tool:update and orm:schematool:update to manage the database for you.
Use the arg --generate-annotations.

Categories