Order by calculated fields in Doctrine QueryBuilder - php

I have the following fields in one of my entities: price(decimal), promo(boolean), and promoPrice(decimal) and I want to get query with order by real price, so I need to create query like this one:
SELECT *, (!promo*price + promo*promo_price) as real_price
FROM `product` ORDER BY real_price ASC
Is there any way to do it with QueryBuilder or maybe I need to use some native methods?

SOLUTION:
$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
$query = $repository->createQueryBuilder('p')
->addSelect('CASE WHEN p.promo = :value THEN p.promoPrice ELSE p.price END AS HIDDEN realPrice')
->setParameter('value', true)
->orderBy('realPrice', 'ASC')
->getQuery();

Related

How to do order by the eloquent query without using join relationship in laravel?

How to order laravel eloquent query using parent model?
I mean I have an eloquent query where I want to order the query by its parent without using join relationship?
I used whereHas and order by on it, but did not work.
Here is a sample of my code:
$query = Post::whereHas('users')->orderBy('users.created_at')->get();
If you want to order Post by a column in user you have to do a join in some way unless you sort after you retrieve the result so either:
$query = Post::select('posts.*')
->join('users', 'users.id', 'posts.user_id')
->orderBy('users.created_at')->get();
Note that whereHas is not needed anymore because the join (which is an inner join by default) will only result in posts that have a user.
Alternatively you can do:
$query = Post::has('users')
->with('users')
->get()
->sortBy(function ($post) { return $post->users->created_at; });
The reason is that eloquent relationships are queried in a separate query from the one that gets the parent model so you can't use relationship columns during that query.
I have no clue why you wanted to order Posts based on their User's created_at field. Perhaps, a different angle to the problem is needed - like accessing the Post from User instead.
That being said, an orderBy() can accept a closure as parameter which will create a subquery then, you can pair it with whereRaw() to somewhat circumvent Eloquent and QueryBuilder limitation*.
Post::orderBy(function($q) {
return $q->from('users')
->whereRaw('`users`.id = `posts`.id')
->select('created_at');
})
->get();
It should generate the following query:
select *
from `posts`
order by (
select `created_at`
from `users`
where `users`.id = `posts`.id
) asc
A join might serve you better, but there are many ways to build queries.
*As far as I know, the subquery can't be made to be aware of the parent query fields
You can simply orderBy in your Post model.
public function users(){
return $this->belongsTo(User::class, "user_id")->orderByDesc('created_at');
}
I hope this helps you.
You can try
Post::query()
->has('users')
->orderBy(
User::select('created_at')
->whereColumn('id', 'posts.user_id')
->orderBy('created_at')
)
->get();
The sql generated would be like
select * from `posts`
where exists (select * from `users` where `posts`.`user_id` = `users`.`id`)
order by (select `created_at` from `users` where `id` = `posts`.`user_id` order by `created_at` asc) asc
But I guess join would be a simpler approach for this use case.
Laravel Docs - Eloquent - Subquery Ordering

Laravel postgres Distinct ON query builder

I would like to make this query the most Laravel way possible by using the query builder.
Thanks
SELECT DISTINCT ON (spaceid) * FROM (SELECT DISTINCT ON (galleryid) * FROM gallery_spaces ORDER BY galleryid, RANDOM()) AS intermediate ORDER BY spaceid, RANDOM() LIMIT 8
Sorry, this is as close as I can get, but it's not the same
$diferentsGalleries = GallerySpace::select(DB::raw('DISTINCT ON(galleryid) *'))
->wherehas('gallery')
->orderByRaw('galleryid, RANDOM()')
->limit(30)
->pluck('id');
$featured_spaces = GallerySpace::select(DB::raw('DISTINCT ON(spaceid) *'))
->with('space')
->with('gallery')
->wherein('id',$diferentsGalleries)
->orderByRaw('spaceid, RANDOM()')
->limit(8)
->get();
I was finally able to figure it out.
$featured_spaces = GallerySpace::select(DB::raw('DISTINCT ON (spaceid) *'))
->fromSub(function ($query) {
$query->from('gallery_spaces')
->select(DB::raw('DISTINCT ON (galleryid) *'))
->orderby('galleryid')
->inrandomorder();}, 'intermediate')
->orderby('spaceid')
->inrandomorder()
->limit(8)
->get();
Laravel offers many ways, none are better than others, having your query the easiet way is to use db facade
$result=DB::select("SELECT DISTINCT ON (spaceid) * FROM (SELECT DISTINCT ON (galleryid) * FROM gallery_spaces ORDER BY galleryid, RANDOM()) AS intermediate ORDER BY spaceid, RANDOM() LIMIT 8");

how to select from subquery using query builder in laravel 5

please help me,
i have query:
SELECT * from (select * from products ORDER BY id DESC LIMIT 20) AS result ORDER BY discount DESC LIMIT 14
so, how to convert into query builder in laravel 5.
tks!
Using the query builder and importing (use) DB, allows you to build and fetch the result you want.
use DB;
$result = DB::table(
DB::raw("(" .
DB::table('products')
->select('*')
->orderBy('id', 'desc')
->limit(20)
->toSql()
. ") as result"
)
)
->select('*')
->orderBy('discount', 'desc')
->limit(14)
->get();
table() selects the table you want to query. In this case we are building up a separate SQL statement to fetch the table.
select() the columns you would like to see.
orderBy($column, $direction) where $column is the name of the column you would like to order and $direction is the order, desc or asc.
limit($limit) only return $limit items to the result set.
toSql() returns the current QueryBuilder SQL statement as a string.
get() returns the data in a Collection.
Also added in the missing discount ordering.
Try something like:
Products::orderBy('id', 'desc')->take(20)->orderBy('discount', 'desc')->take(14)->get();

