How to use date() function / return hydrated objects? - doctrine-orm

In my Entity Repository for Doctrine2, I have the following:
$date = new DateTime('NOW');
$date = $date->format('Y-m-d');
if ($region) {
$region_sql = " AND WHERE region LIKE ?3 ";
} else {
$region_sql = "";
}
$sql = "SELECT *, count(month) as count FROM (SELECT *, date(date_from, 'start of month', '+1 month', '-1 day') as month FROM manifestations WHERE date_to >= :date_to " . $region_sql . ") GROUP BY month";
$stmt = $em->getConnection()->prepare($sql);
$stmt->bindValue(':date_to', $date);
if($region) {
$stmt->bindValue(3, sprintf('%%,%s,%%', $region));
}
$stmt->execute();
return $stmt->fetchAll();
But I need to change this so that it returns the objects hydrated instead of an array. I originally wanted to use DQL or queryBuilder but could not find a way to get the date() function to work.

With NativeQuery you can execute native SELECT SQL statements and map the results to Doctrine entities or any other result format supported by Doctrine.
What you want to do can be achieved using the ResultSetMappingBuilder.
ResultSetMappingBuilder is a convenience wrapper. It can generate the mappings for you based on Entities.
This is how I'd do it (I assume your query works, maybe you'll have to adjust it, as I will use a new alias):
Create the ResultSetMapping:
use Doctrine\ORM\Query\ResultSetMapping;// Don't forget this
$rsm = new ResultSetMappingBuilder($entityManager);// $entityManager points to your entity manager.
$rsm->addRootEntityFromClassMetadata('path/to/class/MyClass', 'a');// Notice the a, it's an alias that I'll later on use in the query.
$rsm->addScalarResult("count", "count");// column, alias
Prepare $region_sql part as you do in your code and add the a alias to whatever you want to map. a.* will be mapped to an object (notice the as a I use in the query):
$sql = "SELECT a.*, count(month) as count FROM (SELECT *, date(date_from, 'start of month', '+1 month', '-1 day') as month FROM manifestations WHERE date_to >= :date_to " . $region_sql . ") as a GROUP BY month";
Execute the query:
$query = $entityManager->createNativeQuery($sql, $rsm);
$query->setParameter('date_to', $date);
$result = $query->getResult();
This will give you an array of rows. Each of them will be a mixed array, $result[n][0] will contain the object and $result[n]["count"] the value of the count column of the query (name of the column is the same as the alias we set up in the $rsm) where n is the number of the row.

Related

My program reads 0 from the database even though there is a 1

I don't understand whats wrong with the code, I have read a lot of times but I can't find the error
pstmt = con->prepareStatement("SELECT (?) FROM votos WHERE id = (?)");
pstmt->setString(1, eleccion);
pstmt->setInt(2, p->getId());
res = pstmt->executeQuery();
while(res->next())
{
p->setVoto(res->getInt(1));
}
When the eleccion and id variables are Provincial and 1 respectively the getInt(1) function should return 1, but it returns 0.
The command (in the mysql command line):
SELECT Provincial from Votos WHERE id=1
Returns a table with one row and one column with the value 1
Side notes:
Spelling was checked
The getId() function works correctly
The compiler doesn't give any error
You can't use a placeholder in a prepared query for a column name. It's returning the value of the string eleccion, not using it as the name of a column in the table. You need to do string concatenation to substitute the column name.
std::string sql = std::string("SELECT `") + eleccion + "` FROM votos WHERE id = ?";
pstmt = con->prepareStatement(sql.c_str());
pstmt->setInt(1, p->getId());
res = pstmt->executeQuery();
while(res->next())
{
p->setVoto(res->getInt(1));
}
If the value of eleccion is coming from the user or some other untrusted source, make sure you validate it before concatenating, to prevent SQL injection.

Doctrine query builder - nested queries

