Recursive function in doctrine - recursion

I have a entity like this:
Person
- id_person
- name
- child //self refering
If I query person entity in a recursive funcion it only returns me the first three levels. Do you know how to tell to doctrine that I want to get deep as many levels is required?
Edit
What was the problem?
Using $queryBuilder->getquery()->getResult(Query::HIDRATE_ARRAY); is not the best way to make a recursive function, it should use the default hydration.
How I resolve it?
Using $queryBuilder->getquery()->getResult(); and call the getters as #Andreas Linden say: $parent->getChild()

all associations will be lazy loaded when you access them from within your model 8if you don't define it to behave differently). so always use getter methods like getChild() which simply does return $this->child;
you can also left-join as many levels as you require so you won't generate thousands of queries (a single query for each child)
$query = $entityManager->createQueryBuilder()
->select('p1, p2, p3, p4, p5, p6')
->from('Entity\Person', 'p1')
->leftJoin('p1.child', 'p2')
->leftJoin('p2.child', 'p3')
->leftJoin('p3.child', 'p4')
->leftJoin('p4.child', 'p5')
->leftJoin('p5.child', 'p6')
->createQuery();

Related

Conditional has()

I am building a query based on passed parameters. For example, if pass p1='a' and p2='b', my query will look something like this:
g.V()
.has("p1","a")
.has("p2","b")
If let's say p2 is not passed, then I won't have a second check:
g.V()
.has("p1","a")
Is it possible to perform parameter check inside the query instead of creating conditional checks for parameters before creating query?
Edit: Use case is based RESTful web service where I have something like, /server/myEndpoint?p1=a and endpoint implementation would build gremlin query with .has() steps solely based on presence of p1 or p2, so if p2 is not passed, query would look like in second snippet, and if passed would look like the one in first.
One possible approach is to build GraphTraversal until it's not executed:
Map<String, String> map = new HashMap<>();
map.put("p1", "a");
map.put("p2", null);
final GraphTraversal<Vertex, Vertex> v = g.V();
map.forEach((k,v) -> {
if(v != null) {
v.has(k,v);
}
});
return v.toStream().collect(Collectors.toList());
As a suggested improvement where you are creating the search, the Gremlin-optimized way to approach this would be through a parameterized script. In theory this would mean that you are building the query in a way which is looking for each parameter.
However, I don't believe there is an inherent "if" statement, per-say, so if you wanted to use the query to optionally handle your "Has" cases, it could be done with an "Or Step", an "And Step", or a "Choose Step". I would discourage it as a pattern, though. It is similar to how MySQL has the options to handle cases in Select queries: it would almost always be more performant and a better separation of concerns to bind the parameters right in the first place than to have the query builder sort out the programmatic logic.

Trying to add virtual property to query result in Symfony2 with JMS

