Reputation: 2083
I'm building a product management tool where the product
can have an arbitrary number of attributes
, documents
, features
, images
, videos
as well as a single type
, brand
, and category
. There are a few other related tables, but this is enough to demonstrate the problem.
There's a Model class called ProductModel
that contains a method like this (reduced for clarity):
public function loadValues() {
//Product entity data
$this->id = $this->entity->getId();
$this->slug = $this->entity->getSlug();
// One of each of these
$this->loadType();
$this->loadBrand();
$this->loadCategory();
// Arbitrary number of each of these
$this->loadAttributes();
$this->loadDocuments();
$this->loadFeatures();
$this->loadImages();
$this->loadVideos();
...
}
Each of the load methods does some boiler plate that eventually executes this method:
public function loadEntitiesByProductId($productId=0) {
// Get all the entities of this type that are associated with the product.
$entities = $this->entityManager
->getRepository($this->entityName)
->findByProduct($productId);
$instances = array();
// Create a Model for each entity and load the data.
foreach ($entities as $entity) {
$id = $entity->getId();
$instances[$id] = new $this->childClass();
$instances[$id]->entity = $entity;
$instances[$id]->loadValues();
}
return $instances;
}
This is OK for cases where the related entity is a single table, but usually it's a mapper. In those cases, I get all the mapper entities in the first query then I have to query for the related entity within the loadValues()
method (via Doctrine's get<Entity>()
method). The result of this process is a huge number of queries (often >100). I need to get rid of the extraneous queries, but I'd like to do so without losing the idioms I'm using across my data models.
Is there a way to get the entityManager to do a better job at using joins to group these queries?
Upvotes: 0
Views: 822
Reputation: 2083
There were a couple problems with my previous approach:
First, I was getting the entities from the repository instead of loading them from the existing entity:
$entities = $this->entityManager
->getRepository($this->entityName)
->findByProduct($productId);
Better is:
$method = $this->deriveGetMethod($this->entityName);
$entities = $productEntity->$method()
Second, I was retrieving the product entity using $this->entityManager->getRespository...
which works fine for loading small data sets (a single table or one or two relations), but there's no way to get the repository's findBy
methods to load relations in a single query. The solution is to use the queryBuilder.
$qb = $this->entityManger->createQueryBuilder();
$query = $this->select('product',/*related tables*/)->/*joins etc.*/
$productEntity = $query->getSingleResult();
Upvotes: 1