3

So recently I have been thinking and can't find a solution yet to this problem since my lack of development with doctrine2 and symfony query builder.

I have 2 tables: Goals: id,user_id,target_value... Savings: id,goal_id,amount

And I need to make a select from goals (all the informations in my table are from the goals table, except that I need to make a SUM(amount) from the savings table on each goal, so I can show the user how much did he saved for his goal)

This is the MySQL query:

    select 

    admin_goals.created,
    admin_goals.description,
    admin_goals.goal_date,
    admin_goals.value,
    admin_goals.budget_categ,
    sum(admin_savings.value) 

    from admin_goals 
inner join admin_savings on admin_savings.goal_id=admin_goals.id 
    where admin_goals.user_id=1 
    group by admin_goals.id

It returns what I want but I have no idea how to implement it with doctrine or query builder, can you please show me an example in both ways? enter image description here I highly appreciate it ! enter image description here

doglover1337
  • 146
  • 4
  • 18
  • You must define your Entity properly. You can follow this: http://stackoverflow.com/questions/18939112/symfony2-doctrine-custom-repository-class-semantical-error-line-0-col-14-near – Sharif Mamun Apr 01 '15 at 18:03

2 Answers2

2

I am going to assume you need this fields only and not your AdminGoals entity. On your AdminGoalsRepository you can do something like this:

public function getGoalsByUser(User $user) 
{
    $qb = $this->createQueryBuilder('goal');
    $qb->select('SUM(savings.value) AS savings_value')
       ->addSelect('goal.created')
       ->addSelect('goal.description')
       ->addSelect('goal.goalDate')
       ->addSelect('goal.value')
       ->addSelect('goal.budgetCat') //is this an entity? it will be just an ID
       ->join('goal.adminSavings', 'savings', Join::WITH))
       ->where($qb->expr()->eq('goal.user', ':user'))
       ->groupBy('goal.id')
       ->setParameter('user', $user);

    return $qb->getQuery()->getScalarResult();
}

Keep in mind that the return object will be an array of rows, each row is an associated array with keys like the mappings above.

Edit

After updating the question, I am going to change my suggested function but going to leave the above example if other people would like to see the difference.

First things first, since this is a unidirectional ManyToOne between AdminSavings and AdminGoals, the custom query should be in AdminSavingsRepository (not like above). Also, since you want an aggregated field this will "break" some of your data fetching. Try to stay as much OOP when you are not just rendering templates.

public function getSavingsByUser(User $user)
{
    $qb = $this->createQueryBuilder('savings');
    //now we can use the expr() function
    $qb->select('SUM(savings.value) AS savings_value')
       ->addSelect('goal.created')
       ->addSelect('goal.description')
       ->addSelect('goal.goalDate')
       ->addSelect('goal.value')
       ->addSelect('goal.budgetCat') //this will be just an ID
       ->join('savings.goal', 'goal', Join::WITH))
       ->where($qb->expr()->eq('goal.user', ':user'))
       ->groupBy('goal.id')
       ->setParameter('user', $user);

       return $qb->getQuery()->getScalarResult();
}

Bonus

