Doctrine ORM - How to choose the first selected item in select query - php

I'm using Symfony 4 with EasyAdmin and I need to make a select query that retrieve all the users, but I want the connected user to be selected as the first result.
I don't know how to proceed at all. I was thinking maybe do multiple select in the same query ? but I don't even know how to do this.
So far I have this :
$repo = $this->getDoctrine()->getRepository(User::class);
$authUser = $this->getUser();
$queryBuilder = $repo->createQueryBuilder('u');
return $queryBuilder;
// Doctrine createQueryBuilder looks like this
public function createQueryBuilder($alias, $indexBy = null)
{
return $this->_em->createQueryBuilder()->select($alias)->from($this->_entityName, $alias, $indexBy);
}
EDIT : I imperatively need to return the queryBuilder object, not the result set, that's why it's tricky.

An approach that will query all users, but gives you an array in the order you describe:
$users = $userRepository->findall();
foreach ($users as $user) {
$this->getUser() == $user ? $currentUser[] = $user : $otherUsers[] = $user;
}
$myUserList = array_merge($currentUser, $otherUsers);
Two sidenotes: 1: This queries all users and then splits them. I'm not sure if this could be what you want. 2: if no user is currently logged in this code will not work because $currentUser won't be defined. Of course, you can change it so it will also work when no user is logged in.
Edit based on the fact that returning the querybuilder is imperative:
You can use CASE to test and create a property, which you can hide with AS HIDDEN in your query and then order by this hidden property like so:
public function currentUserFirst($userId){
$qb = $this->createQueryBuilder('u')
->select('u, CASE WHEN u.id = :userId THEN 0 ELSE 1 END AS HIDDEN order')
->setParameter('userId', $userId)
->orderBy('order', 'ASC');
return $qb;
}

As the first comment pointed out, you will need to use a UNION, which is not supported in DQL.
Therefore your only option is to use a native query using the Connection which you need to get from the EntityManager.
It would look something like this:
$id = $this->getUser()->getId();
$sql = 'SELECT * FROM `users` WHERE `id` = :id UNION SELECT * FROM `users` WHERE`id` != :id'
$users = $this->getDoctrine()->getConnection()->fetchAll($sql, compact('id'));

You can use setMaxResults:
$qb->andWhere('q.start <= :start')
->setParameter(':start', $value)
->setMaxResults(1)
;

Related

Symfony - how to use an array as an parameter by using a querybuilder?

