Convert mysql query with left join into Eloquent - mysql

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

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);
});

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.

Get count of relations from eloquent collection

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();

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);

Retrieve data from one table and order by related table column using Laravel eloquent

I have two models Category and Transaction the table structures are like this
Categories:
id,category_name,..
and
Transactions:
id,category_id,amount..
The relation is
Category hasMany transactions
public function transactions()
{
return $this->hasMany('App\Transaction');
}
Transactions blongsTo Category
public function category()
{
return $this->belongsTo('App\Category', 'category_id');
}
I want to retrieve all data of transaction table which are sorted by category name.
Most importantly I want to get it using the eloquent method.
I have tried eager load which I think doesn't work on the belongsTo relationship.
Here is the code I have used for the eager load.
$transactions = Transaction::with(['category' => function ($query) {
$query->orderBy('category_name', 'asc');
}])->paginate(10);
So far I can achieve this by writing a query like below, but I'd like to use the eloquent method.
$transactions = Transaction::select(DB::raw('transactions.*'))
->leftJoin(DB::raw('(select id,category_name from categories) as categories'), 'categories.id', '=', 'transactions.category_id')
->orderBy('category_name', 'asc')
->paginate(10);
It'd be nice if someone can help me with this. Thank You.
Note: I am using Laravel 5.1
You have to provide the method name that defines the relationship, in your case this is category.
$transactions = Transaction::all()->with('category')->group_by('category.name')->get();
$transactions = Transaction::with('categories')->group_by('category.name')->get();
$cs = Course::where(['courses.active' => 1])
->whereHas('course_dates', function ($join) use ($now) {
$join->where('course_dates.start_date_time', '>', $now);
$join->orderBy('course_dates.start_date_time', 'asc');
})
->whereHas('category', function ($join) use ($cat_slug) {
$join->where('categories.url_slug', '=', $cat_slug);
})
->whereHas('language', function ($join) use ($cat_slug) {
$join->where('languages.string_id', '=', strtoupper(App::getLocale()));
})
->with(['course_dates' => function($q){
$q->orderBy('course_dates.start_date_time', 'desc');
}])
->join('course_dates' ,'course_dates.course_id', '=', 'courses.id')
->orderby('course_dates.start_date_time')
->limit(7)
->get();
To return Eloquent models ordered by related model (hasMany) column, I had to join the tables and then orderBy, still get the models, but correctly ordered by course_date.start_date_time.
Laravel 5.7, I don't think there is a cleaner solution (at least after few hours of tinkering and searching the web).