Symfony doctrine DQL random result in Query with MaxResult - php

How can I get a random result with an dql Query?
This is my query:
$firstCategoryId = 50;
$repository = $this->entityManager->getRepository(BaseProduct::class);
$products = $repository->createQueryBuilder('p')
->join('p.categories', 'c')
->where('c.id = :categoryId')
->setParameter('categoryId', $firstCategoryId)
->getQuery()
->setMaxResults(4)
->getResult();
This returns me always the first 4 products.
Lets say the category with ID 50 has over 100 products. And what I want is querying randomly 4 articles from category with ID 50. But how? Is this possible? Of course I can set no Max Result and than do it with PHP... but this is not a good solution because of performance.

You need to create dql function for that. https://gist.github.com/Ocramius/919465 you can check that.
namespace Acme\Bundle\DQL;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
class RandFunction extends FunctionNode
{
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(SqlWalker $sqlWalker)
{
return 'RAND()';
}
}
After that open your config.yml file and add autoload that RandFunction.
orm:
dql:
numeric_functions:
Rand: Acme\Bundle\DQL\RandFunction
And your query must be like:
$firstCategoryId = 50;
$repository = $this->entityManager->getRepository(BaseProduct::class);
$products = $repository->createQueryBuilder('p')
->join('p.categories', 'c')
->addSelect('RAND() as HIDDEN rand')
->where('c.id = :categoryId')
->orderBy('rand')
->setParameter('categoryId', $firstCategoryId)
->getQuery()
->setMaxResults(4)
->getResult();

Related

Symfony3 - create join query of two tables in controller

How to show two tables data from my controller.
Here is my controller's code.
class TestController extends Controller
{
public function showAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$teacher = $this->getDoctrine()->getRepository(Teacher::class);
$query = $em
->createQueryBuilder('t')
->from('AppBundle:Teacher','t')
->Join('AppBundle:Student','s')
->where('t.id=id and s.tid=tid')
->getQuery()
->getResult();
}
}
When print_r it's showing only one table data.
Please help
Please check below mentioned solution.
$query = $em
->createQueryBuilder('t.*,s.*')
->from('AppBundle:Teacher','t')
->Join('AppBundle:Student','s')
->where('t.id=id and s.tid=tid')
->getQuery()
->getResult();
}
Let me know if it not works.
I assume that you have defined a relationship between Teacher and Student in your entities. In this case you can get the Student objects by calling $teacher->getStudents() (assuming that you have defined such a method in your Teacher entity class). See Doctrine documentation about association mapping
Example for a One-To-Many relationship:
<?php
use Doctrine\Common\Collections\ArrayCollection;
/** #Entity */
class Teacher
{
// ...
/**
* One Teacher has Many Students.
* #OneToMany(targetEntity="Student", mappedBy="teacher")
*/
private $students;
// ...
public function __construct() {
$this->students = new ArrayCollection();
}
}
/** #Entity */
class Student
{
// ...
/**
* Many Students have One Teacher.
* #ManyToOne(targetEntity="Teacher", inversedBy="students")
* #JoinColumn(name="teacher_id", referencedColumnName="id")
*/
private $teacher;
// ...
}
In the QueryBuilder object you can avoid the need of additional queries on $teacher->getStudents() calls by adding something like that:
$query = $em
->createQueryBuilder('t')
->from('AppBundle:Teacher','t')
->join('AppBundle:Student','s')
->select(array('t', 's'))
->where('t.id=id and s.tid=tid')
->getQuery()
->getResult();
}
If there is a relationship defined between Teacher and Student in your entities as mentioned above you can even simplify the join:
$query = $em
->createQueryBuilder('t')
->from('AppBundle:Teacher','t')
->join('t.students', 's')
->select(array('t', 's'))
->getQuery()
->getResult();
}
Furthmore you do not need to call the from() method if you create the QueryBuilder object via the TeacherRepository object:
$query = $teacher
->createQueryBuilder('t')
->join('t.students', 's')
->select(array('t', 's'))
->getQuery()
->getResult();
}
$query = $em
->createQueryBuilder('t')
->add('select', 't,s')
->from('AppBundle:Teacher', 't')
->Join('AppBundle:Student', 's')
->where('t.id = s.tid')
->getQuery()
->getResult();
it working perfect.
First we select all from Teachers table, then join students. Assume that your relationship name in Teachers model is student. In repository file:
public function getWithStudents() {
return $this->createQueryBuilder('t')
->Join('t.student', 's')
->addSelect('s')
->getQuery()->getArrayResult();
}
Then in controller call it:
$teachersWithStudents = $teacher->getWithStudents();
Or in this case you can just
$teachersWithStudents = $teacher->getStudents();
Suppose you have two tables.comment table and article table and You want to fetch comments on each article
$commentContent = $em
// automatically knows to select Comment
// the "c" is an alias you'll use in the rest of the query
->createQueryBuilder('c')
->select('c.message, c.name')////Fields required to display
->from('AppBundle:Comment','c')
->join('AppBundle:Article','a')
->where('c.idArticle=a.id and c.publish_mainPage = 1')
->orderBy('c.idArticle', 'DESC')
->getQuery()
->getResult();
var_dump($commentContent);

