Laravel: orderBy relationship's field - mysql

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

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

Sort the parent model based on the child model / relationship

I have a model called appointments, each appointment has an option_id that is linked through a one to one relationship and the option_id can also be null. The option model has a property datetime_start. I want to sort the appointments based on the option.datetime_start.
Here is my code :
$appointments = $user->appointments()
->with(['option'
])
->get();
Edit :
Appointment model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Appointment extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function option()
{
return $this->hasOne(Option::class, 'id', 'option_id');
}
}
Option model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Option extends Model
{
protected $fillable = ["appointment_id",
"datetime_start"
];
public function appointment()
{
return $this->belongsTo(Appointment::class, 'id','appointment_id');
}
}
Any help would be greatly appreciated.
In order to sort Appointment model by a column from related Option model, you need to add a JOIN to your query so that you can sort by related fields:
$appointments = $user->appointments()
->leftJoin('options', 'options.appointment_id', '=', 'appointments.id')
->orderBy('options.datetime_start', 'ASC')
->with(['option'])
->get();
This will give you all appointments, including those without Option - the ones without an Option and, hence, without datetime_start, will be all returned either at the beginning or the end of the result list, you'll need to check that. If you only want appointments with Option, replace leftJoin() with join().
$appointments = Appointment::with(['option' => function ($query){ $query->orderBy('datetime_start', 'desc'); } ])->get();

Laravel: Select from Database

in my Laravel App I have two tables:
Projects
- id
- user_id
- name
- etc...
Images
- id
- project_id
- url
How can I achieve it, to show all the Projects each user has and all the connected images (each project can have up to 20 images (stored in FTP) and Link in Field URL - the project_id from table "Projects" will be saved in field images.project_id)?
I learned, that I can show the projects like this:
$projects = DB::table('projects')->where('user_id','=',$user->id)->get();
and I tried with
$images = DB::table('images')->where('project_id','=',$projects->id)->get();
But I get an error message:
Property [id] does not exist on this collection instance.
What I am missing? Thank you for pointing me into the correct direction :-)
Kind Regards,
Stefan
For your question i suggest to use eloquent way like set up your models
class Project extends Model
{
public function images()
{
return $this->hasMany(\App\Models\Image::class, 'project_id');
}
public function user()
{
return $this->belongsTo(\App\Models\User::class, 'user_id');
}
}
class Image extends Model
{
public function project()
{
return $this->belongsTo(\App\Models\Project::class, 'project_id');
}
}
Now to find projects with their images you can query as
$projects = Project::with('images')->get();
Each object in $projects will have collection of their associated images.
To add filter for user you can use whereHas on relations
$projects = Project::with('images')
->whereHas('user', function ($query) use ($user) {
$query->where('id', '=', $user->id);
})->get();

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