How can I convert this query into symfony 2 doctrine query builder?
SELECT
artist_id,
DATE,
balance,
TYPE
FROM TRANSACTION AS
t1
WHERE
DATE =(
SELECT
MAX(DATE)
FROM TRANSACTION
WHERE
artist_id = t1.artist_id AND
STATUS
IN(
'partial',
'pending',
'deducted',
'accepted'
) AND TYPE NOT LIKE 'payment'
)
GROUP BY
artist_id
ORDER BY
artist_id
I tried the following:
$qb = $this->getEntityManager()->createQueryBuilder()
->select('t.balance','a.id','t.date')
->from('TestMainBundle:Transaction','t')
->Join('t.artist','a')
->where("t.status in ('partial','pending','deducted','accepted')")
->andWhere("t.type NOT LIKE 'payment'")
->groupBy('a.id')
->orderBy('a.id');
return $qb->getQuery()->getResult();
But I am stuck with including the condition of max (date) as well. Any help on this is very much appreciated.
Your Doctrine query will look something like this,
$qb1 = $this->getDoctrine()->getManager()->createQueryBuilder();
$select = $qb1->select('MAX(date) AS max_data')
->from('YourBundle:Transaction', 's')
->where('s.artist_id = :ti_artist_id')
->andWhere('s.status IN (:statuses)')
->andWhere('s.type NOT LIKE :type')
->getQuery();
$qb2 = $this->getDoctrine()->getManager()->createQueryBuilder();
$result = $qb2->select('t.artist_id', 't.date', 't.balance', 't.type')
->from('YourBundle:Transaction', 't');
$result->where($qb2->expr()->eq('t.date', $select->getDQL()))
->setParameter('ti_artist_id', 't.id')
->setParameter('statuses', array('partial','pending','deducted','accepted'))
->setParameter('type', 'payment') //possibly '%payment%'
->orderBy('t.artist_id')
->getQuery()
->getResult();
Cheers!!!

Doctrine2 DQL Syntax error when ordering by count

This is Doctrine Repository function
public function mostReadArticleByUser($userId){
$total = $this->createQueryBuilder('ar')
->select('ar.articleId', 'COUNT(ar)')
->where('ar.authorId = :userId')
->groupBy('ar.articleId')
->orderBy('COUNT(ar)', 'DESC')
->setMaxResults(1)
->setParameter('userId', $userId)
->getQuery()
->getResult();
return $total;
}
which should be equivalent to this query
SELECT article_id, count(id)
FROM profile_article_reads
WHERE author_id = 2
GROUP BY article_id
Order by count(id) DESC
LIMIT 1;
When I execute this code I get error
Error: Expected end of string, got '('
QueryException: SELECT ar.articleId, COUNT(ar) FROM
SciProfileBundle\Entity\ProfileArticleReads ar WHERE ar.authorId =
:userId GROUP BY ar.articleId ORDER BY COUNT(ar) DESC
THe count funtion accept a field, so try with
COUNT(ar.id)
instead of:
COUNT(ar)
Probably for sorting is better using an alias, as example:
public function mostReadArticleByUser($userId){
$total = $this->createQueryBuilder('ar')
->select('ar.articleId', 'COUNT(ar.id) as total')
->where('ar.authorId = :userId')
->groupBy('ar.articleId')
->orderBy('total', 'DESC')
->setMaxResults(1)
->setParameter('userId', $userId)
->getQuery()
->getResult();
return $total;
}
Hope this help

Doctrine 2 how to increment a column for multiple rows at once?

