I'm playing with Doctrine, and I think I probably miss something.
So I have a table for some relations, and here is the columns:
id | user_id | workshop_id
Basically, I use this table to know that a user is register for a workshop.
I want to count how many users subscribe for a workshop.
So in my Repository, I use a DQL request:
/** #var EntityManager $entityManager */
$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery("
SELECT COUNT('uw.id')
FROM App\Entity\UserWorkshops AS uw
LEFT JOIN App\Entity\User AS u WITH u.userId = uw.user
WHERE uw.workshop = :workshop_id");
$count = $query
->setParameters(['workshop_id' => $workshopId])
->getResult();
And here, and example of the result:
I just need to retrieve 3, as an integer.
How can I return something different ?
thanks
You can use getSingleScalarResult, from the doc:
Query#getSingleScalarResult(): Retrieves a single scalar value from
the result returned by the dbms. If the result contains more than a
single scalar value, an exception is thrown. The pure/mixed
distinction does not apply.
As example:
$count = $query
->setParameters(['workshop_id' => $workshopId])
->getSingleScalarResult();
Related
I have some One-To-Many, unidirectional with Join Table relationships in a Symfony App which I need to query and I can't figure out how to do that in DQL or Query Builder.
The Like entity doesn't have a comments property itself because it can be owned by a lot of different types of entities.
Basically I would need to translate something like this:
SELECT likes
FROM AppBundle:Standard\Like likes
INNER JOIN comment_like ON comment_like.like_id = likes.id
INNER JOIN comments ON comment_like.comment_id = comments.id
WHERE likes.created_by = :user_id
AND likes.active = 1
AND comments.id = :comment_id
I've already tried this but the join output is incorrect, it selects any active Like regardless of its association with the given comment
$this->createQueryBuilder('l')
->select('l')
->innerJoin('AppBundle:Standard\Comment', 'c')
->where('l.owner = :user')
->andWhere('c = :comment')
->andWhere('l.active = 1')
->setParameter('user', $user)
->setParameter('comment', $comment)
I see 2 options to resolve this:
Make relation bi-directional
Use SQL (native query) + ResultSetMapping.
For the last option, here is example of repository method (just checked that it works):
public function getLikes(Comment $comment, $user)
{
$sql = '
SELECT l.id, l.active, l.owner
FROM `like` l
INNER JOIN comment_like ON l.id = comment_like.like_id
WHERE comment_like.comment_id = :comment_id
AND l.active = 1
AND l.owner = :user_id
';
$rsm = new \Doctrine\ORM\Query\ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata(Like::class, 'l');
return $this->_em->createNativeQuery($sql, $rsm)
->setParameter('comment_id', $comment->getId())
->setParameter('user_id', $user)
->getResult();
}
PS: In case of Mysql, 'like' is reserved word. So, if one wants to have table with name 'like' - just surround name with backticks on definition:
* #ORM\Table(name="`like`")
I find the Symfony documentation very poor about unidirectional queries.
Anyway I got it working by using DQL and sub-select on the owning entity, which is certainly not as fast. Any suggestion on how to improve that is more than welcomed!
$em = $this->getEntityManager();
$query = $em->createQuery('
SELECT l
FROM AppBundle:Standard\Like l
WHERE l.id IN (
SELECT l2.id
FROM AppBundle:Standard\Comment c
JOIN c.likes l2
WHERE c = :comment
AND l2.owner = :user
AND l2.active = 1
)'
)
->setParameter('user', $user)
->setParameter('comment', $comment)
;
I've table like: product_id->name,description, price
I want to retrieve all products, so this is my code for retrieve products
$doc = $this->getDoctrine();
$em = $doc->getEntityManager();
$conn = $em->getConnection();
$repo = $doc->getRepository('AppBundle:Products');
$query = $repo->createQueryBuilder('p')->getQuery();
$sql = $query->getSql();
$stmt = $conn->prepare($sql);
$stmt->execute();
$products = $stmt->fetchAll(\PDO::FETCH_ASSOC);
Problem is when building a query it's always add column index suffix after column name as alias
So results is going to be like:
[
{"product_id_0":"1","name_1":"Name1","description_2":"Desc1","price_3":"100"},
{"product_id_0":"2","name_1":"Name2","description_2":"Desc2","price_3":"200"}
]
Notice their are suffix like _0, _1 after column name that I don't want it,
How can I build query without alias like that?
You've already got a repository.
When you have a repository, you do not need to create raw SQL queries or even use the created ones. Let Doctrine take care of that (this is the DBAL, Doctrine's database abstraction layer).
Instead, use a method like findAll() or findBy() to get objects.
Example:
$repo = $doc->getRepository('AppBundle:Products');
$results = $repo -> findAll();
// Returns array(Product, Product, ...), which should be mapped to an entity
// so that you can use $product -> getName() or whatever you need.
You can also create a query to get an array result:
$results = $repo -> createQueryBuilder('p') -> getQuery() -> getArrayResult();
Documentation: http://symfony.com/doc/current/book/doctrine.html
I have an issue with DQL in Doctrine 2.
Subqueries seem to be unavailable in DQL, so I don't know how to transform :
SELECT DISTINCT a.ID_DOMAINE, L_DOMAINE, b.ID_SS_DOMAINE, L_SS_DOMAINE, c.ID_COMPETENCE, L_COMPETENCE
FROM ((qfq_prod.REF_DOMAINE a inner join qfq_prod.REF_SS_DOMAINE b on a.id_domaine = b.id_domaine)
inner join qfq_prod.REF_COMPETENCE c on b.id_ss_domaine = c.id_ss_domaine)
inner join qfq_prod.REF_PERS_COMP d on c.id_competence = d.id_competence
into a DQL expression.
I tried it and got
"Error: Class '(' is not defined."
I saw that we can use Query Builder to do this as well.
Being new with Doctrine 2, can someone explain to me how I can do this please ?
My DQL is currently :
$query = $this->getEntityManager()->createQuery ( "SELECT DISTINCT a.ID_DOMAINE, L_DOMAINE, b.ID_SS_DOMAINE, L_SS_DOMAINE, c.ID_COMPETENCE, L_COMPETENCE
FROM ((BdDoctrine\Entity\Domaine a inner join BdDoctrine\Entity\SsDomaine b on a.id_domaine = b.id_domaine)
inner join BdDoctrine\Entity\Competence c on b.id_ss_domaine = c.id_ss_domaine)
inner join BdDoctrine\Entity\LienPersComp d on c.id_competence = d.id_competence" );
$res = $query->getResult ();
Subqueries seem to be unavailable in DQL, so I don't know how to transform :
Actually, they are. Your code (no offence) is hardly readable so I will give you an example:
//controller
$repo = $this->getDoctrine()->getRepository("Your:Bundle:Category") ;
$results = $repo->findAllForSomePage() ;
// CategoryRepository.php
public function findAllForSomePage()
{
return $this->createQueryBuilder("o")
->innerJoin("o.products", "p", "WITH", "p.price>:price")->addSelect("p")
->setParameter("price", 50)
->where("o.id IN (SELECT s1.id FROM Your:Bundle:Something s1 WHERE s1.col1=5)")
->getQuery()->getResult() ;
}
Here is presumed you have Category hasMany Products relation and that you defined CategoryRepository file. You should never create queries in controller.
This example will fetch Categories only if they have Products with price bigger than 50, AND the ID of categories are those fetched by fictional subquery. This 100% works.
You should apply the same logic on your requirement.
Also, you should not use ON statement when using joins, that is handled by doctrine.
If you have the relationships properly defined in your entities, then you can make your joins on those relationships. And as Zeljko mentioned, you don't need to specify the ON condition, as the entities should already know how they are related. You are joining entities not tables. (That's under the hood.)
I don't know what your entities look like, so I made a guess at the relationship names below, but it should give you the idea.
$dql =
<<<DQL
SELECT
DISTINCT a.ID_DOMAINE, b.L_DOMAINE, b.ID_SS_DOMAINE, b.L_SS_DOMAINE, c.ID_COMPETENCE, c.L_COMPETENCE
FROM
BdDoctrine\Entity\Domaine a
JOIN a.ss_domaine b
JOIN b.competence c
JOIN c.lien_pers_comp d
DQL;
$query = $this->getEntityManager()->createQuery($dql);
$res = $query->getResult();
I am trying to obtain value of a custom field of a product. I only have ID of that product. I know title of the custom field.
How can I get value of that custom field?
Please help.
PS: I know PHP well but I am new to Joomla/Virtuemart.
This will work for VM2.x
VM is storing the custom field values in #__virtuemart_customs
The relation between the product and the custom field is maintaining in the #__virtuemart_product_customfields
You have title and Product Id so you can try this
$db = &JFactory::getDBO();
$sql = "SELECT F.custom_value #__virtuemart_customs AS C LEFT JOIN #__virtuemart_product_customfields AS F ON F.virtuemart_customfield_id = C.virtuemart_custom_id where (C.custom_title='$your_customtitle' and F.virtuemart_product_id = '$product_id')";
$db->setQuery($sql);
$db->query();
$res = $db->loadAssoc();
echo $res['custom_value'];
Also you try with inner sub query .
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('virtuemart_product_id,custom_value');
$query->from('#__virtuemart_product_customfields');
$db->setQuery((string)$query);
$results = $db->loadObjectList();
if ($results){
foreach($results as $result)
{
// do your stuff here
}
Using Zend Lucene Search, I am returning a list of relevance-ordered IDs that map to blog records that I will fetch from the database.
Is this the proper way of handling an array with Doctrine2's WHERE IN expression:
$dql = "SELECT b FROM BlogPost WHERE b.id IN (" . implode(', ', $ids) . ")";
$query = $em->createQuery($dql);
...
Or is there a better way of maybe passing in the actual $ids array as a parameter to the query?
Also, the Zend Search returns the array of IDs based on relevance. Will using the above technique preserve the order of relevance in retrieving the blog posts?
If it makes you feel better, you can use the ExpressionBuilder.
$ex = $em->getExpressionBuilder();
$dql = 'SELECT b FROM BlogPost b WHERE ' . $ex->in('b.id', $ids));
$query = $em->createQuery($dql);
function cmp($a, $b) {
global $ids;
return (array_search($a->getId(), $ids) < array_search($b->getId(), $ids)) ? -1 : 1;
}
usort($res, 'cmp');
It's a bit cleaner, but does the same as you behind the screens.
You should pas the $ids array through setParameter() function as this is a best practice in doctrine:
$query = $this->_em->createQuery('SELECT b FROM BlogPost WHERE b.id IN (?1)');
$query->setParameter(1, implode(',', $ids));
I think the IN statement will not preserve the order of the passed ID's as IN will match the first found $id not depending on the order.