Optimization
You can optimize your process without using Doctrine result caches:
First create a map of years for your identifiers, for example:
$yearsMap = array(); $q = $em->createQuery('SELECT y.id, y.year_name FROM Entities\Year y'); foreach ($q->getScalarResult() as $row) { $yearsMap[$row['year_name']] = $row['id']; }
Also create a department map for their identifiers and divide them into identifiers. This will result in 3 (light) queries. The best place to put this code in a (custom) repository.
Then you can run your loop, but βgetβ the actual objects like this:
$year = $this->em->getReference('Entities\Year', $yearsMap[$this->year[$i]]); $department = $this->em->getReference('Entities\Department', $departmentsMap[$this->branch[$i]]); $division = $this->em->getReference('Entities\Division', $divisionsMap[$this->division[$i]]);
I say "get" because getReference() actually creates a proxy (if it has not already been loaded by the manager entity, but in this case it probably is not). This proxy server has not yet been loaded, so requests are not executed here.
The rest of the code does not need to be changed.
Now when flush() is called, Doctrine will load every single year / department / division only once. This can lead to several requests, depending on how many different years / departments / units are used. Therefore, if all 100 students use different years / departments / departments, you will receive 403 requests (3 for maps, 300 for downloading proxies, 100 for inserting students). But if all 100 students use the same year / department / department, you will receive a total of 106 requests (3 for maps, 3 for downloading proxies, 100 for inserting students).
Optimize another way
Another way is to use the names you collected to get all the objects you need:
$q = $em->createQuery('SELECT y FROM Entities\Year y INDEX BY y.year_name WHERE y.year_name IN(:years)'); $q->setParameter('years', $yearNames); $yearsMap = $q->getResult();
Now you have all the Year objects you need with just one query. You can do the same for departments and departments.
Also pay attention to INDEX BY in the DQL statement: this will make sure that you get an array with year_name as the key, and the object as the value. You can use this directly in your loop like this:
$year = $yearsMap[$this->year[$i]]; $department = $departmentsMap[$this->branch[$i]]; $division = $divisionsMap[$this->division[$i]];
The final result for 100 students will always be 103 requests (3 for cards, 100 for inserting students).
Cache
If you need to run this cycle relatively often, and it creates a database, it makes sense to use the Doctrine result cache . A few things to note:
getReference() does not support caching of results (yet), and the result cache is not used automatically. Therefore, I suggest you add something like this to the repository:
public function findOneYearByName($name) { $q = $em->createQuery('SELECT y FROM Entities\Year y WHERE y.year_name = :year'); $q->setParameter('year', $name); $q->useResultCache(true); return $q->getSingleResult(); }
You probably want to tune the result cache, see the docs for more on this.
Another note: the result cache caches the result obtained from the database before it is hydrated. Therefore, even when using result caches, actual objects are hydrated every time. Nevertheless, I still recommend using cards, but implemented a little differently:
$yearsMap = array(); $departmentsMap = array(); $divisionsMap = array(); forloop (...): if (!isset($yearsMap[$this->year[$i]])) { $yearsMap[$this->year[$i]] = $this->em->getRepository('Entities\Year')->findOneYearByName($this->year[$i]); } if (!isset($departmentsMap[$this->branch[$i]])) { $departmentsMap[$this->branch[$i]] = $this->em->getRepository('Entities\Department')->findOneDepartmentByName($this->branch[$i]); } if (!isset($divisionsMap[$this->division[$i]])) { $divisionsMap[$this->division[$i]] = $this->em->getRepository('Entities\Division')->findOneDivisionByName($this->division[$i]); } $year = $yearsMap[$this->year[$i]]; $department = $departmentsMap[$this->branch[$i]]; $division = $divisionsMap[$this->division[$i]];
This ensures that every single year / department / division is hydrated only once.
PS: Using the result cache for "Optimize another path" will not work as efficiently, because the names of years / departments / units may be different each time the cycle starts. With every name change, the queries change, and the cached results cannot be used.
DBAL
Can I get identifiers for the year, department and division on their behalf directly when inserting data?
You can, but you will not use ORM, but only DBAL. You basically do this:
$connection = $em->getConnection(); $statement = $conn->executeQuery('insert query', array('parameter1', 'etc')); $statement->execute();
I doubt it will be more efficient, because MySQL (or any other provider that you use) will still execute these 3 (auxiliary) queries for each insertion, they just don't "go through the wire." And you do not get any help from ORM, like association management, etc.
However, you can find everything on the topic here .