Eloquent get rows where relation count equals column value - mysql

Assuming that i have posts table has field minimum_comments and have query scope in posts table to get the active posts doing something like this
public function scopeActive($query)
{
$query->has('comments', '>=', 'posts.minimum_comments');
}
Putting in consideration that all relation already set up
How can i achieve this?

I haven't test it, but try this:
// Post.php
public function scopeActive($query)
{
return $query->where('minimum_comments', '<=', $this->comments()->count());
}
Then you can go and use it like this:
// PostsController.php
public function myCoolFunction()
{
$posts = Post::active()->get();
// the rest of your logic..
}

Related

How to query multiple rows in a column of a table

Below is my controller and when I dd($types_id); I get an array of two ids. Now the problem is when I pass the variable in the where method to pluck the names of the types associated with the ids, it only fetches the name of the first id instead of the two ids. What may I be doing wrong?
/*Get Type List*/
public function getTypeList(Request $request)
{
$types_id = DB::table("vehicles")
->where("condition_id",1)
->pluck("type_id","id")->unique();
//dd($types_id);
$types = DB::table("type_ids")
->where("type_id", $types_id)
->pluck("name");
//dd($types);
return response()->json($types);
}
Problem 1 - You should be using whereIn for the second query.
Problem 2 - Without selecting any column from table and getting all columns for the collection is bad optimization for the fetching operation. You should be using distinct for the query instead of filtering collection by unique method.
public function getTypeList(Request $request)
{
$types_id = DB::table("vehicles")
->where('condition_id',1)
->select("type_id")
->distinct("type_id")->get()
->pluck("type_id");
$types = DB::table("type_ids")
->select('name')
->whereIn("type_id", $types_id)->get()
->pluck("name");
return response()->json($types);
}
Just change where to whereIn
public function getTypeList(Request $request)
{
$types_id = DB::table("vehicles")
->where("condition_id",1)
->pluck("type_id","id")->unique();
//dd($types_id);
$types = DB::table("type_ids")
->whereIn("type_id", $types_id)
->pluck("name");
//dd($types);
return response()->json($types);
}

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 5.6 - eager load depending on value in parent table

Post.php
$fillable = ['id','flag'];
public function tags()
{
return $this->belongsToMany('App\Tags')->withPivot('pivot_flag');
}
public function flaggedTags()
{
return $this->tags()->where('pivot_flag', 1)->get();
}
Tag.php
$fillable = ['id']
public function posts()
{
return $this->belongsToMany('App\Post');
}
Pivot table post_tag columns: post_id,tag_id,pivot_flag
I need to fetch Post with Tags:
$post = Post::select('id','flag')->with('tags')->find($postId);
However, if the flag is set to 1 in Post then I only want tags which have pivot_flag value in pivot table post_tag set to 1
So in that case I'd have following:
$post = Post::select('id','flag')->with('flaggedTags')->find($postId);
How can I do this in single query? I could always do one query to check if the Post is flagged or not and then run the appropriate query for Tags, but that seems wasteful.
Eloquent or raw MySQL queries are welcome.
Thanks!
UPDATE - Alternative solution
Load all tags and then filter the result depending on Post's flag value:
if($post->flag)
{
$tags = $post->tags->filter(funtction($q) {
return $q->pivot->pivot_flag == 1;
});
}
else
{
$tags = $post->tags;
}
This might be faster depending on number of tags. Not really sure, need to test it though. Any feedback about this in comments is welcome.

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

Include specific column from related-models table

Using Laravel 5.1: Given two related models, User and Item, with through table Item_User, how can I include a specific column from the through table, item_count using a with statement?
Item_User table:
Query:
$userGameProfile = User::with([
'items' => function($query) use ($profile_id) {
$query->with(['phrases'])->where('profile_id', '=', $profile_id);
}])->find($id);
Adding ->select('item_count') to this query only returns item count, and not the related item objects. Adding ->select('*') gets me the items and fields I want, but breaks the nested with(['phrases']) relationship I need to get related phrases to items.
I was told I should use withPivot, but I cannot find documentation or examples on how to use this.
Preferably, I'd like to add this to the relationship, rather than each query.
Models:
User:
public function items()
{
return $this->belongsToMany(Item::class, 'user_item');
}
Item:
public function users()
{
return $this->belongsToMany(User::class, 'user_item');
}
public function phrases()
{
return $this->belongsToMany(Phrase::class, 'item_phrase');
}
Phrase:
public function items()
{
return $this->belongsToMany(Item::class, 'item_phrase');
}
This article highlights how to include additional information from the pivot table.
In User model, I know I will always want to include item_count from user_items pivot table, so you can add withPivot to the relation to items:
public function items()
{
return $this->belongsToMany(Item::class, 'user_item')
->withPivot('item_count');
}
User now comes back with items and pivot data: