EDIT:
Further research: Looks like the answer lies on changin default Hydrator for a customized. Doctrine2 allows you to change it just by sending his name as a parameter:
$query->getResult('CustomHydrator');
Dont forget to register it first, in your config.yml file:
doctrine:
orm:
hydrators:
CustomHydrator: \your\bundle\Hydrators\CustomHydrator
My relationship between Blog and Comment entity is one to many. 1 Blog has N Comments
After researching about how to add an extra field to a fetched object in Doctrine 2 I found Aggregate Fields, it is a good article but just talk about to get balance from a single Account, it never says what we should do when working with an array of Accounts, it may sound silly but let me explain my situation.
In my case is not about accounts and entries, is about blogs and comments.
What I'm trying to do is to list a number of blogs and just show how many comments it has without loading any comment information, in other words I want to translate this query to the Doctrine2 World.
'SELECT b.*, COUNT( b.id ) AS totalComments FROM `blogs` b LEFT JOIN comments c ON b.id = c.blog_id GROUP BY b.id LIMIT 8'
and the result that I expect is an array of Blog Objects with a totalComments attribute setted correctly, like this:
array (size=8)
0 =>
object Blog
'id' => int 330
'title' => string 'title blog'
// Added field, not visible in table DB. Came through query COUNT() statement
'totalComments' => int 5
// ... more attributes
1 => ...
//more object blogs
);
I just can't achieve this, the best I could do was this:
Creating and fetching Query:
$qb = $this->createQueryBuilder('b')
->select('b, c')
->addSelect('count(b.id) as nComments')
->leftJoin('b.comments', 'c')
->groupBy('b.id')
return $qb->getQuery()->getResult();
and the result I'm getting is an array of arrays, where position 0 has Blog Object and position "totalComments"
// var_dump($result)
array (size=8)
0 =>
array(2) =>
0 =>
object Blog
'id' => int 330
'title' => string 'title blog'
// ... more attributes
"totalComments" => int 5
1 => ...
);
I also tried to make my own Hydrator but I just started using Doctrine2 and found myself kinda lost.
I hope been enough clear. I can give any other information if needed.
Thanks in advance!
You either have to name the fields you want, or have a mixed result ( like your 2nd example). So for a flat array:
$qb = $this->createQueryBuilder('b')
->select('b.title, b.author')
->addSelect('count(c.id) as nComments')
->leftJoin('b.comments', 'c')
->groupBy('b.id')
return $qb->getQuery()->getArrayResult();
Or a mixed result:
$qb = $this->createQueryBuilder('b')
->select('b')
->addSelect('count(c.id) as nComments')
->leftJoin('b.comments', 'c')
->groupBy('b.id')
return $qb->getQuery()->getResult();
After few days I came up with this solution.
I had to add a totalComments attribute to my Blog Entity Class and his get/set methods, and tweak a bit my getLatestBlogs function:
function getLatestBlogs(){
$qb = $this->createQueryBuilder('b')
->select('b, c')
->addSelect('count(b.id) as totalComments')
->leftJoin('b.comments', 'c')
->groupBy('b.id');
$result = $qb->getQuery()->getResult();
//tweaking original result
foreach($result as $row){
$row[0]->setTotalComments($row['totalComments']);
$blogList[] = $row[0];
}
return $blogList;
}
Doing it this way I finally get a simple array of Blog Objects, and it just took an extra loop.
After this I realized that would be nice to have a general function who can work with any Entity, so I made the next function:
function fixResult($qResult){ //Receives $qb->getQuery()->getResult();
if(is_array($qResult)){
$list = array();
$keys = array_keys($qResult[0]); //Getting all array positions from first row
$object = $qResult[0][0]; //Getting the actual object fetched
foreach($keys as $key){ //Searching for existing set methods in the Object
$method = "set".ucfirst($key);
if(method_exists($object,$method))
$methods[$key] = $method;
}
foreach($qResult as $row){ //Calling set methods for every row fetched and storing into a new array
foreach($methods as $key => $met){
$row[0]->$met($row[$key]);
$list[] = $row[0];
}
}
return $list;
}
else return false;
}
I hope somebody else find it useful.
Related
I am new in Symfony, and I have a problem with an existing application I maintain.
In one of the repositories, there's is a method, that selecting the failed transactions, and the related payment.
Now, they have asked me, to allow filter the transactions, based on the total amount of failed transactions which could be either 1 failed transaction or 2.
What I am trying to do in the query builder, is something like that:
$this
->createQueryBuilder('t')
->join('t.payment', 'p')
->leftJoin( Transaction::class, 'tr', Query\Exprt\Join::WITH, 'p.id = tr.payment')
->groupBy('tr.id');
Until that point everything is fine. The query is executed normally, and I can see the transactions I need.
Now the problem is that I cannot use the following statement:
$this
->createQueryBuilder('t')
// This is the column I need to insert
->addSelect('COUNT(tr.id) AS TotalRecords')
->join('t.payment', 'p')
->leftJoin( Transaction::class, 'tr', Query\Exprt\Join::WITH, 'p.id = tr.payment')
->groupBy('tr.id');
Because the output looks like that:
array:1 [▼
0 => array:2 [▼
0 => Transaction {#1221 ▶}
"TotalRecords" => "1" <- This is the total transactions number I need
]
]
Instead of the output above, I need to have the TotalRecords inside the Transaction Object.
So, Is there a way to achieve that with the query builder? Do you think I do something wrong?
you can just loop over your result set and set TotalRecords on all Transaction objects... and return an array of Transactions, as you probably have hoped. The overhead is minimal but the standard doctrine hydration isn't smart enough
// the following is your query:
$qb = $this
->createQueryBuilder('t')
->addSelect('COUNT(tr.id) AS TotalRecords')
->join('t.payment', 'p')
->leftJoin( Transaction::class, 'tr', Query\Exprt\Join::WITH, 'p.id = tr.payment')
->groupBy('tr.id');
// fetch the results, and instead of straight returning them, "merge"
$results = $qb->getQuery()->getResult();
$return = [];
foreach($result as $row) {
$row[0]->totalCount = $row['TotalCount'];
$return[] = $row[0];
}
return $return; // <-- now an array of Transaction
you also could just not use addSelect but instead having and just use the number of transactions you want to filter by, as a parameter (unless the filtering is done later, in which case that approach won't work)
I'm wondering if there's a clean way to get around this little conundrum... Been getting around it, but finally have time to look into "the right way"!
Doctrine Query Builder Snippet
public function getSprockets(array $id_list, $include_stats = false )
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('p')
->from( 'Entity\Foo', 'p' )
->where('p.id IN ( :pids )')
->setParameter('pids', $id_list);
if( $include_stats )
$qb->addSelect( '(SELECT SUM( a.value ) FROM Entity\Stats a WHERE a.foo_id = p.id AND a.type="bar" ) AS bar_count' );
$res = $qb->getQuery()->getArrayResult();
return $res;
}
As is, this snippet behaves very differently whether addSelect is invoked or no. If it is not there, I get a nice flat array as is expected. If the addSelect is however used ($include_stats is true), a row in $res is quite different, containing:
the entity's parts in a $row[0]
and the addSelect result at the base of the $row, e.g., $row['bar_count']
I realize that listing the columns in ->select('p.id, p.that, p.this') ... gets around the problem, but I don't want to have to maintain these many queries each time the schemata is changed.
Is there a convenient-or-built-in way to get a flat array out of getArrayResult?
Thanks!
When I execute something like this in Doctrine:
$qb = $doctrine
->getRepository('EntityA')
->createQueryBuilder('a')
->addSelect('b')
->join('EntityB', 'b', 'WITH', 'a.b = b')
->getQuery()
->getResult()
I get an array that looks like this:
array(0 => EntityA,
1 => EntityB,
2 => EntityA,
4 => EntityB)
In fact, I get 2 result rows, but an array which has a size of 4. This makes iterating over it for displaying in templates nearly impossible.
I would like a result like this:
array(0 => array(EntityA, EntityB),
1 => array(EntityA, EntityB))
Of course I could create a mapping on EntityA that references EntityB. But even with the possibility to change the loading behavior, LAZY, EAGER etc., it could be desirable to have the possibility to create such relations on the fly.
For example:
In overviews with large amounts of Entities, EAGER loading is needed to prevent excessive amounts of queries. But when I want to display only one Entity and do not need the extra data, LAZY loading is more desirable.
Since you have EntityB mapped as EntityA.b, as indicated by this relation:
...join('EntityB', 'b', 'WITH', 'a.b = b')...
Thus you do not need in fact to add b to the select as you are loading it either eagerly or lazily.
Remove ->addSelect('b') from your query builder and use EntityB trough your EntityA.b mapping.
Example:
$as = $doctrine
->getRepository('EntityA')
->createQueryBuilder('a')
->join('EntityB', 'b', 'WITH', 'a.b = b')
->getQuery()
->getResult();
foreach($as as $a){
echo "EntityA property".$a->id;
echo "EntityB property".$a->b->id;
}
This query will return an ArrayCollection of EntityA.
Note: The example above assumes that EntityA.b is a public property.
Update:
After some digging I found this older post: Doctrine 2 QueryBuilder add multiple select elements /parameters?. According to it you can get the result you wan by directly separarating the entities with commas instead of using the ->addSelect() method.
Try the following DQL:
$query = $em->createQuery("SELECT a, b FROM EntityA a JOIN a.b b")
The query above will dynamically eagerly fetch b.
I have a database table that generally (because of a NDA) has the structure of:
Category:
id(int) parent_id(int) title(string) description(text)
Note that I cannot change the table's schema, so that's not a possible solution to my problem.
I need to be able to get all of these categories' info, including a list of all their children, but I'm not quite sure how to do it. I'm fairly certain that I'll need to use recursion, but I'm not sure if there's anything built into Doctrine that can aid in it, or if I need to write raw DQL or even SQL. I currently have the following in my Symfony controller:
$em = $this->get('doctrine')->getManager();
$categoryQuery = $em->getRepository('Acme\MyBundle\Entity\Category')->findBy(array(), array('sortPosition' => 'asc'));
$categories = array();
foreach ($categoryQuery as $category) {
$categories[] = array(
'id' => $category->getId(),
'parent' => $category->getParent(),
'title' => $category->getTitle(),
'description' => $category->getDescription(),
);
}
There's another wrinkle: I need to output this info in JSON.
Ultimately, I'm not sure
A. How to create a query that will get me both a category and all of its child info (which can have more children)
B. How to then output that info as JSON
Any ideas would be greatly appreciated
If you have the category <> category relation defined in doctrine:
$categoryQuery = $em->getRepository('Acme\MyBundle\Entity\Category')
->createQueryBuilder('c')
->leftJoin('c.subCategories sc') // assuming your category <> category relation is called "subCategories
->getQuery()
->getResult()
;
Then in each record of $categoryQuery you will have a Category and its children in ->subCategories (you dont really need the join there)
If you dont have a relation defined in doctrine, then:
$query = $em->createQuery(
'SELECT c
FROM AcmeMyBundle:Category c
LEFT JOIN AcmeMyBundle:Category sc on sc.parent_id = c.id
ORDER BY sortPosition ASC'
);
$categories = $query->getResult();
And return them as JSON
return new JSONResponse($categories);
A. Recursive selection is ... hard/impossible without a path.
See : MySQL parent children one query selection
B. You can use this in Symfony2 :
Symfony\Component\HttpFoundation\JsonResponse
I have 3 tables: user, user_followers and blog_posts.
Users can follow other users and users are related to blog_post by user_id.
I need to get all blog posts that people I follow have written.
I tried something like:
$followedUsers = $user->getFollowedByMe(); //This one works
$posts = $entityManager->getRepository('<BundleHere>:BlogPosts')
->findBy(array('user_id' => $followedUsers));
And I tried a lot more variations but can't figure it out. Maybe someone knows a better way to search by multiple objects not just one.
You can use this kind of code in your BlogRepository.php in example.
public function getBlogPost($userId)
{
return $this
->_em
->createQueryBuilder('p')
->leftJoin('p.user', 'u')
->where('u.id = :id')
->setParameter('id', $userId)
->getQuery()
->getResult();
}
createQueryBuilder('p') will automatically create the select and from (select entity (post ?) from table).
Then, you can use it like this :
$posts = $entityManager->getRepository('<BundleHere>:BlogPosts')->getBlogPost($userId);
I can't give you the exact query because we don't have enough informations about your entities. But this way, you can write nice queries to get exactly what you want.
You can do:
$posts = $entityManager->getRepository('<BundleHere>:BlogPosts')
->createQueryBuilder('b')
->whereIn('b.user', $followedUsers)
->getQuery()
->getResult();
'user' should be the name of property used to hold the user in the Blogpost object.
So i figured it out (thanx guys for pointing me in the right direction with queryBuilder).
$followedByMe = $user->getFollowedByMe(); //Getting users i follow
$followedIds = $followedByMe
->map(function( $obj ) { //Using map method to create an array of id's for all followers
return $obj->getId(); //Value to put into array (in this case id)
})->toArray(); //Create an array and assign it to $followedIds variable
$qb = $em->getRepository('<BundleHere>:BlogPosts')->createQueryBuilder('b');
$posts = $qb->where($qb->expr()->in('b.user', $followedIds ))
->orWhere('b.user = :my_id')->setParameter('my_id', $user->getId()) //Get my posts too
->getQuery()
->getResult();