public function FooAction($args) 
{
    $em = $this->getDoctrine()->getManager();
    $user = $this->getUser();
    //check if user is User etc depends on your config
    ...

    $savings = $em->getRepository('AcmeBundle:AdminSavings')->getSavingsByUser($user);

    foreach($savings as $row) {
        $savings = $row['savings_value'];
        $goalId =  $row['id'];  
        $goalCreated = $row['created'];
        [...]
    }
    [...]
}
stevenll
  • 1,025
  • 12
  • 29
  • I get an: Attempted to call method "createQueryBuilder" on class "AppBundle\Entity\Goal". Controller: $x=$goal->getGoalsByUser($user); // $goal=new Goal(); ... in I put your function in the Goal Entity, am I doing something wrong? – doglover1337 Apr 01 '15 at 15:19
  • As I have said, this has to be on your `AppBundle\Entity\GoalRepository`. If you don't have one, you can [generate](http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes) it. – stevenll Apr 01 '15 at 15:23
  • Thank you for your answer, I finally generated my repository and run the function.. i get an: [Semantical Error] line 0, col 173 near 'savings WHERE': Error: Class AppBundle\Entity\Goal has no association named adminSavings .. this basically means that i don't have a adminsavings(this is the sum) field in the table, but i don't want to alter my table structure for goals, what if i want to show 3-4 sums() from other tables, do i need to create a table collumn for each sum() i want to show? – doglover1337 Apr 01 '15 at 16:15
  • See updated, `savings_value` crashed with `savings` from join. Also, many of the names here I just thought of from your query. Make sure to write them as you entity properties are named – stevenll Apr 01 '15 at 16:55
  • What do i have to write on this line ? ->join('goal.adminSavings', 'savings', Join::WITH)) ?? I dont have admin_savings in goal .. I have goal_id in the savings table.. so many savings are linked to one goal – doglover1337 Apr 01 '15 at 17:03
  • Can you update your question with your entities mappings? At least with the parts you're trying to fetch – stevenll Apr 01 '15 at 17:22
  • SQLSTATE[42S02]: Base table or view not found: 1146 Table 'db.Saving' doesn't exist .. I have the table name admin_savings and the Entity: Saving ...can I rewrite this option somewhere or do I have to rename my database tables? ... my declration in Saving Entity is : /** * @ORM\Entity * @ORM\HasLifecycleCallbacks * @ORM\Table(name="admin_savings") */ – doglover1337 Apr 01 '15 at 18:52
0

If you use createQuery(), then you can do something like this:

    $dqlStr = <<<"DSQL"
select 
admin_goals.created,
admin_goals.description,
admin_goals.goal_date,
admin_goals.value,
admin_goals.budget_categ,
sum(admin_savings.value)
from admin_goals 
inner join admin_savings on admin_savings.goal_id=admin_goals.id 
where admin_goals.user_id=1 
group by admin_goals.id
DSQL;

    $em = $this->getDoctrine()->getManager();
    $query = $em->createQuery($dqlStr);
    $query->getResult();

On the other hand, if you would like to use createQueryBuilder(), you can check this link: http://inchoo.net/dev-talk/symfony2-dbal-querybuilder/

Sharif Mamun
  • 3,508
  • 5
  • 32
  • 51
  • I tried your sollutiuon too but it doesnt work, I initially get a sintax error, no quotes ? Or isn't there a need for them? If i put quotes I get : Attempted to call method "createQuery" on class "Doctrine\DBAL\Connection". Did you mean to call "createQueryBuilder"? .... I just want to run a simple query, jeez.. \ – doglover1337 Apr 01 '15 at 16:53
  • I didn't mean to call createQueryBuilder(). For complex queries, using QueryBuilder can create a mess and you might waste your time. I added '$em = $this->getDoctrine()->getManager();' and updated! – Sharif Mamun Apr 01 '15 at 17:28
  • ok i got it running but i have this error : [Semantical Error] line 0, col 151 near 'admin_goals inner': Error: Class 'admin_goals' is not defined. .. it appears the error is on the admin_goals inner join.. part – doglover1337 Apr 01 '15 at 17:47
  • That is one of the way how people write multi-line string: http://stackoverflow.com/questions/1848945/best-practices-working-with-long-multiline-strings-in-php. One thing you can try is, don't put any space/tab before the lines as I updated! – Sharif Mamun Apr 01 '15 at 17:47
  • It means something is wrong with your raw sql query! – Sharif Mamun Apr 01 '15 at 17:50
  • I had run the same query copy paste in mysql(phpmyadmin) and worked.. but on the page I get that error.. – doglover1337 Apr 01 '15 at 17:58
  • Dropped a comment under your question, you are not getting admin_goals from Doctrine. You should have something like: 'from SOMEBundle:Project admin_goals' – Sharif Mamun Apr 01 '15 at 18:06