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?
I highly appreciate it !
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'];
[...]
}
[...]
}
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/
Related
I am working on a Symfony 2.8 project that allows registered users to manage contacts, documents, appointments, etc.
Now I would like to implement a feature that allows users to reset/delete all their data. So the user account itself should stay intact but all the data entities should be deleted.
Currently I am using a Doctrine/ORM approach to loop though all entities and delete them:
public function deleteAllData($user) {
$this->currentBatch = 0;
$this->deleteEntities('MyBundle:Contact', $user);
$this->deleteEntities('MyBundle:Document', $user);
...
$this->deleteEntities('MyBundle:...', $user);
$this->flushIfNecessary(true);
}
private function deleteEntities($type, $user) {
$repo = $this->em->getRepository($type);
$entities = $repo->findByUser($user);
foreach ($entities as $entity) {
$repo->delete($entity);
$this->flushIfNecessary();
}
}
private function flushIfNecessary($force = false) {
$this->currentBatch++;
if ($force || $this->currentBatch % 100 == 0) {
$this->em->flush();
$this->em->clear();
}
}
While this works fine, it is very slow. Using different batch sizes I was able to speed the process up a little bit, but it still takes quite long to delete a large number of entries. Of course this is due to all the ORM handling that is done in the background.
It would be much faster to use simple SQL queries:
private function deleteEntities($type, $user) {
$tableName = null;
$userId = $user->getId();
switch ($type) {
case 'MyBundle:Document':
$tableName = 'document';
break;
...
}
if ($tableName && $userId) {
$this->em->getConnection()->query("
DELETE FROM $tableName
WHERE user_id = '$userId'
");
}
}
This solution works as well, but it require to know the internal DB structure, e.g the table names of the different entities, the name of the userId field, etc.
Of course it is not a big deal to get this information but to me this solution seems to be not as clean an the first one.
Is there a way to get the necessary information from Doctrine while still working plain query / bypassing ORM?
Or is there even a better solution to get this done`?
Your first approach is slow because of object hydration. You don't really need to load all records and transform them into objects. You can simply use QueryBuilder:
$qb = $em->createQueryBuilder('c');
$qb->delete('MyBundle:Contact', 'c')
->where('c.user = :user')
->setParameter('user', $user)
->getQuery()
->execute()
;
Or DQL:
$query = $em->createQuery(
'DELETE MyBundle:Contact c
WHERE c.user = :user')
->setParameter('user', $user);
$query->execute();
I'm trying to better understand how CI works.
This is my first table, app_news:
id / name
My second is app_news_comments:
id / id_news / comment
The model I use in order to display news informations is:
public function get_news($where = array()) {
return $this->db->select('*')
->from('app_news')
->where($where)
->get()
->result();
}
The model I use in order to count comments for a news is:
public function count_comment($id_news) {
return (int) $this->db->where(array('id_news' => $id_news)
->count_all_results('app_news_comments');
}
The first solution I have is to print a foreach on my view and putting the count_comment function inside the loop in order to count how much comments I have however I won't respect the MVC pattern. How can I do so ?
The best way to get that information is using this query:
SELECT
app_news.id,
app_news.name,
COUNT(app_news_comments.id)
FROM app_news_comments
JOIN app_news ON app_news_comments.id_news = app_news.id
GROUP BY app_news_comments.id_news
So, you need to create a query using Active Record on Codeigniter or you could add the directly, because Active Record on Codeigniter have some restrictions.
However, you could use this way on Codeigniter to create the query:
$this->db->select('[YOUR FIELDS AND COUNT]', FALSE)
->from('app_news_comments') .... etc
and to add the query directly:
$this->db->query('[QUERY HERE]');
I hope this help you.
I have an sql request:
$listUsers=$connection->prepare("SELECT u.id as userid From user u , account a where (a.id_parent=$id_account OR a.id=$id_account) and u.id_account=a.id ");
$listUsers->execute();
$users=$listUsers->fetchColumn();
but fetchColumn return just one result, and I want a set of list user id to use it in the next request.
$listPush=$connection->prepare("select id from Table_name where id_user in (?));
$listPush->bindValue(1, $users);
$listPush->execute();
$idpushs=$listPush->fetchColumn();
but this return just one result. Any Idea to replace fetchColumn by other request or using doctrince.
With your logic, you can simply fetch all your users and implode it.
$listUsers=$connection->prepare("SELECT u.id as userid From user u , account a where (a.id_parent=$id_account OR a.id=$id_account) and u.id_account=a.id ");
$listUsers->execute();
$users=$listUsers->fetchAll();
$userIds = array();
foreach ($users as $user) {
$userIds[] = $user['userid'];
}
$users = implode(',', $userIds); //that hurts me so hard to code like this with symfony :'(
Fetch colum will only return the column of the next row. So for your second piece of code, you can loop over results.
But, if you're are using a framework like symfony, you have many other ways to make it cleaner. Just check out the symfony doc to use repository relations and more generally doctrine
I have a website where I populate the first page with objects of different nature (last posts, last recipes, last ingredients published). I have currently one querybuilder and then one query for each of them because I call ->getQuery()->getResult() on each of them.
Is there not a way to merge all those querybuilders before executing the query so as to retrieve an array of results made of the results of each of those querybuilders ?
Would that be a best practice ? How would we do it ?
EDIT: what I hoped we could do:
$recipesQueryBuilder = $this->getDoctrine->getRepository('Recipe')->createQueryBuilder('r');
$postsQueryBuilder = $this->getDoctrine->getRepository('Post')->createQueryBuilder('p');
$results = mergeQueryBuilder($recipesQueryBuilder, $postQueryBuilder)->getQuery()->getResult();
$recipes = $results['r'];
$posts = $results['p'];
I do this with many of our queries. I doubt there is a formal "best practice" for this kind of thing, however I can vouch for the fact that re-using builders does simplify the code. For example:
public function getListBuilder(User $user)
{
return $this->createQueryBuilder('l')->where('l.user = :user')->setParameter('user', $user)->orderBy('l.name');
}
I have a number of queries that re-use this base builder. For example:
public function countLists(User $user = null)
{
$qb = $this->getListBuilder($user);
return $qb->select('COUNT(l)')->getQuery()->getSingleScalarResult();
}
Likewise another method findActiveLists() changes the order to createdAt and generates a query with setMaxResults() specified.
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();