Symfony custom repository query giving error - symfony

I am really new at Symfony and I am trying to get used to query builder. At the moment I am trying to join three tables and the following MySQL query gives me the results I need when I run it in PhpMyAdmin
SELECT * FROM pe_users u
LEFT JOIN pe_apply a ON u.id = a.user
LEFT JOIN pe_offer o ON a.id = o.application
However when I move this inside a Symfony Repository method
namespace Ache\AdminBundle\Repository;
use Doctrine\ORM\EntityRepository;
class AdminRepository extends EntityRepository{
public function findAllAppByStatus(){
$query = $this->getEntityManager()->createQuery(
'SELECT * FROM pe_users u
LEFT JOIN pe_apply a ON u.id = a.user
LEFT JOIN pe_offer o ON a.id = o.application
');
try {
return $query->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
}
I get the error
[Syntax Error] line 0, col 7: Error: Expected IdentificationVariable |
ScalarExpression | AggregateExpression | FunctionDeclaration |
PartialObjectExpression | "(" Subselect ")" | CaseExpression, got '*'
What does this error mean? what am I doing wrong?
UPDATE
The three entities I have are as following
UserBundle:User
CoreBundle:Apply
AdminBundle:Offer
User.id links with Apply.user and Offer.application links with Apply.id

You can still use raw sql with Symfony if you are comfortable with that
$conn = $this->getEntityManager()->getConnection();
$sql = "SELECT * FROM pe_users u LEFT JOIN pe_apply a ON u.id = a.user LEFT JOIN pe_offer o ON a.id = o.application WHERE u.id = a.user";
$stmt = $conn->prepare($sql);
$stmt->execute();
return $stmt->fetchAll();

I would do :
public function findAllAppByStatus(){
$qb = $this->createQueryBuilder('u')
->leftJoin('CoreBundle:Apply', 'a', 'WITH', 'a.user = u')
->leftJoin('AdminBundle:Offer', 'o', 'WITH', 'o.application = a')
->setMaxResults(1); // if you return only 1 result, you want to be sure only one (or none) result is fetched
return $qb->getQuery()->getOneOrNullResult();
if you want to return possibly many results, as the 'All' in the method name suggests, get rid of the ->setMaxResults() and use $qb->getQuery()->getResult();
see how the queryBuilder works with objects, not tables. Joins are built on entities and properties, not tables and field names.

Related

Need help for performing join Query with QueryBuilder

I got a working SQL query : select p.name, p.id from acv_project p join acv_product prod on prod.project_id = p.id where prod.weight <> 0 and p.green_user_id = 18
If i pass it into a `
$stmt = $em->getConnection()->prepare($rawSql);
$stmt->execute([]);
$projects = $stmt->fetchAll();
It works but i'd like to pass it by adding the "green_user_id" as a parameter and not always 18.
When i try with this code : `
$sql2 = "select p from ArtoAcvBundle:Project p join prod ArtoAcvBundle:Product on prod.project_id = p.id where prod.weight <> 0 and p.green_user_id =:userId";
$query2 = $em->createQuery($sql2)->setParameters(
array('userId' => $userId));
$projects = $query2->getResult();
I get [Semantical Error] line 0, col 48 near 'ArtoAcvBundle:Product': Error: Identification Variable prod used in join path expression but was not defined before.
And with QueryBuilder, i tried lots of thing but fails to understand how to write it.
Here are some links to my 2 Doctrine entities :
Entity Product
Entity Project
Thanks for help !
Proof with:
$sql2 = "select p from ArtoAcvBundle:Project p join ArtoAcvBundle:Product prod where prod.weight <> 0 and p.green_user_id =:userId";
Yep, great thanks for having found this solution. I continued to search and find there existed a bindValue() method in Doctrine.
So i passed my parameter with the raw SQL modified and it works
Example with QueryBuilder
// select p from ArtoAcvBundle:Project p join prod ArtoAcvBundle:Product on prod.project_id = p.id where prod.weight <> 0 and p.green_user_id =:userId
$query = $this->getRepository(Project::class)->createQueryBuilder('p');
$query->join('p.products' , 'prod')
->andWhere('prod.weight <> 0')
->andWhere('p.greenUser = :user')
->addParameter('user', $youruserEntity);
return $query->getQuery()->getResult();

Doctrine query works with SQLite, not with MySQL

I've created two entities called Project and ProjectTag using Doctrine (doctrine/orm v2.6.1) and Symfony. I'd like to select all projects that have all tags with the names given in $tags. The following query from my ProjectRepository.php works in my dev environment with SQLite:
public function findByAllTags($tags)
{
$queryBuilder = $this->createQueryBuilder('p');
for ($i = 0; $i < count($tags); $i++) {
$tagSubQueryBuilder = $this->createQueryBuilder("p$i")
->join('p.tags', "tags_joined_$i")
->andWhere("tags_joined_$i.name = ?$i");
$queryBuilder->andWhere(
$queryBuilder->expr()->exists($tagSubQueryBuilder->getDQL())
);
}
$queryBuilder->orderBy('p.timeCreated','DESC');
return $queryBuilder->getQuery()->setParameters($tags)->getResult();
}
This gets converted to
SELECT
p0_.id AS id_0,
p0_.name AS name_1
FROM
project p0_
WHERE
(
EXISTS (
SELECT
p1_.id
FROM
project p1_
INNER JOIN projects_tags p3_ ON p0_.id = p3_.project_id
INNER JOIN project_tag p2_ ON p2_.id = p3_.project_tag_id
WHERE
p2_.name = ?
)
)
ORDER BY
p0_.created DESC
However, in my production environment with MySQL I get the following error:
An exception occurred while executing 'SELECT p0_.id AS id_0, p0_.name AS name_1 FROM project p0_ WHERE (EXISTS (SELECT p1_.id FROM project p1_ INNER JOIN projects_tags p3_ ON p0_.id = p3_.project_id INNER JOIN project_tag p2_ ON p2_.id = p3_.project_tag_id WHERE p2_.name = ?)) ORDER BY p0_.created DESC' with params ["video"]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'p0_.id' in 'on clause'
In response to your comment, this is an untested approach to your problem without nested queries:
public function findAllByTags($tags)
{
$qb = $this->createQueryBuilder('p');
$qb->join('p.tags', 't');
$qb->where($qb->expr()->in('t.name', ':tags'));
$qb->groupBy('p.id');
$qb->having($qb->expr()->eq('COUNT(t.id)', ':count'));
$qb->setParameter('tags', $tags);
$qb->setParameter('count', count($tags));
return $qb->getQuery()->getResult();
}
This assumes you pass the tag names as an array (or that the Tag class implements a __toString() method that returns the tags name).

Get unjoined values with dql

how can i get the unjoined values with dql ?
The problem in this code is that i'm getting only posts that have comments ..
public function getAllPostsDQL()
{
$q = $this->getEntityManager()
->createQuery('SELECT p.type,p.date,p.urlImage,p.nom,u.nom as nomU,u.prenom as prenomU ,COUNT(co) as nb,MAX(co.date) as maxDate FROM PidevBundle:Publication p LEFT OUTER JOIN
PidevBundle:Commentaire co WITH co.idPublication=p
JOIN PidevBundle:User u WITH p.idUser=u
');
return $q->getResult();
}
Try this: (for Symfony3 syntax may need to be changed)
public function getAllPostsDQL()
{
$qb = $this->createQueryBuilder('publication');
$qb->select(array('publication.type', 'publication.date', 'publication.urlImage',
'publication.nom', 'u.nom AS nomU', 'u.prenom AS prenomU',
'COUNT(co.id) AS nb', 'MAX(co.date) AS maxDate'))
->leftJoin('p.idUser', 'u')
->leftJoin('p.idCommentaire', 'co');
return $qb->getQuery()->getResult();
}
Try something like this:
public function getAllPostsDQL()
{
$q = $this->getEntityManager()->createQuery(
'SELECT
p.type,
p.date,
p.urlImage,
p.nom,
u.nom as nomU,
u.prenom as prenomU,
COUNT(co.id) as nb,
MAX(co.date) as maxDate
FROM PidevBundle:Publication p
LEFT JOIN p.idUser u
LEFT JOIN PidevBundle:Commentaire co ON co.id = u.id
');
return $q->getResult();
}
I have finally found a solution for it , although i think it's a trash code :/
Thanks for everyone .
Code :
public function getAllPostsDQL()
{
$q = $this->getEntityManager()->createQuery(
'SELECT
p.type,
p.date,
p.urlImage,
p.nom,
u.nom as nomU,
u.prenom as prenomU,
(SELECT COUNT(co.id) FROM PidevBundle:Commentaire co WHERE co.idPublication=p) as nb,
(SELECT MAX(com.date) FROM PidevBundle:Commentaire com WHERE com.idPublication=p) as maxDate
FROM PidevBundle:Publication p
INNER JOIN p.idUser u
');
return $q->getResult();
}

Symfony2: Doctrine subquery in the WHERE clause including a LIMIT

I'm trying to convert this SQL into either DQL or whatever the query builder variant would look like.
select *
from project_release r
where (select s.title as status_name
from release_status_log l
left join release_status s
on l.release_status_id = s.id
where l.release_id = r.id
order by l.created_at desc
limit 1
) not in ('Complete', 'Closed')
;
From inside the repository class for the Release entity, I've tried this
return $this->getEntityManager()->createQuery("
select r.*
from MyBundle:Release r
where (select s.title
from MyBundle:ReleaseStatusLog l
join l.status s
where l.release = r
order by l.createdAt desc
limit 1
) IN ('Complete','Closed')
order by r.release_date ASC
limit 10
")->getArrayResult();
Which gives the error
[Syntax Error] line 0, col 265: Error: Expected
Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got 'limit'
Which is referring to the limit 1 in the subquery.
So then I tried this
return $this
->createQueryBuilder('r')
->select('r.*')
->where("(select s.title
from MyBundle:ReleaseStatusLog l
join l.status s
where l.release = r
order by l.created_at desc
limit 1
) $inClause ('Complete', 'Closed')
")
->setMaxResults( $limit )
->orderBy('release_date', 'ASC')
->getQuery()
->getArrayResult()
;
Which gives the same error. How can I execute a subquery limited to 1 row per row in the parent query?
Symfony 2.0.15
Doctrine 2.1.7
PHP 5.3.3
MySQL 5.1.52
I have a solution for this now. I ended up falling back on the native query system with the result set mapping from the entity in questions.
It's not a great solution, but it works and until I see another solution, it's the only option with this type of WHERE clause.
Here's what my finder method looks like now
/**
* Finds Releases by their current status
*
* #param array $statuses White-list of status names
* #param boolean $blackList Treat $statuses as a black-list
* #param integer $limit Limit the number of results returned
* #param string $order Sort order, ASC or DESC
*
* #throws \InvalidArgumentException
*
* #return array <Release>
*/
public function findByCurrentStatus( array $statuses, $blackList=false, $limit=null, $order='ASC' )
{
if ( empty( $statuses ) )
{
throw new \InvalidArgumentException( "Must provide at least one status" );
}
$inClause = $blackList ? 'not in' : 'in';
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata('MyBundle:Release', 'r');
$SQL = "
select *
from project_release r
where (select s.title as status_name
from release_status_log l
left join release_status s
on l.release_status_id = s.id
where l.release_id = r.id
order by l.created_at desc
limit 1
) $inClause ('" . implode( "','", $statuses ) . "')
order by r.release_date $order
";
if ( $limit )
{
$SQL .= " limit $limit";
}
return $this
->getEntityManager()
->createNativeQuery( $SQL, $rsm )
->getResult()
;
}
I kind of loathed going back to building a query as a string, but oh well. Oh, and for you eagle-eyes, $statuses does not come from user data, so no SQL injection vulnerabilities here ;)
In addition to your Native SQL solution, you can create two queries using DQL within a single repository method.
Some adjustment may be required but you could try this:
public function findCompletedReleases()
{
$em = $this->getEntityManager();
$dqlSubQuery = <<<SQL
SELECT
s.title status_name
FROM
Acme\MyBundle\Entity\ReleaseStatus s,
Acme\MyBundle\Entity\ReleaseStatusLog l,
Acme\MyBundle\Entity\Release r
WHERE
l.release = r.id AND
l.status = s.id
ORDER BY l.createdAt DESC
SQL;
$statusName = $em->createQuery($dqlSubQuery)
->setMaxResults(1)
->getSingleScalarResult();
$dql = <<<SQL
SELECT
r
FROM
Acme\MyBundle\Entity\Release r
WHERE
:status_name IN ('Complete','Closed')
ORDER BY r.release_date ASC
SQL;
$q = $em->createQuery($dql)
->setParameters(array('status_name' => $statusName))
->setMaxResults(10);
return $q->getArrayResult();
}

Why is Doctrine joining this entity onto itself? (Not unique table/alias)

I have an entity named 'tile', with a manyToMany relationship with another entity named 'coins'.
/**
* #ORM\ManyToMany(targetEntity="Coin")
* #ORM\JoinTable(name="coin",
* joinColumns={#ORM\JoinColumn(name="tile_id", referencedColumnName="tile_id")},
* inverseJoinColumns={#ORM\JoinColumn(name="coin_id", referencedColumnName="coin_id")}
* )
**/
protected $coins;
I have a page that lists tiles and all of their associated coins. There can be a lot of coins and tiles on a page and using lazy loading can put 100-300 database queries on a single page. I am trying to avoid this by using a leftJoin.
public function getUserTiles($id)
{
$qb = $this->createQueryBuilder('b')
->select('b', 'z')
->leftJoin('b.coins', 'z')
->leftJoin('z.userInfo', 'u')
->add('where', "b.userId = ".$id." AND b.status = 'COMPLETED'")
->add('orderBy', "b.checkinDate DESC");
return $qb->getQuery()
->getResult();
}
This gives me the following error:
*[2/2] DBALException: An exception occurred while executing '
SELECT t0_.tile_id AS tile_id0, t0_.quilt_id AS quilt_id1, t0_.user_id AS user_id2, t0_.comment AS comment3, t0_.checkout_date AS checkout_date4, t0_.checkin_date AS checkin_date5, t0_.x AS x6, t0_.y AS y7, t0_.status AS status8, t0_.completed_neighbors AS completed_neighbors9, t0_.required_completed_neighbors AS required_completed_neighbors10, t0_.visible AS visible11, t0_.visible_date AS visible_date12, t0_.count_towards_coins AS count_towards_coins13, c1_.coin_id AS coin_id14, c1_.user_id AS user_id15, c1_.tile_id AS tile_id16, c1_.comment AS comment17, c1_.date_given AS date_given18, c1_.status AS status19, c1_.origin AS origin20, t0_.quilt_id AS quilt_id21, t0_.user_id AS user_id22, c1_.tile_id AS tile_id23, c1_.user_id AS user_id24
FROM tile t0_ **LEFT JOIN coin *c1_* ON t0_.tile_id = c1_.tile_id
LEFT JOIN coin *c1_* ON c1_.coin_id = c1_.coin_id**
LEFT JOIN user_info u2_ ON c1_.user_id = u2_.user_id
WHERE t0_.user_id = 14 AND t0_.status = 'COMPLETED'
ORDER BY t0_.checkin_date DESC':
SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'c1_'*
As you can see, it is trying to join the coin table twice, using the same alias. What am I doing wrong?
Try:
$this->createQueryBuilder('b')
->select('b')
->addSelect('z')
->leftJoin('b.coins', 'z')
->leftJoin('z.userInfo', 'u')
->where("b.userId = :id AND b.status = 'COMPLETED'")
->orderBy('b.checkinDate', 'DESC')
->setParameter('id', $id)
->getQuery('b.checkinDate DESC')
->getSingleResult();
I fixed this problem by changing the ManyToMany relationship to OneToMany as it should have been in the first place.

Resources