I'm about to output a list that includes several documents(called waiver). However not every user should be allowed to see all documents and therefore I've implemented an filter to check if the user has the same "airline" and "market" assigned. So every user should only see the documents that are assigned to his "airline" and "market".
This is f.e. the getter for the airline of the user entity:
/**
* Get airlines
*
* #return array
*/
public function getAirlines()
{
if($this->airlines != null)
{
$airlines = explode(",", $this->airlines);
return $airlines;
}
return Array();
}
This is the controller logic:
//Get User:
$user = $this->container->get('security.context')->getToken()->getUser();
// Gets an Array of User markets
$user_markets = $user->getMarkets();
// Gets an Array of User carriers
$user_airlines = $user->getAirlines();
if(!$this->ROLE_IS(Array( 'ROLE_XY'))){
$query = $em->createQuery(
'SELECT w
FROM WaiverBundle:Waiver w
WHERE w.carrier = :carrier
AND w.market = :market
ORDER BY w.id DESC'
)
->setFirstResult($page*10-10)
->setMaxResults(10)
// I wan't to get the whole array and not just one position here:
->setParameters(array(':carrier'=>$user_airlines[0],
':market'=>$user_markets[0],
));
}else{
$query = $em->createQuery(
'SELECT u
FROM WaiverBundle:Waiver u
ORDER BY u.id DESC'
)
->setFirstResult($page*10-10)
->setMaxResults(10);
}
Question: How do I manage to compare the DQL attributes with an array and not just a string as a parameter?
I think you want to use "IN" syntax, not "=" syntax:
'SELECT w
FROM WaiverBundle:Waiver w
WHERE w.carrier IN (:carrier)
AND w.market = :market
ORDER BY w.id DESC'
Your query is not complicated. I think you should consider QueryBuilder instead of DQL in this case. Something like this would do the trick:
$qb = $em->createQueryBuilder();
$qb->select('w')
->from('WaiverBundle:Waiver', 'w')
->where($qb->expr()->in('w.carrier', ':carrier'))
->andWhere($qb->expr()->eq('w.market', ':market'))
->orderBy('w.id', 'DESC')
->setParameters(
array(
'carrier'=>$user_airlines[0],
'market'=>$user_markets[0)
);

Doctrine get entity with item

I want to get the field of an entity that is associated with another .
My entity Offers has a last_offer field.
Offers is related to the Products entity.
Then , with a consultation in my entity Products, I want to get the latest offer associated with the entity Offer.
Controller:
public function lastAction($key)
{
$em = $this->getDoctrine()->getManager();
$last_offer = $em->getRepository('MyAppBundle:Products')->findOfferByKey($key);
$response = new JsonResponse();
return $response->setData($last_offer);
}
My repository:
public function findOfferByKey($key){
$em = $this->getEntityManager();
$dql = 'SELECT pr, of FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter('key', $key)
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
return $query->execute();
}
My routing:
last_offer:
path: /{key}/last_offer
defaults: { _controller: "MyAppBundle:Products:last" }
But, this return an array.
I want to return only last_offer element.
Or to return the entity offers without being in an array.
You're specifically telling doctrine to generate/return an array with this line
->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);
I think all you need to do is remove that line and doctrine will generate/return entities instead. However, you will get a Collection back, so make sure to take that into account. Perhaps something like
public function findOfferByKey($key) {
$dql = 'SELECT pr, of FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter('key', $key)
;
$results = $query->execute();
if (count($results) !== 1)
{
// It's up to you how to handle zero or multiple rows
}
return $results->current();
}
EDIT
I see what's happening - I wasn't paying attention to your SELECT caluse. You're not selecting just the last_offer columns, you're selecting the entirety of the Products and Offers entities = $results is going to be an array of all of these together. In this scenario, $query->execute() will return an array() instead of a collection.
If you want to select just the Offer entities, you need to modify the SELECT
$dql = 'SELECT of
FROM MyAppBundle\AppBundle\Entity\Products pr
INNER JOIN pr.offers of
WHERE pr.key = :key';
But be wary that this still may return more than one row.

Yii2 hasMany custom condition

I have sql condidtion SELECT * FROM (SELECT * FROM Prices WHERE aliasId = :aliasId order by id desc) p1 group by p1.currency and I am trying to use it in hasMany statement.
$q = $this->hasMany(Prices::className(), ['aliasId' => 'id']);
$db = \Yii::$app->db;
$query = $db
->createCommand('SELECT * FROM (SELECT * FROM Prices WHERE aliasId = :aliasId order by id desc) p1 group by p1.currency')
->bindValue(':aliasId', $this->id);
$query->prepare(true);
$q->sql = $query->getRawSql();
return $q;
But $this->id is empty when hasMany calling. Is there any way to bind custom query and link array there?
UPDATE.
I know that the reason of $this->id is empty, because I'm using Prices::find()>with('prices') in my Controller, so Yii creates query for all prices list. hasMany just adds addWhere('in', $key, $value) in empty query from $link parameter, I'm trying to override his query, but I can't.
$this->id is empty for new PriceAlias instances, and it's filled only after the model is saved in db - you are getting an empty value most likely because getPrices() is called before the model is saved in db.
You can test if $this->id != null or $this->isNewRecord == false before building the custom command, otherwise return null, an empty array or as required.
UPDATE 1: not sure I fully understand your update,
Prices::find()>with('prices') does create a WHERE ... IN (...) query, but
hasMany does not add an addWhere rule, it creates a relation for the ActiveRecord class. In your case:
$this->hasMany(Prices::className(), ['aliasId' => 'id'])
// generates: SELECT * FROM `prices` WHERE `aliasId` = :id
And the query is executed only when you specifically call getPrices() for an object.
So your problem is? after $q->sql = $query->getRawSql(); statement, $q->sql is not SELECT * FROM (SELECT * FROM Prices WHERE aliasId = :aliasId order by id desc) p1 group by p1.currency ?
UPDATE 2: I understand now. I can't think of any way of using Prices::find()->with() on relations with custom sql, at least not as the one you would like to use.
I can only suggest to find an alternative to find()->with() in your controller if you need to keep the custom query.
From official doc:
$subQuery = (new Query())->select('id')->from('user')->where('status=1');
// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u
$query->from(['u' => $subQuery]);
In your case it should be something like this:
$subQuery = (new Query())->select('*')->from('Prices')->where('aliasId = :aliasId', ['aliasId'=>$aliasId])->orderBy('id');
$query->from(['p1' => $subQuery])->groupBy('p1.currency');