i'm looking for a way to increment a value for multiple rows at once without looping , is it possible in doctrine ?
here is the query in simple sql :
$sql = "UPDATE table set compteur = compteur + 1 where id in ('1','2','3') ";
using doctrine for updating many rows ( not incrementing ) , i have this :
$qb = $this->getEntityManager()->createQueryBuilder();
$query = $qb->update('Application\Entity\Table', 'l')
->set('l.compteur', $qb->expr()->literal('8'))
->where("l.id in ('$ids')")
->getQuery();
$retour = $query->execute();
Thanks for any idea !!
Use DQL for this:
$this->getEntityManager()->createQuery('
UPDATE Application\Entity\Table t
SET t.compteur = t.compteur + 1
')
->execute();
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html#dql-update
You can also do it with the querybuilder like so:
$qb->set('l.compteur', $qb->expr()->sum('l.compteur', 1));

Doctrine 2: how do you use a subquery column (in the SELECT clause)

I'm trying to do a simple select query with a subquery in the SELECT clause and have simply not found a way to do it. I've tried with both DQL and with the QueryBuilder, neither work. The code follows, please don't say I could just use a join, this is a simplified example just to illustrate the problem, I have legitimate use cases for subqueries.
// With QueryBuilder
$query = $qb->select(array('a',
'(SELECT at.addresstypeName
FROM e:Addresstype at
WHERE at.addresstypeId = a.addresstypeId
) AS addresstypeName'))
->from('e:Address', 'a')
->where('a.addressId = :addressId')
->setParameter('addressId', 1);
// With DQL
$dql = "SELECT a,
(SELECT at.addresstypeName
FROM e:Addresstype at
WHERE at.addresstypeId = a.addresstypeId
) AS addresstypeName
FROM e:Address a
WHERE a.addressId = :addressId";
$query = $em->createQuery($dql)->setParameter(':addressId', 1);
The following relationship is defined on the Address table:
/**
* #ORM\ManyToOne(targetEntity="Addresstype")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="addresstype_id", referencedColumnName="addresstype_id")
* })
*/
protected $addresstype;
In native SQL, the query would look like this:
SELECT
a.*,
(
SELECT at.addresstype_name
FROM addresstype at
WHERE at.addresstype_id = a.addresstype_id
) AS addresstype_name
FROM address a
WHERE a.address_id = 1
Any ideas?
$query = $qb->select('a')
->addSelect('(SELECT at.addresstypeName
FROM e:Addresstype at
WHERE at.addresstypeId = a.addresstypeId) AS addresstypeName'
)
->from('e:Address', 'a')
->where('a.addressId = :addressId')
->setParameter('addressId', 1);
For me subquery with doctrine works with this query :
$qb->select('e.field')
->addSelect('(SELECT count(mv.nm)
FROM Clt\Bundle\MyBundle\Entity\MV mv
LEFT JOIN Clt\Bundle\MyBundle\Entity\M ma WITH mv.nm=ma.nm
WHERE mv.ne=e.ne and ma.nm is null
) AS nm'
)
->from($this->_entityName, 'e')
->leftJoin('e.m', 'm')
->where($qb->expr()->eq('t.id'.$typeModule, $idElementModule));
Note that in the left join you must use WITH instead of ON...
I know this is an old question, but if you want, you could have used another query builder as your subquery:
$qb->select("a")
->addSelect("(" . $qb2->select("at.addresstypeName")
->from("e:Addresstype", "at")
->where("at.addresstypeId = a.addresstypeId")
->getDQL() . ") AS addresstypeName"
)
->from('e:Address', 'a')
->where('a.addressId = :addressId')
->setParameter('addressId', 1);
In my scenario what I needed was to look into a join and find an Id and use it as boolean, found 1 otherwise 0, then applying this to orderBy. DQL expressions worked only when combined with Where clause, which wasn't my case. So, a DQL subselect saved me.
Adapted more or less to your scenario, it would look like this:
// With QueryBuilder
// In AddressRepository
// Where one address may belong to several addressTypes
public function getWithType($addressType){
$qb = $this->createQueryBuilder('a1');
$qb->addSelect('a1.someField', 'a1.otherField')
$qb->addSelect(
'(SELECT at.addressTypeName
FROM App\Entity\Address a2
JOIN a2.addressType at
WHERE at.id = '.$addressType.' AND a2.id = a1.id
) AS addressTypeName')
//The rest part of the query
}