Get count of relations from eloquent collection - mysql

I have Call model. Each call has Theme. How i did this:
class Call extends Model
{
// some code here
public function theme()
{
return $this->hasOne(Theme::class, 'id', 'theme_id')->withTrashed();
}
// some code there
}
This works great. What i want to do is get count of each themes used in all calls.
There is 200 calls for example. 100 of them has theme_id=3, 50 of them has 6, and last 50 has 8. I want to get result like:
{
{theme_id:3, themes_count=100},
{theme_id:6, themes_count=50},
{theme_id:8, themes_count=50},
}
How to do that?

\DB::select("select theme_id, count(*) themes_count from calls group by theme_id");
Or using QueryBuilder:
Call::selectRaw("count(*) themes_count, theme_id")
->groupBy("theme_id")
->get();

In eloquent query builder:
$collection = Theme::groupBy('theme_id')
->selectRaw('theme_id, count(*) as themes_count')
->get();

Related

Laravel - Query builder with subquery

So i have a pivot table like the following :
match id
player id
score
And i want to query to get the number of wins/losses for a given user id. (wins based on user id with the highest score for a game)
In sql i would write this like :
SELECT user_id, score
from match_user matchu1
where score = (select max(score) from match_user matchu2 where matchu1.match_id = matchu2.match_id)
How would i express this query in laravel, or is there a better method of doing this that i am missing ?
There are multiple ways to achieve this. The easiest and cleaniest way to me is defining
a relationship with pivot.
class Match extends Model
{
public function players()
{
return $this->belongsToMany(User::class, 'match_user')->withPivot('score');
}
public function winner()
{
return $this->players
->sortByDesc(function ($player) {
return $player->pivot->score;
})
->first();
}
}
Then you can simply get the winner by saying:
$match->winner();
This is to answer your true intention of asking this question, which is to get the number of wins for a single user, as you commented on my answer. The following the the best solution that I can think of for now:
class Match extends Model
{
public function scopeWonBy($query, User $user)
{
return $query->selectRaw('matches.id, max(match_user.score) AS max_store, match_user.player_id AS player_id')
->join('match_user', 'matches.id', '=', 'match_user.match_id')
->groupBy('matches.id')
->having('player_id', $user->id);
}
}
Later on, you can say:
$matches = Match::wonBy($user);
$count = Match::wonBy($user)->count();
I'm not going to write your query for you as I don't want to provide an untested solution but the following code example should give you a good idea of how to implement subqueries with the query builder.
$q->where('price_date', function($q) use ($start_date)
{
$q->from('benchmarks_table_name')
->selectRaw('min(price_date)')
->where('price_date', '>=', $start_date)
->where('ticker', $this->ticker);
});

Convert mysql query with left join into Eloquent

I have a mysql query like this:
select result.*, rank_text.text
from cache_user_challenge_result result
left join rank_text on result.rank
between rank_text.rank_from and rank_text.rank_to
Now I want to change this to eloquent for my laravel project, I tried this but it's not work:
$this->select(
"{$this->table}.user_id",
'rank_text.text'
)
->leftJoin('rank_text', "{$this->table}.rank")
->whereBetween("{$this->table}.rank", ['rank_text.rankFrom', 'rank_text.rankTo'])
->first();
Can you tell me what's wrong with this?
Thank you very much!
Update:
After a lot of hours, I change my query to this:
$this->select(
"{$this->table}.user_id",
'rank_text.text'
)
->leftJoin('rank_text', function($query) {
$query->whereBetween("{$this->table}.rank", ['rank_text.rank_from', 'rank_text.rank_to']);
})
->get();
But it's only can get user_id, not the text.
Try to create cache_user_challenge_result model Like
class CacheUserChallengeResult extends Model {
protected $table = 'cache_user_challenge_result';
public function scopeGetCacheUserRank()
{
return static::select(DB::raw('cache_user_challenge_result.*,rank_text.text'))->leftJoin('rank_text', function($join) {
$join->on('cache_user_challenge_result..rank', '=', 'rank_text.rank')->whereBetween('cache_user_challenge_result.rank',['rank_text.rankFrom', 'rank_text.rankTo']);
});
}
}
In Controller You can call like this
CacheUserChallengeResult::getCacheUserRank()->get();
It will provide you the collection for the rank you desire, also you can pass param for which rank you want in range

Laravel join use to get one to many relations?

Is there any way to get relational data ( one to many relation records ) using joins in laravel without creating different records in collection :
Example:
$orders = DB::table('orders')
->join('users', 'users.id', '=', 'orders.users_id')
->join('order_items', 'order_items.order_id', '=', 'orders.id')
->select('users.*', 'order_items.*')
->get();
So here what's happening is that its creating 6 records if order has 6 items but i want something like single records in which it has array or collection where are order items are listed.
Output I want is generally of this order:
Collection {# ▼
#items: array:[▼
0 => {
+'id':1,
...,
+ 'items: [
//here i want all the records of relation order items
]
}
]
}
Is there any way to achieve this result without using with() or load() and just only with joins or raw queries?
You should try this:
$orders = DB::table('orders')
->select('users.*', 'order_items.*')
->leftJoin('users', 'users.id', '=', 'orders.users_id')
->leftJoin('order_items', 'order_items.order_id', '=', 'orders.id')
->get();
This answer is speculative, but perhaps you want to report a single row for each user, with a CSV list of order items:
$orders = DB::table('orders AS o')
->join('users AS u', 'u.id', '=', 'o.users_id')
->join('order_items AS oi', 'oi.order_id', '=', 'o.id')
->select('u.id', DB::raw('GROUP_CONCAT(oi.name) AS orders'))
->groupBy('u.id')
->get();
If there's no reason stopping you from using the Eloquent, is the best way for working with databases.
The Eloquent ORM included with Laravel provides a beautiful, simple
ActiveRecord implementation for working with your database
Here is the Documentation Eloquent: Getting Started
The model relationship made easy in laravel, you can get you want as following:
The User model relationships:
//user orders
public function orders() {
return $this->hasMany('App\Order');
}
The Order model relationships:
//order items
public function items() {
return $this->hasMany('App\OrderItem');
}
//order owner
public function user() {
return $this->belongsTo('App\User');
}
The OrderItem model relationships:
//order
public function order() {
return $this->belongsTo('App\Order');
}
Here is a quick example of how you may get the order's items
$orders = User::find($id)->orders
foreach($orders as $order) {
$orderItems = $order->items;
}
you can make the collection the way you prefer, but strongly recommend using Eloquent Resources, since most apps these days expecting JSON responses.