How can I check whether an array parameter is empty?

I want to select users from a database with Doctrine and Symfony. Depending on whether I have a supplied list of user IDs I want to only select users with these IDs. If the list is empty, then all users should be selected.
Here is the code I have created so far:
class UserRepository extends EntityRepository {
public function selectUsers (array $userIds) {
$dql = "
SELECT
u
FROM
MyBundle:User
WHERE
u.id IN (:users)"; // OR (:users) does not contain any values
$query = $this
->getEntityManager()
->createQuery($dql)
->setParameter("users", $userIds);
return $query->getResult();
}
}
How can I check whether the array is empty? So far, I have tried IS EMPTY, = (), = [], SIZE(:users) = 0, COUNT(:users) = 0 but all of them give me errors. What is the correct syntax here?
You can dynamically build DQL query
public function selectUsers(array $userIds)
{
$dql = "SELECT u FROM MyBundle:User";
$params = array();
if ($users) {
$dql .= " WHERE u.id IN (:users)";
$params["users"] = $userIds;
}
return $this->getEntityManager()->createQuery($dql)->execute($params);
}
You are building two different queries - select all, select specific users. I think you cannot build such SQL query. Maybe DQL has some shortcut how you can do it, but I would prefer SQL-ish syntax.
The only solution I have come up with so far is to calculate the count in PHP and pass it in as an additional parameter.
class UserRepository extends EntityRepository {
public function selectUsers (array $userIds) {
$dql = "
SELECT
u
FROM
MyBundle:User
WHERE
u.id IN (:users) OR :userCount = 0";
$query = $this
->getEntityManager()
->createQuery($dql)
->setParameter("users", $userIds)
->setParameter("userCount", count($userIds));
return $query->getResult();
}
}
However, I have a hard time believing that this is impossible to do directly in DQL.

select in application give me an empty array but in database give rows(Codeigniter)

My model:
public function getSolServicoById($id){
$select = 'SELECT * FROM solicitacao_servico WHERE id_solicitacao = "$id" LIMIT 1';
$query = $this->db->query($select);
return $query->result();
}
My controller:
public function editaSolicitacao($id){
$this->load->model('Pedido_Model','pedido');
echo $id;
$data = $this->pedido->getSolServicoById($id);
print_r($data);
}
When i select it on database i receive rows but when i select in application i get empty array and i don't know why it happen?!
Try this :
$select = "SELECT * FROM solicitacao_servico WHERE id_solicitacao = '{$id}' LIMIT 1";
Also look forward to using prepared statements to reduce sql-injection vulnerability.
A better way to do this, just because its a simple query in CI is:
$this->db
->select('*')
->from('solicitacao_servico')
->where('id_solicitacao',$id)
->limit(1)
->get();
Doing it this way doesn't constrain your code to a particular database type (MySQL, MSSQL, etc) because it will create the correct syntax for your application with the built in active record feature.

Categories