i have a problem, using symfony2, doctrine
first my models and relationships:
tables:
1.company (has many employements, has many projects)
2.user (has many employements)
3.project (has one company)
4.employement (has one user, has one company)
(I know about the possibility ManyToMany between user<->company, but i go with an extra model for employement for other reasons)
The Issue: an the projects/index page, i want to show all projects, from all companies, where this user is employed, ordered by created at. As you can see a user can be employed in more companies at the same time.
In my controller i get all projects like this:
$projects = $this->getDoctrine()
->getRepository('AcmeDemoBundle:Projects')
->findAll();
I Guess the solution could be:
find employements by user
find companies by that employements
get projects by that companies...
Any Idea?
Your solution seems right, but you will probably need to create a custom repository class with joins to explicitly tell Doctrine to fetch relations (without proxy).
See Databases and Doctrine (The Symfony Book) and Doctrine Query Language — Doctrine 2 #joins
Example :
use Doctrine\ORM\EntityRepository;
class EmployementRepository extends EntityRepository
{
public function findWithJoins($user) {
$qb = $this->createQueryBuilder('e');
$qb->leftJoin('e.company', 'e')
->addSelect('e')
->where('e.user = :user')
->setParameter('user', $user);
return $qb->getQuery()->getResults();
}
}
Related
Is it possible to have simple read-only entities, that can have an association with an other doctrine entity, but their data is stored in a text ( YAML ) file ?
Let's say I have a product entity, and I want to set a category for each product. But for now, I only have very few categories ( and don't need to edit or add ), so I don't want/need to create a full doctrine entity with it's own table in the DB.
So I create a very simple entity:
class ProductCategory
{
private $id;
private $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
}
Now I would like to create a simple YAML file where the data is stored, like so:
0: Cheese
1: Meat
2: Dairy Products
....
Then I would like to set a ManyToOne relation from the product entity to the ProductCategory entity.
Is that possible ?
And, how to "query" the categories ? ( let's say I want to list all categories that start with a certain letter )
'Why' you ask ?
Well for now, as I said, I only have very few categories, but maybe some day I want to add many more, and even have a CRUD editor for them and so on, then I could easily convert it to a full doctrine entity.
Or any other suggestions on how to approach this ?
There is already a library that provides what you are looking for that's called Alice:
https://github.com/nelmio/alice
https://github.com/hautelook/AliceBundle
https://github.com/fzaninotto/Faker
https://github.com/h4cc/AliceFixturesBundle
This way you can create random test data en masse and can still work with Doctrine as usual.
If you want to do this manually it will be a pain to solve the problem of connecting the entities. Your best bet is to keep all of them in arrays with id's being used as keys but even then you will probably end up writing lots of glue code to connect the entities.
We are building a CMS and website building platform with a lot of different vendors and clients sites, so there are a lot of different content type entities, which are edited by generic controllers and helper services that don't necessarily (easily) know what the entity manager is for a given entity.
NOTE: We have several entityManagers to separate access to different databases, e.g. Global, Billing, Local, etc
There are many cases where we need to detect what is the entity's EntityManager. For example, we have a MediaHelper that dynamically associates media from a database with matching fields on the entity (this doesn't work with associations because the Media has to connect with literally any entity and you can't have that kind of dynamic association and we don't want a hundred different associations).
The media is in a bundle managed by the 'Local' EntityManager. But the entity may be in a 'Global' EntityManager (you can't assume it's in the same entity manager). So we need to detect and persist the right entity manager for the right entity.
So how do you recommend dynamically detecting the entityManager for an entity?
Original Custom Method
NOTE: the accepted answer is a much better solution. This is just here for archival purposes.
Here is a simple solution that works. But I don't know enough about Symfony and Doctrine to know if it's a bad idea? Does anyone else know? If not, I don't know why this wouldn't be in the core, as a Doctrine Utility.
I created an EntityHelper service that injects the Doctrine service into it:
gutensite_cms.entity_helper:
class: Gutensite\CmsBundle\Service\EntityHelper
arguments:
- "#doctrine"
Then in the entity helper is one simple function to get the Entity Manager for an entity (the config.yml registers the bundles for entity managers already):
/**
* Automagically find the entityManager for an entity.
* #param $entity
* #return mixed
*/
public function getManagerForEntity($entity) {
$className = \Doctrine\Common\Util\ClassUtils::getRealClass(get_class($entity));
foreach (array_keys($this->doctrine->getManagers()) as $name) {
if(in_array($className, $this->doctrine->getManager($name)->getConfiguration()->getMetadataDriverImpl()->getAllClassNames())) return $em;
}
}
NOTE: Doctrine Registry#getAliasNamespace already does something nearly identical to this foreach loop, I just modified the idea to return the entity manager instead of the namespace, e.g.
public function getAliasNamespace($alias) {
foreach (array_keys($this->getManagers()) as $name) {
try {
return $this->getManager($name)->getConfiguration()->getEntityNamespace($alias);
} catch (ORMException $e) {
}
}
throw ORMException::unknownEntityNamespace($alias);
}
Update 10/21/15: Entity Detection Code updated per #Cerad's suggestion.
Per the suggestion of #qooplmao, there is an easy method already in the Doctrine core.
// 1) get the real class for the entity with the Doctrine Utility.
$class = \Doctrine\Common\Util\ClassUtils::getRealClass(get_class($entity))
// 2) get the manager for that class.
$entityManager = $this->container->get('doctrine')->getManagerForClass($class);
Updated 10/22/15 After suggestions from Cerad and Qooplmao
Can you not give the entity manager a alias that suits the context it is for? You talk about Global, Billing, Local, so for example:
'service_manager' => array(
'aliases' => array(
'global_entity_manager' => 'My\Global\EntityManager',
'billing_entity_manager' => 'My\Billing\EntityManager',
'local_entity_manager' => 'My\Local\EntityManager',
),
)
You could also map the entity manager to the namespace of the entity.
So let's say you have a folder for your Global entities that is Global\Entity, then you could alias the entity manager for those entities Global\Entity\EntityManager. The advantage of this solution is that you can map several namespaces to the same entity manager, so your Billing and Global entities can easily share the same entity manager:
'service_manager' => array(
'aliases' => array(
'Global\Entity\EntityManager' => 'My\Global\EntityManager',
'Billing\Entity\EntityManager' => 'My\Global\EntityManager', // <-same name
'Local\Entity\EntityManager' => 'My\Local\EntityManager',
),
)
This only works if your entities in one namespace are manager by the same EntityManager instance. I can hardly believe this would not be the case in any project, but otherwise you should maybe reorganize a bit? :D
I have an entity called tournament to which users can register to participate in.
The relationship between the two entities is ManyToMany and need to create a view of Symfony2 in which list all tournaments, with or without registered users so that they can join.
This is my DoctrineQueryBuilder
$em->createQueryBuilder('d')
->select('d, i, u')
->leftJoin('d.item','i')
->leftJoin('d.users','u')
->where('d.active = 1')
->andWhere('d.state = 1')
->orderBy('d.dateStart', 'ASC');
I also need to get the number of users who have joined the tournament.
Preamble
There are various ways to achieve what you want. You can create a sub-query to do the count, however a simpler solution is to let doctrine handle this for you.
The solution described below is based on Doctrine lazy/eager loading capability. When doctrine loads an entity, it will also populate it's associations, either lazily or eagerly (default is lazy).
Solution
Assuming your Tournament entity maps the users association as a ManyToMany relation. You can create a new method which counts your Tournament->users.
ManyToMany associations would populate the entity property (in this cases $users) with an ArrayCollection.
A method called countUsers would do the trick, example implementation below:
...
class Tournament {
...
/**
* #ManyToMany(targetEntity="User")
* #JoinTable(name="tournament_users",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="tournament_id", referencedColumnName="id"}
* )
**/
private $users;
...
public function countUsers(){
return $this->users->count();
}
In the view when iterating over the collection of tournaments, simply call the $tournament->countUsers() method to display the count.
References:
ArrayCollection: http://www.doctrine-project.org/api/common/2.1/class-Doctrine.Common.Collections.ArrayCollection.html
I'm building an application where users are tied to accounts (as in, multiple users all share an account), then other entities - lets call them products are tied to the accounts. The products are associated with the accounts and only users that are tied to that account can edit the products.
The difference being in my case, there are many different entities being shared in the same model.
If it was just the one (product) entity, it wouldn't be a problem to have a method in my ProductRepository like:
public function checkOwnership($id, $account)
{
$count = $this->createQueryBuilder('s')
->select('count(s.id)')
->where('s.account = :acc')
->andWhere('s.id = :id')
->setParameters(array('id' => $id, 'acc' => $account))
->getQuery()
->getSingleScalarResult();
if($count == 0)
throw $this->createNotFoundException('ye..');
return;
}
To make sure the id of the product is tied to the user's account.
The account argument is passed to this function by using:
$this->getUser();
In the controller.
What is the best way for me to make this function available to the other entities to prevent code duplication? There are (currently) only 3 other entities, so it wouldn't be the end of the world to just have it in each repository, but I'm just wondering if there were a way to create a 'common' or global repository that follows good standards? I'd sure like to know about it.
Edit:
Or have I just completely over-thought this? Can I just create a 'Common' directory in my 'MainBundle', define a namespace and add a use statement at the start of my controllers that need access to the function?
I hope I fully understand your question.
Solution one, duplication, easy: let #ParamConverter do the job (automatic response to 404 if doesn't exist)
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* #Route("/pets/{id}")
*/
public function showAction(Pet $pet)
{
// Current logged user
$currentUser = $this->get('security.context')->getToken()->getUser();
// Owner
$ownerUser = $pet->getUser();
if(!$currentUser->equals($ownerUser)) {
// 401, 403 o 404 depends on you
}
}
Solution two, DRY, a bit harder: use JMSAOPBundle and define an interceptor that intercepts all request to you controller and actions (show, delete, etc.). The pointcut (connected to the interceptor) will get the current user from the context and the owner from the entity. Then you do the check...
I have 3 entities: person, with an ID, employee and applicant.
Entity person have vars employee and applicant, and OneToOne relationship.
Employee and Applicant have a person as an ID, with referencedColumnName="id", and a relationship OneToOne.
Fine, When I create a form with the entity person, to display a input select with all applicants, thanks to fuzzy-loading of Doctrine2 make queries to retrieve each person, each related candidate and related empleado, which means that a bd 2000 people are doing almost 6000 requests bd to display only the name.
If I don't print the form, no problem, but, if I put this in the view:
{{ form_rest(formularioEnlazarCandidato.person) }}
And Doctrine2 execute a lot of querys.
I'm not sure how to fix this, as to show an entity in the form will not let me do only select per.id and per.name.
I had similar problem and they helped me - look here Optimize DQL with double join. Basically you need fetch join in your repository - I did:
class ProfileRepository extends EntityRepository {
public function findAll()
{
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder()
->select("p, stu, sta, pdd, pdt, pdc")
->from('AldenBonBundle:Profile', 'p')
->leftJoin('p.studies', 'stu')
->leftJoin('stu.statute', 'sta')
->leftJoin('p.disabilityDegree', 'pdd')
->leftJoin('p.disabilityType', 'pdt')
->leftJoin('p.disabilityCode', 'pdc')
->orderBy('p.profileId')
;
$res = $qb->getQuery()->getResult();
return $res;
}