Symfony Custom DoctrineFunctions always returns null

I'm writing a simple Custom Doctrine Function on Symfony that computes AGE given the bithdate of the entity. Here is my function:
class AgeFunction extends FunctionNode
{
private $birthDate;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->birthDate = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
$bday = $this->birthDate->dispatch($sqlWalker);
$currDate = DateFormatter::formatDate(new \DateTime());
return "TIMESTAMPDIFF(YEAR, {$bday}, '{$currDate}')";
}
}
And here is how i used it:
public function getAge()
{
$qb = $this->createQueryBuilder('s')
->select('AGE(s.dateOfBirth)')
->orderBy('s.id', 'DESC');
dump($qb->getQuery()->getResult());
}
This is the query produced:
SELECT TIMESTAMPDIFF(YEAR, s0_.date_of_birth, '2017-04-13') AS sclr_0 FROM suspect s0_ ORDER BY s0_.id DESC;
I think whats wrong here is s0_.date_of_birth never gets the actual value since when i replace it manually it works well.
So how can I do this? Thanks.
Maybe you're originally trying to do something else but the business requirement seems weird to me cos you're trying get just last person's age . Anyway let me just ignore it for now and focus on what you need. I've checked the example below and worked fine.
DQL
namespace My\Bundle\Product\APIBundle\Entity\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
class TimestampDiff extends FunctionNode
{
public $value;
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->value = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(SqlWalker $sqlWalker)
{
return sprintf(
'TIMESTAMPDIFF(YEAR, %s, %s)',
$this->value->dispatch($sqlWalker),
date('Y-m-d')
);
}
}
REPOSITORY
public function findAge()
{
$qb = $this->createQueryBuilder('s')
->select('TIMESTAMPDIFF(s.dateOfBirth) AS Age')
->orderBy('s.id', 'DESC')
->setMaxResults(1);
return $qb->getQuery()->getResult(Query::HYDRATE_SIMPLEOBJECT);
}
CALL
$p = $this->suspectRepository->findAge();
REGISTER (My setup is different so you can check links below to make it work for your setup)
# app/config.yml
doctrine:
dbal:
default_connection: hello
connections:
hello:
driver: "%database_driver%"
host: "%database_host%"
....
....
orm:
default_entity_manager: hello
entity_managers:
hello:
dql:
string_functions:
TIMESTAMPDIFF: My\Bundle\Product\APIBundle\Entity\DQL\TimestampDiff
connection: hello
....
How to Register custom DQL Functions
How to create and use custom built doctrine DQL function in symfony
RESULT
SELECT
TIMESTAMPDIFF(YEAR, s0_.date_of_birth, 2017-04-13) AS sclr_0
FROM suspect s0_
ORDER BY s0_.id DESC
LIMIT 1

Get the last value of a entity field with condition

