I have a tournament, a tournament can have many >
public function championships()
{
return $this->hasMany(Championship::class);
}
and a Championship hasOne Category. In Category, I have the isTeam attribute.
Now I need a function that get me all the championships that have the isTeam = 1 in Category table.
public function teamChampionships()
{
}
Of course, I have defined : $tournament->championships, $championship->category
In my controller, I get all of them:
$tournament = Tournament::with('championship.category')->find($tournament->id);
Any idea???
Try
$tournament = Tournament::with(['championships' => function ($query) {
$query->whereHas('category', function($subquery) {
$subquery->where('isTeam', '=', 1);
});
}])->get();
If the above doesn't work, try a different approach. Define isTeam() scope in Category model
public function scopeIsTeam($query) {
return $query->where('isTeam', 1);
}
Then you can use it like this
$tournament = Tournament::with('championships.categoryIsTeam')
->find($tournament->id);
Even better, create another scope in Championship that loads only teams
public function categoryTeam() {
return $this->hasOne(Category::class)->isTeam();
}
Sorry for too much information. One of those should do the job.
Related
I have 2 models: publictxt and Pulicetxtrecive
publictxt model:
class Publictxt extends Model
{
protected $fillable=['sender','reciver','description','useridseen'];
public function Publictxtrecives()
{
return $this->hasMany(Pulicetxtrecive::class,'publictxt_id', 'id');
}
}
Pulicetxtrecive model:
protected $fillable=['publictxt_id','user_id','seen'];
public function publictxts()
{
return $this->belongsTo(Publictxt::class);
}
I want to get the values from the Publictxt that are available in the Pulicetxtrecive.
When a record is stored in the Publictxt, users are registered in the Pulicetxtrecive after viewing.
$pulictxtcount=Publictxt::where('reciver',Auth::user()->shift)->orwhere('reciver',1)->with('Publictxtrecives')->whereHas('Publictxtrecives',function($q){$q->where('seen', '=', 0);})->count();
this code doesn't work.
There are some conflicts in your database structure.
You said when a user sees a letter the Publictxtrecives will be created.
That means if a Publictxt has a Publictxtrecives that definitely has been seen .
But there is a seen column in Publictxtrecives table.
You should pick one.
But anyway as this structure:
$pulictxtcount=Publictxt::where(
function($query){
$query->where('reciver',Auth::user()
->shift)->orwhere('reciver',1);
})
->Where(function($query)
{
$query->whereHas('Publictxtrecives',
function($q){$q->where('seen',1);
}
)->orWhereDoesntHave('Publictxtrecives');
})
->with('Publictxtrecives');
$pulictxtcount = Publictxt::with([
'Publictxtrecives' => function($query){
$query->where('seen', 0);
}
])
->where('reciver', Auth::user()->shift)
->orwhere('reciver', 1)
->get();
I solved this problem:
$pulictxtcount = Publictxt::where(function($q){$q->where('reciver',Auth::user()->shift)->orwhere('reciver',1);})->whereDoesntHave('Publictxtrecives', function($q){$q->where('user_id', Auth::user()->id);
})->count();
I have a list with gamers and another table with game stats.
My list code is:
$gamers = Gamer::with(['lastGameStat' => function($query) {
$query->orderBy('total_points', 'DESC');
}])->paginate(20);
relation:
public function lastGameStat() {
return $this->hasOne(GameStat::class, 'gamer_id', 'id')->orderBy('created_at', 'DESC');
}
in relation table I have field: total_points and with this code I thought it's possible to sort list of gamers by total_points $query->orderBy('total_points', 'DESC');
It doesn't work, can somebody give me an advice here how can I sort the result on a field from relation table?
I guess you'll need either another relation or custom scopes to fetch various game stats of a gamer.
Second relation
Gamer.php (your model)
class Gamer
{
public function bestGameStat()
{
return $this
->hasOne(GameStat::class)
->orderBy('total_points', 'DESC');
}
}
Custom scopes
Gamer.php
class Gamer
{
public function gameStat()
{
return $this->hasOne(GameStat::class);
}
}
GameStat.php
use Illuminate\Database\Eloquent\Builder;
class GameStat
{
public function scopeBest(Builder $query)
{
return $query->orderBy('total_points', 'DESC');
}
}
In your controller:
$gamersWithTheirLatestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->latest();
}])->paginate(20);
$gamersWithTheirBestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->best();
}])->paginate(20);
Be aware as this is untested code and might not work.
I have a User model with a Credits relation.
public function credits()
{
return $this->hasMany('App\Credit');
}
I'd like to return all users where their credit balance is greater than 0. Right now, I have two methods; one to retrieve the total credits the user has amassed and another to retrieve the credits the user has spent.
public function creditsIncome()
{
return $this->credits->where('type', 0)->sum('amount');
}
public function creditsExpense()
{
return $this->credits->where('type', 1)->sum('amount');
}
To get the balance, I have a third method:
public function creditsBalance()
{
return $this->creditsIncome() - $this->creditsExpense();
}
Is there any way to do something like User::where('creditsBalance', '>', 0);?
You can use a modified withCount():
User::withCount([
'credits as income' => function($query) {
$query->select(DB::raw('sum(amount)'))->where('type', 0);
},
'credits as expense' => function($query) {
$query->select(DB::raw('sum(amount)'))->where('type', 1);
}
])->having(DB::raw('income - expense'), '>', 0)->get();
i have three table one is category table and another is product table and one more product_to_category table, and it has only product_id and category_id column.
Now i want to get top 10 categories with maximum number of product, with details of 10 products from each category.
What i wrote is
$result = ProductToCategory::groupBy('category_id')->with(['product',function($q){
$q->take(10);
}])->orderBy('category_id)->take(10);
But this is not working.How to write this query properly
Can anyone please help. ty
Model relations
For Product model
public function category(){
return $this->belongsTo(ProductToCategory::class);
}
For Category model
public function products()
{
return $this->hasMany(ProductToCategory::class);
}
For ProductToCategory model
public function product()
{
return $this->hasMany(Product::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
The most efficient way would be using a raw SQL query because you can't filter products by using eager loading constraint.
But if you want an Eloquent solution anyway, define the relationships:
In the Product model:
public function categories()
{
return $this->belongsToMany(Category::class, 'product_to_category');
}
And in the Category model:
public function products()
{
return $this->belongsToMany(Product::class, 'product_to_category');
}
Then you'll have two options, both have their pros and cons:
1. This code will execute just 2 queries but will use more memory. You could get top ten categories with their products:
$categories = Category::withCount('products')->latest('products_count')->take(10)->with('products')->get();
And then keep only first ten products:
$categories->transform(function($category) {
$topProducts = $category->products->take(10);
unset($category->products);
$category->products = $topProducts;
return $category;
});
2. This solution will create 12 queries but will save the memory:
$categories = Category::withCount('products')->latest('products_count')->take(10)->get();
$categories->transform(function($category) {
$category->products = Product::whereHas('categories', function($q) use($category) {
$q->where('id', $category->id);
})
->take(10)
->get();
return $category;
});
Here is the DB facade version:
$tenPopularTags = DB::table('product_to_category')
->join('category', 'product_to_category.category_id', '=', 'category.id')
->select(DB::raw('count(product_to_category.category_id) as repetition, question_tag.tag_id'))
->groupBy('product_to_category.category_id')
->orderBy('repetition', 'desc')->take(10)
->get();
However I like #Alexey Mezenin way of doing it. Because that is the cleaner way have customized it a bit:
$tenCategories = Category::withCount('products')->orderBy('questions_count', 'DESC')->take(10)->get();
Have used both in my project blog with post and categories relationship and it works!
I am trying to access the child objects of nested relationships that return many results from the parents object.
Let's say I have 4 models : Country - Provinces - Cities - Municipalities
Their relationships are as follows :
Country Model
class Country extends Eloquent
{
protected $table = 'countries';
public function provinces()
{
return $this->hasMany('Province');
}
}
Province Model
class Province extends Eloquent
{
protected $table = 'provinces';
public function cities()
{
return $this->hasMany('City');
}
public function country()
{
return $this->belongsTo('Country');
}
}
City Model
class City extends Eloquent
{
protected $table = 'cities';
public function municipalities()
{
return $this->hasMany('Municipality');
}
public function province()
{
return $this->belongsTo('Province');
}
}
Municipality Model
class Municipality extends Eloquent
{
protected $table = 'municipalities';
public function cities()
{
return $this->belongsTo('City');
}
}
Now what I am trying to do is get all municipalities in a given country that have a population over 9000 and are located in provinces that are considered West.
So far I have something like this :
$country_id = 1;
$country = Country::whereHas('provinces', function($query){
$query->where('location', 'West');
$query->whereHas('cities', function($query){
$query->whereHas('municipalities', function($query){
$query->where('population', '>', 9000);
});
});
})->find($country_id);
Now I can easily get the provinces with $country->provinces but I can't go any deeper than that.
EDIT1 : Fixing the belongsTo relationship as noticed by Jarek.
EDIT2: In addition to Jarek's answer, I wanted to share what I also found however Jarek's is probably the more proper method.
Instead of trying to go from top to bottom (Country -> Municipality) I decided to try the other way (Municipality -> Country) Here's how it works (and I tested it, also works)
$municipalities = Municipality::where('population', '>', 9000)
->whereHas('city', function($q) use ($country_id){
$q->whereHas('province', function($q) use ($country_id){
$q->where('location', 'West');
$q->whereHas('country', function($q) use ($country_id){
$q->where('id', $country_id);
});
});
})->get();
I have no idea if this is an actual proper way or if performance would be accepted but it seemed to do the trick for me however Jarek's answer looks more elegant.
Your Municipality-City is probably belongsTo, not hasMany like in the paste.
Anyway you can use hasManyThrough relation to access far related collection:
Country - City
Province - Municipality
Unfortunately there is no relation for 3 level nesting, so you can't do this just like that.
Next, your code with whereHas does not limit provinces to west and municipalities to 9000+, but only limits countries to those, that are related to them. In your case this means that result will be either Country (if its relations match these requirements) or null otherwise.
So if you really want to limit related collections, then you need this piece:
$country = Country::with(['provinces' => function($query){
$query->where('location', 'West');
}, 'provinces.cities.municipalities' => function ($query){
$query->where('population', '>', 9000);
}])->find($country_id);
This is applying eager loading constraints, and what it does is:
1. loads only West provinces for country with id 1
2. loads all the cities in these provinces
3. loads only 9k+ municipalities in these cities
Since you're not interested in cities, you could use hasManyThrough on the Province:
// Province model
public function municipalities()
{
return $this->hasManyThrough('Municipality', 'City');
}
then:
$country = Country::with(['provinces' => function($query){
$query->where('location', 'West');
}, 'provinces.municipalities' => function ($query){
$query->where('population', '>', 9000);
}])->find($country_id);
However in both cases you can't access the municipalities directly, but only like this:
// 1 classic
$country->provinces->first()->cities->first()->municipalities;
// 2 hasManyThrough
$country->provinces->first()->municipalities;
That being said, if you'd like to work with all those municipalities, you need this trick:
$country = Country::with(['provinces' => function($query){
$query->where('location', 'West');
}, 'provinces.municipalities' => function ($query) use (&$municipalities) {
// notice $municipalities is passed by reference to the closure
// and the $query is executed using ->get()
$municipalities = $query->where('population', '>', 9000)->get();
}])->find($country_id);
This will run additional query, but now all the municipalities are in single, flat collection, so it is very easy to work with. Otherwise you likely end up with a bunch of foreach loops.