I have a simple task ahead of me, yet I find myself pretty much incapable of completing it.
My model is pretty complex, so I'll try to simplify for the sake of being specific.
I have two entities, Call and Caller, with entity repositories that I access via custom services. I am using JMS Serializer Bundle. All entities are mapped correctly, the the whole thing is working pretty fine. But I have this idea that I just can't make happen. To the point...
These are my entities described:
--Call--
#call_id
location
date_created
Caller
--Caller--
#caller_id
phone_num
The idea is to have a list of all calls with their fields for example:
New York, 2015.12.12. 20:07:06, Novak Djokovic, 3816976548 [YY]
London, 2015.12.13. 20:07:06, Jelena Jankovic, 3811116333 [XX]
Fields YY and XX represent the number of calls already in a database with that specific number.
I have a query that returns the list without YY and XX values, and I also have a separate query that returns number of calls from a specific number. The thing gets complicated when I try to join them. Not sure how to do that.
I read about VirtualProperty annotation for JMS, but failed to actually see how to use it this time (since it's not a good practice to access your repository or service from an Entity).
These are my methods:
1 - get list of all calls with callers
public function findAllCalls()
{
$callerAlias = "c";
//getAlias method returns the alias of the current entity (call)
$qb = $this->createQueryBuilder($this->getAlias());
$qb->leftJoin($this->getAlias() . '.caller', $callerAlias);
return $qb->getQuery()->getResult();
}
2 - get number of calls from a specific number based on a call as a parameter
public function getNumberOfCalls(Call $call) {
$callerAlias = "c";
$qb = $this->createQueryBuilder($this->getAlias());
$qb->leftJoin($this->getAlias() . '.caller', $callerAlias);
$qb->select("COUNT(" . $this->getAlias() . ".call_id)");
$qb->where($callerAlias.".phonenbr = ".$call->getPhoneNumber());
return $qb->getQuery()->getScalarResult();
}
Hoping to hear your opinions on this, 'cause I really struggled to find the sollution.

How to prevent Doctrine from lazy loading one to one relationsip?

EDIT: If you're having similar issues This Topic will be of interest to you
I have User and UserSettings with one to one bidirectional relationship. It appears that even if i do not use any UserSettings values in my page, doctrine lazy loads it anyways.
Is this expected behavior? Why is Doctrine fetching this data even though I'm not using it in my page? If I'm unable to stop it, I would have to join this UserSettings to User every time I retrieve user object, but this is so unnecessary.
What can I do to prevent this from happening?
Code that loads data:
->createQuery('SELECT p, u, s FROM TestPostBundle:Post p LEFT JOIN p.user u LEFT JOIN p.sub s WHERE p.id IN (:ids)')
->setParameter('ids', $ids)
->getResult();
The in twig I loop through posts and display Post data and associated user, but I never request any UserSettings variables, I'm not accessing them at all.
I've seen this question asked in a few places and am adding my answer here:
I came across this same problem and remember that the symblog tutorial gave an example of how to reduce the lazy loading by explicitly add left joins on the tables that you do not need. It seems strange to include tables on a join when you do not even want that data at all, but this way you will reduce all of those extra queries down to 1 and it does run faster.
Search for lazy loading - about 1/5 of the way down http://tutorial.symblog.co.uk/docs/customising-the-view-more-with-twig.html
To fix this for the user/userdata issue try adding this to the user repository and use to whenever you need to get all users even if you do not want userdata. It can be further enhanced by selecting partial: ->select('partial p.{user_id,name,}')
public function getAll($limit = 500) {
$qb = $this->createQueryBuilder('u')
->select('u', 'd')
->leftJoin('p.userdata', 'd')
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()->getResult();
}
UPDATE
The symblog tutorial seems to be down and I'm leaving the link here for the time being in case its temporary. The relevant code is here in the answer.
I also faced same problem. It seems that when querying from inverse side doctrine also queries the owning side. See this discussion.

getEntityManager() and getDoctrine() in Symfony2

Is there any difference between these two statements:
$this->getDoctrine()->getEntityManager()->getRepository();
$this->getDoctrine()->getRepository();
Does the difference relate to any OOP concept I am missing out?
In general, no difference, since
$this->getDoctrine()->getRepository();
is just a helper for
$this->getDoctrine()->getEntityManager()->getRepository();
You can have several entity managers, and then there will be a slight difference in getting a repository from one:
$this->getDoctrine()->getRepository($entityName, $enityManagerName);
$this->getDoctrine()->getEntityManager($entityManagerName)->getRepository($entityName);
But again, no difference in the result you'll get.
All other things being equal, I'd go with the shortest one.
The result is the same, but if you need the entityManager for more than just getting the repository, it might be handy to store it and then recieve the repository as well as do other operations such as flush:
$_em = $this->getDoctrine()->getEntityManager();
$repository = $_em->getRepository();
//...
$_em->flush();
As said before, if you only need to get the Repository, go with the second statement, which is shorter and as easy to read as the first one.
There is no difference. If you look at the source code of AbstractManagerRegistry.php. You can see this code:
public function getRepository($persistentObjectName, $persistentManagerName = null)
{
return $this->getManager($persistentManagerName)->getRepository($persistentObjectName);
}
As you can see, when you call getRepository(), it first calls getManager() and then getRepository(). I would suggest using the second one as it gives intellisense in IDEs such as PHPStorm. Hope it helps.

Deleting items in foreach

Should you be allowed to delete an item from the collection you are currently iterating in a foreach loop?
If so, what should be the correct behavior?
I can take quite a sophisticated Collection to support enumerators that track changes to the collection to keep position info correct. Even if it does some compromisation or assumptions need to be made. For that reason most libraries simply outlaw such a thing or mutter about unexpected behaviour in their docs.
Hence the safest approach is to loop. Collect references to things that need deleting and subsequently use the collected references to delete items from the original collection.
It really depends on the language. Some just hammer through an array and explode when you change that array. Some use arrays and don't explode. Some call iterators (which are wholly more robust) and carry on just fine.
Generally, modifying a collection in a foreach loop is a bad idea, because your intention is unknown to the program. Did you mean to loop through all items before the change, or do you want it to just go with the new configuration? What about the items that have already been looped through?
Instead, if you want to modify the collection, either make a predefined list of items to loop through, or use indexed looping.
Some collections such as hash tables and dictionaries have no notion of "position" and the order of iteration is generally not guaranteed. Therefore it would be quite difficult to allow deletion of items while iterating.
You have to understand the concept of the foreach first, and actually it depends on the programming language. But as a general answer you should avoid changing your collections inside foreach
Just use a standard for loop, iterate through the item collection backwards and you should have no problem deleting items as you go.
iterate in reverse direction and delete item one by one... That should proper solution.
No, you should not. The correct behaviour should be to signal that a potential concurrency problem has been encountered, however that is done in your language of choice (throw exception, return error code, raise() a signal).
If you modify a data structure while iterating over its elements, the iterator might no longer be valid, which means that you risk working on objects that are no longer part of the collection. If you want to filter elements based on some more complex notation, you could do something like this (in Java):
List<T> toFilter = ...;
List<T> shadow;
for ( T element : toFilter )
if ( keep(element) )
shadow.add(element);
/* If you'll work with toFilter in the same context as the filter */
toFilter = shadow;
/* Alternatively, if you want to modify toFilter in place, for instance if it's
* been given as a method parameter
*/
toFilter.clear();
toFilter.addAll(shadow);
The best way to remove an item from a collection you are iterating over it to use the iterator explitly. For example.
List<String> myList = ArrayList<String>();
Iterator<String> myIt = myList.iterator();
while (myIt.hasNext()) {
myIt.remove();
}

Resources