Symfony Doctrine QueryBuilder add where clause on Join

I have a query builder with the following select:
$starterRepository = $this->getDoctrine()
->getManager()
->getRepository('CatalogBundle:Starter');
$query = $starterRepository->createQueryBuilder('s')
->where('s.active = 1')
->orderBy('s.voltage, s.power, s.serial');
It selects the table "starters", but inside Starter.php I have an association "references" like this:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="StarterReference", inversedBy="starters")
* #ORM\JoinTable(name="starters_references")
*/
protected $references;
So inside my query results I have both "starters" and "starters_references" tables. 1 starter has many starter references. Now the problem is that I need to select not all of the starter references, but only the ones which has some value in column named "ref_usage".
So I need to write where clause in my query, I am trying this:
->where('reference.ref_usage = 1')
But this way I get only one "starter" item with all the references it has included. And I need all the starter items, but with only the references with ref_usage 1.
Any ideas?
Here is the full files and functions I am using.
Controller function:
http://pastebin.com/0tTEcQbn
Entity "Starter.php":
http://pastebin.com/BFLpKtec
Entity "StarterReference.php":
http://pastebin.com/Kr9pEMEW
EDIT:
This is the query I get if I use:
->where('reference.ref_usage = 1')
SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id0 FROM (SELECT s0_.id AS id0, s0_.serial AS serial1, s0_.voltage AS voltage2, s0_.power AS power3, s0_.rotation AS rotation4, s0_.teeth AS teeth5, s0_.module AS module6, s0_.b_terminal AS b_terminal7, s0_.comment AS comment8, s0_.commenten AS commenten9, s0_.commentru AS commentru10, s0_.commentpl AS commentpl11, s0_.commentde AS commentde12, s0_.commentes AS commentes13, s0_.type AS type14, s0_.adaptation AS adaptation15, s0_.alternative_product_1 AS alternative_product_116, s0_.alternative_product_2 AS alternative_product_217, s0_.alternative_product_3 AS alternative_product_318, s0_.alternative_product_4 AS alternative_product_419, s0_.active AS active20 FROM starters s0_ INNER JOIN starters_references s2_ ON s0_.id = s2_.starter_id INNER JOIN starter_reference s1_ ON s1_.id = s2_.starterreference_id WHERE s0_.active = 1 AND s1_.ref_usage = 1 ORDER BY s0_.voltage ASC, s0_.power ASC, s0_.serial ASC) dctrn_result) dctrn_table
As you can see it adds ref_usage = 1 to where clause. But the problem is that I don't need it here, I only need to check ref_usage when I inner join my references.
You should join the references in your query and then add the where clause.
$query = $starterRepository->createQueryBuilder('s')
->join('s.references', 'reference')
->where('s.active = 1')
->andwhere('reference.ref_usage = 1')
->orderBy('s.voltage, s.power, s.serial');

get user with most articles and its amount with active record or query

I want to get the user that wrote the most articles. I do so fine in two ways with ActiveRecord like the following:
$table = Articles::find()
->select('articles.*, COUNT(*) AS cnt')
->with('user','userDetails')
->groupBy('articles.user_id')
->orderBy(('cnt DESC'))
->limit(10)
->offset($offset)
->all();
and with a query like the following:
$query = (new Query())
->select('articles.user_id, COUNT(*) AS num_articles')
->from('articles')
->join('LEFT JOIN', 'user_details', 'user_details.user_id = articles.user_id')
->groupBy('articles.user_id')
->orderBy('num_articles DESC')
->limit(10)
->offset($offset)
->all();
The problem is that the ActiveRecord gives me further needed informations userDetails that I need. But I do not get the amount of articles of user that should be on cnt
With the Query I get the user_id and the amount of articles. But I do not get it working by joining with userDetails. All of these does not work: LEFT JOIN, RIGHT JOIN, INNER JOIN.
I am interested in resolving both for learning, but for concrete I need help with the ActiveRecord problem.
Okay well I solved it for the ActiveRecord. The ActiveRecords needs a public $var; in the Model. So to get the amount you have to add the mentioned public... to your Model so that in my case:
public $cnt; extends the ActiveRecord of Articles
now I can access it with the given Request in my Question. But this just solves the first point. I am still interested in the second way for more complex Queries.
I dont have much idea about active record but I think the below is something what you are looking for
select * from user_details where user_id in
(select A.user from
(select user_id as user, COUNT(*) AS num_articles
from articles group by user_id order by num_articles desc LIMIT 10)A
);
for second point you should include required column from joined table to select statement:
$query = (new Query())
->select('articles.user_id, COUNT(*) AS num_articles, user_details.username as username')
->from('articles')
->join('LEFT JOIN', 'user_details', 'user_details.user_id = articles.user_id')
->groupBy('articles.user_id')
->orderBy('num_articles DESC')
->limit(10)
->offset($offset)
->all();

Categories