Laravel: orderBy relationship's field

I have an issue with ordering model by it's relationship's latest object field.
I have a class named Coin that is related to DailyPrices, that 1 coin can have many DailyPrices. I want to sort coins by latest DailyPrices field named vol.
I tried doing
$coins = Coin::join('daily_prices', 'daily_prices.coin_id', '=', 'coins.id')
->orderBy('daily_prices.vol', 'desc')
->paginate(100);
and many variations about it, but I can't get it to work. What am I doing wrong?
why don't you use created_at instead of vol
$coins = Coin::join('daily_prices', 'daily_prices.coin_id', '=', 'coins.id')->order_by('created_at', 'desc')->paginate(100);
You can do with creating two separate model for coin and daily price.
class Coin extends Model {
public function dailyPrices(){
return $this->hasMany('App\DailyPrice')->orderBy('vol', 'DESC');
}
}
class DailyPrice extends Model {
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'daily_prices';
/**
* Get the coin that owns the daily price.
*/
public function coin()
{
return $this->belongsTo('App\Coin');
}
}
and call with ID in your controller like this
$coinData = Coin::find($coin_id)->with('dailyPrices')->first();
You made two mistakes in your code:
Condition check
Not select table
Here is a code after making changes:
$order='desc';
Coin::join('daily_prices', 'coins.id', '=', 'daily_prices.coin_id')->orderBy('daily_prices.vol', $order)->select('coins.*')->paginate(100);
You can also see more details from here:Order by on relationship
You can use One to Many relationship :
class Coin {
public function dailyPrices() {
return $this->hasMany(DailyPrice::class);
}
}
DailyPrice :
class DailyPrice {
public function coin() {
return $this->belongsTo(Coin::class);
}
}
Then in your controller :
Coin::with('dailyPrices' => function ($query) {
$query->orderBy('vol', 'desc');
))->paginate(100);
The with function will load dailyPrice relationship on your item and the value argument will execute a query related to the relation loaded (here an order by).
Hopes it helps you.
Maybe the question was unclear, I don't know. But neither of these were relevant to an issue. I have resolved the problem with this query:
SELECT c.* FROM daily_prices AS dp JOIN coins AS c ON dp.coin_id=c.id WHERE dp.id IN (SELECT MAX(id) FROM daily_prices GROUP BY coin_id) ORDER BY vol DESC
and transformed to laravel call:
Coin::join('daily_prices', 'daily_prices.coin_id', '=', 'coins.id')
->whereIn('daily_prices.id', function ($query) {
$query->selectRaw('MAX(id)')
->from('daily_prices')
->groupBy('coin_id');
})
->orderBy('daily_prices.vol', 'desc')
->paginate(100);

Count where query in Laravel

I am having a issue here that I am not solving.
I am trying to make a query in Laravel where I need to count how many offers does a business has, to show Business id, Total Offers for a Business and Business Name.
$business_offers = DB::table('offers')
->join('businesses', 'offers.id_business', '=', 'businesses.id')
->select(['businesses.id', 'businesses.name', DB::raw('count(offers.id) as total_offers')])
->orderby('total_offers', 'DESC')
->get();
but it is not working in this way. I have also done the query in mysql but can't integrate it in Laravel.
SELECT bu.id,(SELECT count(of.id) from offers of where of.id_business = bu.id )
as total_offers, bu.`name` from businesses bu ORDER BY total_offers DESC
thanks in advance
Reading it literally something like the following might work:
$business_offers = DB::table('businesses bu')
->select(['bu.id', DB::raw("(SELECT count(of.id) from offers of where of.id_business = bu.id )") ,'bu.name'])
->orderBy('total_offers', 'DESC')
->get();
If you want it a bit more efficient (maybe):
$business_offers = DB::table('offers')
->join('businesses', 'offers.id_business', '=', 'businesses.id')
->select(['businesses.id', DB::raw("COUNT(1) as total_offers"),'businesses.name'])
->groupBy("businesses.id","businesses.name")
->orderBy('total_offers', 'DESC')
->get();
If you have models and relations set up it should be as easy as:
$businesses = Business::withCount(['offers'])->get;
foreach ($businesses as $business) {
echo $business->offers_count;
}
From the Laravel documentation:
If you want to count the number of results from a relationship without
actually loading them you may use the withCount method, which will
place a {relation}_count column on your resulting models.
All you need in your Business model is:
class Business extends Model
{
/**
* Get all of the offers for the business.
*/
public function offers()
{
return $this->hasMany('App\Offer');
}
}
and in the Offer model:
class Offer extends Model
{
/**
* Get the business for the current offer.
*/
public function business()
{
return $this->belongsTo('App\Business', 'business_id');
}
}