I'm making a website for performance monitoring.
I have 2 tables:
Users table linked in one-to-Many with performances
Performances table linked in Many-to-one with users table
I just wanna get the last weight which is not null in my table performances and display it in twig
database screenshot
For exemple in this database, the result would be : 80
I tried with queries but I get an arror message so I don't know how to do
Thanks in advance for your help !
I hope this could helps you.
I suppose you have a repository class for your Performance entity. I would use this kind of code for a Symfony3 application
<?php
namespace AppBundle\Repository; //replace AppBundle by the name of your bundle
use Doctrine\ORM\Tools\Pagination\Paginator;
/**
* PerformanceRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class PerformanceRepository extends \Doctrine\ORM\EntityRepository
{
public function getLastWeigth()
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('p')
->from($this->_entityName, 'p')
->expr()->isNotNull('p.weight')
->orderBy('p.date', 'desc')
->setMaxResults(1);
$query = $qb->getQuery();
$result = $query->getSingleResult();
return $result;
}
}
Edit: here is an Exemple of use in the controller in a Symfony 3 application:
<?php
namespace AppBundle\Controller\Performance;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use AppBundle\Repository\PerformanceRepository;
class PerformanceController extends Controller
{
public function lastWeightAction()
{
$repo = $this->getDoctrine()->getManager()->getRepository('AppBundle:Performance');
$lastPerformance = $repo->getLastWeigth();
//some other code
}
}
Edit2:
If you need to get the last weight by a user Id:
class PerformanceRepository extends \Doctrine\ORM\EntityRepository
{
public function getLastWeigth($userId)
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('p')
->from($this->_entityName, 'p')
->join('p.user', 'u')
->where('u.id = :userId')
->expr()->isNotNull('p.weight')
->orderBy('p.date', 'desc')
->setMaxResults(1);
->setParameter(':userId', $userId);
$query = $qb->getQuery();
$result = $query->getSingleResult();
return $result;
}
}

Error when use custom DQL function with Doctrine and Symfony2

I use Symfony 2 and the ORM Doctrine. I want to create and register a custom DQL function. In fact, I want to use the SQL function "CAST" in my request, like this :
$qb = $this->_em->createQueryBuilder();
$qb->select('d')
->from('\Test\MyBundle\Entity\MyEntity', 'd')
->orderBy('CAST(d.myField AS UNSIGNED)', 'ASC')
return $qb->getQuery()->getResult();
For this, I have created a "CastFunction" who extend "FunctionNode" :
namespace Test\MyBundle\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\Parser;
class CastFunction extends FunctionNode
{
public $firstDateExpression = null;
public $secondDateExpression = null;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_IDENTIFIER);
$this->secondDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return sprintf('CAST(%s AS %s)', $this->firstDateExpression->dispatch($sqlWalker), $this->secondDateExpression->dispatch($sqlWalker));
}
}
Of course, I have registered this class in my config.yml :
doctrine:
orm:
dql:
string_functions:
CAST: Test\MyBundle\DQL\CastFunction
Now, when I try my request, I obtain the following error:
"[Semantical Error] line 0, col 83 near 'UNSIGNED)': Error: 'UNSIGNED' is not defined."
I search but I don't where is the problem!
Have you got a idea?
After several search, I have finally found the solution. I had two problems: first my parse function was wrong, second, I called a SQL function in my orderBy (thank you Cerad).
So, here is my correct class:
namespace Ypok\YPoliceBundle\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\Parser;
class CastFunction extends FunctionNode
{
public $firstDateExpression = null;
public $unit = null;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->StringPrimary();
$parser->match(Lexer::T_AS);
$parser->match(Lexer::T_IDENTIFIER);
$lexer = $parser->getLexer();
$this->unit = $lexer->token['value'];
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return sprintf('CAST(%s AS %s)', $this->firstDateExpression->dispatch($sqlWalker), $this->unit);
}
}
And now, I can use perfectly the SQL function 'CAST' in my repository:
$qb = $this->_em->createQueryBuilder();
$qb->select('d, CAST(d.myField AS UNSIGNED) AS sortx')
->from('\Test\MyBundle\Entity\MyEntity', 'd')
->orderBy('sortx', 'ASC')
return $qb->getQuery()->getResult();
Best regards
Can't find the reference but functions are not allowed in the order by clause. You need to cast your value in the select statement then sort by it.
Something like:
$qb->select('d, CAST(d.myField AS UNSIGNED) AS sortx)
->from('\Test\MyBundle\Entity\MyEntity', 'd')
->orderBy('sortx, 'ASC')
That is assuming your CAST function is written correctly.

Doctrine : how to manipulate a collection?

With symfony && doctrine 1.2 in an action, i try to display the top ranked website for a user.
I did :
public function executeShow(sfWebRequest $request)
{
$this->user = $this->getRoute()->getObject();
$this->websites = $this->user->Websites;
}
The only problem is that it returns a Doctrine collection with all the websites in it and not only the Top ranked ones.
I already setup a method (getTopRanked()) but if I do :
$this->user->Websites->getTopRanked()
It fails.
If anyone has an idea to alter the Doctrine collection to filter only the top ranked.
Thanks
PS: my method looks like (in websiteTable.class.php) :
public function getTopRanked()
{
$q = Doctrine_Query::create()
->from('Website')
->orderBy('nb_votes DESC')
->limit(5);
return $q->execute();
}
I'd rather pass Doctrine_Query between methods:
//action
public function executeShow(sfWebRequest $request)
{
$this->user = $this->getRoute()->getObject();
$this->websites = $this->getUser()->getWebsites(true);
}
//user
public function getWebsites($top_ranked = false)
{
$q = Doctrine_Query::create()
->from('Website w')
->where('w.user_id = ?', $this->getId());
if ($top_ranked)
{
$q = Doctrine::getTable('Website')->addTopRankedQuery($q);
}
return $q->execute();
}
//WebsiteTable
public function addTopRankedQuery(Doctrine_Query $q)
{
$alias = $q->getRootAlias();
$q->orderBy($alias'.nb_votes DESC')
->limit(5)
return $q
}
If getTopRanked() is a method in your user model, then you would access it with $this->user->getTopRanked()
In your case $this->user->Websites contains ALL user websites. As far as I know there's no way to filter existing doctrine collection (unless you will iterate through it and choose interesting elements).
I'd simply implement getTopRankedWebsites() method in the User class:
class User extends BaseUser
{
public function getTopRankedWebsites()
{
WebsiteTable::getTopRankedByUserId($this->getId());
}
}
And add appropriate query in the WebsiteTable:
class WebsiteTable extends Doctrine_Table
{
public function getTopRankedByUserId($userId)
{
return Doctrine_Query::create()
->from('Website w')
->where('w.user_id = ?', array($userId))
->orderBy('w.nb_votes DESC')
->limit(5)
->execute();
}
}
You can also use the getFirst() function
$this->user->Websites->getTopRanked()->getFirst()
http://www.doctrine-project.org/api/orm/1.2/doctrine/doctrine_collection.html#getFirst()

Categories