Laravel eloquent get relation count - php

I use Laravel 5.3.
I have 2 tables :
Articles
---------
id
cat_id
title
And
Category
---------
id
parent_id
title
I have defined my relations in my models :
// Article model
public function category()
{
return $this->belongsTo(Category::class);
}
// Category model
public function children()
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
Is there an easy way using Eloquent to have a list a categories with count of articles. The difficulty is that I want to group categories where id_parent = 0, i.e. I want to display only parent categories with count of articles in children.
I tried something like that :
$category = new \App\Models\Category();
$categoryTable = $category->getTable();
return $category->leftJoin('article', 'article.cat_id', '=', 'category.id')
->whereIn('article.cat_id', function($query)
{
$query->select('cat_id')
->from('categories')
->where('categories.parent_id', ???)
->orWhere($this->tableName .'.cat_id', $id);
})
->groupBy('cat_id');
But I am lost...

you can use withCount(). It is available from 5.3 version
for more info about eloquent visit : https://laravel.com/docs/5.3/eloquent-relationships

Define a articles() relation in your Category model as:
public function articles()
{
return $this->hasMany(Article::class, 'cat_id');
}
Then you can try it as:
Category::where('parent_id', 0)->withCount('articles')->get();

You can use the hasManyThrough() Eloquent method to fetch all of the childrens' Articles, then add the article counts together in a nice little getter. I added the getter to the $appends array on the model to help illustrate it in the Tinker output.
class Category extends Model
{
protected $appends = [
'articleCount'
];
public function articles()
{
return $this->hasMany(Article::class);
}
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
public function childrenArticles()
{
return $this->hasManyThrough(Article::class, Category::class, 'parent_id');
}
public function getArticleCountAttribute()
{
return $this->articles()->count() + $this->childrenArticles()->count();
}
}
Here's the Tinker output:
Psy Shell v0.8.0 (PHP 7.0.6 — cli) by Justin Hileman
>>> $cat = App\Category::first();
=> App\Category {#677
id: "1",
name: "Cooking",
parent_id: null,
created_at: "2016-12-15 18:31:57",
updated_at: "2016-12-15 18:31:57",
}
>>> $cat->toArray();
=> [
"id" => 1,
"name" => "Cooking",
"parent_id" => null,
"created_at" => "2016-12-15 18:31:57",
"updated_at" => "2016-12-15 18:31:57",
"articleCount" => 79,
]
>>>
If you want to restrict your Category query to ones that have children that have articles, you could do that using the has() method:
Category::has('children.articles')->get();
Here's more on the has() method:
https://laravel.com/docs/5.3/eloquent-relationships#querying-relationship-existence
And the hasManyThrough() method:
https://laravel.com/docs/5.3/eloquent-relationships#has-many-through

This should work:
$category
->where('categories.parent_id', 0)
->leftJoin('article', 'article.cat_id', '=', 'categories.id')
->select('categories.id', \DB::raw('COUNT(article.id)'))
->groupBy('categories.id')
->get();
The above query will get you category IDs and count of all articles that belong to the category.
After reading your question and comments again, if I understand correctly you want to get the count of all articles that belong to those categories (with parent_id = 0) + the count of articles that belong to sub categories (those with parent_id = (id of some category)).
Now I have no way of testing this easily, but I think something along these lines should work for that.
$category
->where('categories.parent_id', 0)
->leftJoin('article', 'article.cat_id', '=', 'categories.id')
->leftJoin('categories as c2', 'c2.parent_id', '=', 'categories.id')
->leftJoin('article as a2', 'a2.cat_id', '=', 'c2.id')
->select('categories.id', \DB::raw('(COUNT(article.id)) + (COUNT(a2.id)) as count'))
->groupBy('categories.id')
->get();
That beign said, I think you're better of having a column named count in categories and update it each time a new article gets added. For performance.

public function NoOfStudent()
{
return $this->hasMany(UserAssignment::class,'assignment_id','id');
}
$assignment = Assignment::select('id','batch_id','title','description','attachment','last_submission_date',DB::raw('(CASE WHEN type = 9 THEN "Quiz Type" ELSE "Descriptive" END) AS assignment_type'),DB::raw('(CASE WHEN status = 1 THEN "Assigned" ELSE "Not Assigned" END) AS status'))
->with('assignmentBatch:id,batch_number')
->where('assignments.instructor_id',auth('api')->user()->id)
->orderBy('created_at','DESC');
if(!$request->user_id){
$assignment =$assignment->withCount('NoOfStudent');
}

In regards to Carlos_E.'s answer.
You can improve the query by using whereHas instead of using whereIn:
$agents = Agents::whereHas('schedule')
)->with('schedules')->get();

I am sure somebody is still going through this, I was able to solve it the following way, suppose I have an Agent model and a Schedule model, i.e. one agent may have many schedules:
class Schedule extends Model {
public function agent() {
return $this->belongsTo(Agent::class, 'agent_id');
}
}
class Agent extends Model {
public function user(){
return $this->belongsTo(User::class);
}
public function schedules(){
return $this->hasMany(Schedule::class);
}
}
Well some agents may not necessarily have schedules assigned, thus, I filtered those before calling the with() method, like this:
$agents = Agents::whereIn(
'id',
Schedule::distinct()->pluck('agent_id')
)->with('schedules')->get();
Hope this helps!.

Related

find reviews has more than one tag. in many to many polymorphic laravel relation

hi I'm trying to use many to many polymorphic but somehow it doesnt work
I couldn't get related reviews in many-to-many polymorphic relation
I want get a review by tags, selected by customer
Table Structure:
review
> id - integer
> name - string
tags
> id - integer
> name - string
taggables
> tag_id - integer
> taggable_id - integer
> taggable_type - string
Models:
class Tag extends Eloquent
{
public function reviews()
{
return $this->morphedByMany(Review::class, 'taggable');
}
}
class Review extends Eloquent
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
the request from customer [tag_id_1,tag_id_2,tag_id_3,tag_id_4]
the request like [1, 2, 3, 4, 5]
array of tags-key
if a review related to this tags find and get the review, i tried something like that
Code for return related reviews:
return Review::join('taggables', 'taggables.taggable_id', '=', 'reviews.id')
->where('taggables.taggable_type', '=', Review::class)
->whereIn('taggables.tag_id', [1, 2, 3, 4, 5])
->groupBy('reviews.id')
->orderBy('name', 'asc')
->get();
OR:
Review::WhereHas('tags', function ($query) {
$query->whereIn('tags_id', [1, 2, 3, 4, 5]);
})->get();
the result i need:
The only reviews that should have these tags
review:{
name: "Review",
tags :[1, 2, 3, 4, 5]
}
laravel eloquent relationships many-to-many-polymorphic
If you want the review that has all tags, this is one way to do it:
$tagIds = [1,2,3,4,5];
$reviews = Review::query();
foreach($tagIds as $id){
$reviews->whereHas('tags', function($query) use ($id) {
return $query->where('id', $id);
});
}
$reviewsWithAllThoseIds = $reviews->get();
//if you don't want to use foreach.
collect($tagIds)->each(function($id) use ($reviews){
$reviews->whereHas('tags', function($q) use ($id) {
return $q->where('id', $id);
});
});
This should give you the reviews that has all tags that has the ids in the array.
you have a typo in your query.correct form of taggables.tagable_id should be taggables.taggable_id. I don't know this is the problem or not but suggest to write your code like below.
in the review model define relations like this:
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable','taggables','taggable_id','tag_id');
}
and in Tag model define relation like :
public function reviews()
{
return $this->morphedByMany(Review::class, 'taggable','taggables','tag_id','taggable_id');
}
and when you want return reviews of a specific tag do it like:
$tag=Tag::find(1);
$tagReviews=$tag->reviews;

laravel orderby nested related model

I have model with name "Date" which has relationship (one-to-one) with "Tour" and "Tour" Model has relationship (many-to-many) with "Type" Model.
I want to order my Date Records Based on "Type" name. Unfortunately I don't have any clue to do it with eloquent.
Date model:
public function Tour()
{
return $this->belongsTo('\App\Tour');
}
Tour model:
public function Date()
{
return $this->hasOne('\App\Date','tour_id');
}
public function Types()
{
return $this->belongsToMany('\App\Type');
}
Type model:
public function Tours()
{
return $this->belongsToMany('\App\Tour');
}
and my controller for output:
public function tourList()
{
$dates = new Date();
$dates = $dates->orderBy('id','asc')
->paginate(6)
->appends([
'sort_price' => request('sort_price'),
'minmax' => request('minmax'),
'type' => request('type')
]);
return view('primary.Tour.list', compact(['dates']));
}
$dateRes = Date::join('type_tour as TT', 'TT.tour_id', '=', 'date.tour_id')
->join('type as T','TT.type_id','=','T.id')
->orderBy('T.name')
->get();
See if it is work for you.
if you want to order the result based on nested relation column, you must use a chain of joins:
$result= Date::join('tours','tours.id','=','dates.tour_id')
->leftJoin('type_tour','type_tour.tour_id','=','tours.id')
->leftJoin('types','types.id','type_tour.type_id')
->orderBy('types.name')->get();
make sure the relation middle table between tours & types table is type_tour
it might be tour_type or something else ...
please note that if you want to order by multiple columns you could add 'orderBy' clause as much as you want:
->orderBy('Types.id', 'DESC')->orderby('Types.name', 'ASC') //... ext
check my answer here:
https://stackoverflow.com/a/61194625/10573560

How OrderBy afterwards HasMany and BelongsTo Relationships

I created a relationship between the "Review, Games and Info" tables, unfortunately, though, the main table is Games, and he orders me all for Games, While I would like to order the ID of "review" table.
Models: Review
public function games()
{
return $this->hasMany('App\Models\Giochi', 'id_gioco', 'id');
}
Models: Giochi
public function review()
{
return $this->belongsTo('App\Models\Review', 'id', 'id_gioco');
}
public function infogiochi()
{
return $this->belongsTo('App\Models\InfoGiochi', 'id', 'id_gioco');
}
Models: InfoGiochi
public function games()
{
return $this->hasMany('App\Models\Giochi', 'id', 'id_gioco');
}
Controller:
$review = Giochi::with('Review','InfoGiochi')->orderBy('id','DESC')->get();
Here is a screenshot of my json:
How do I order content for review table IDs?
You have 2 options. You use a join and order in the sql statement or you order it after retrieving the results in the collection.
Using Join
Giochi::select('giocos.*')
->with('Review','InfoGiochi')
->leftJoin('reviews', 'reviews.id', '=', 'giocos.id_gioco')
->orderBy('reviews.id','DESC')
->get();
Sorting Collection
Giochi::with('Review','InfoGiochi')
->get()
->sortByDesc(function($giochi) {
return $giochi->review->id;
});
This would be the shortest version to sort on the collection:
Giochi::with('review')
->get()
->sortByDesc('review.id');
You can modify your relationship query when you fire it:
Giochi::with([
'Review' => function ($query) { return $query->orderBy('id','DESC'); },
'InfoGiochi'
])->orderBy('id','DESC');
You can try with a raw query or you can use ->orderBy() directly on review function.

Laravel leftJoin not return correctly

I have a problem with laravel select using leftJoin. I'm trying to select 2 posts and count how many comments there is(first post have 7 comments, second - 0), but I got only first post with 7 comments.
Code is:
$posts = DB::table('posts')
->leftJoin('comments', 'comments.post', '=', 'posts.id')
->select(DB::raw('posts.title, posts.body, posts.created_at, posts.slug, CASE WHEN comments.post IS NULL THEN 0 WHEN comments.post IS NOT NULL THEN count(comments.post) END as count'))
->get();
And when I trying to check what i see in web browser i got error:
Call to a member function count() on a non-object
This error in my view file at line where i using #if($posts->count())
I have debugged that i got only one post from using print_r().
Any suggestions?
I think your best bet here is to use some of the built in functionality of laravel's Eloquent ORM.
set up a relationship in the models:
Post.php:
<?php
class Post extends Eloquent {
protected $table = 'posts';
public function comments()
{
return $this->hasMany('Comment', 'posts');//first param refrences the other model, second is the foreign key
}
Comment.php:
<?php
class Comment extends Eloquent {
protected $table = 'comments';
public function comments()
{
return $this->belongsTo('Post');//first param refrences the other model, second is unnecessary if you are using auto incrementing id
}
now you have a relationship set up and there is no need for the join.
Usage:
there may be a better way to do this, but this should work.
$posts = Post::with('comments')->get();//retrieves all posts with comments
foreach($posts as $post){
$count = count($post['comments']);
$post['comment_count'] = $count;
}
return $posts;
this will return a result that contains all of the posts, with a field called 'comments' that contains an array of all of the comments related. the 'comment_count' field will contain the count.
example:
[
{
"id": 1,
"created_at": "2014-07-02 11:34:00",
"updated_at": "2014-07-02 11:34:00",
"post_title": "hello there",
"comment_count": 1,
"comments": [
{
"id":'blah'
"comment_title":"blah"
}
]
}
you can now pass this to your view and loop through each post and get the $post['comment_count']

Laravel / Eloquent : hasManyThrough WHERE

In the documentation of Eloquent it is said that I can pass the keys of a desired relationship to hasManyThrough.
Lets say I have Models named Country, User, Post. A Country model might have many Posts through a Users model. That said I simply could call:
$this->hasManyThrough('Post', 'User', 'country_id', 'user_id');
This is fine so far! But how can I get these posts only for the user with the id of 3 ?
Can anybody help here?
So here it goes:
models: Country has many User has many Post
This allows us to use hasManyThrough like in your question:
// Country model
public function posts()
{
return $this->hasManyThrough('Post', 'User', 'country_id', 'user_id');
}
You want to get posts of a given user for this relation, so:
$country = Country::first();
$country->load(['posts' => function ($q) {
$q->where('user_id', '=', 3);
}]);
// or
$country->load(['posts' => function ($q) {
$q->has('user', function ($q) {
$q->where('users.id', '=', 3);
});
})
$country->posts; // collection of posts related to user with id 3
BUT it will be easier, more readable and more eloquent if you use this instead:
(since it has nothing to do with country when you are looking for the posts of user with id 3)
// User model
public function posts()
{
return $this->hasMany('Post');
}
// then
$user = User::find(3);
// lazy load
$user->load('posts');
// or use dynamic property
$user->posts; // it will load the posts automatically
// or eager load
$user = User::with('posts')->find(3);
$user->posts; // collection of posts for given user
To sum up: hasManyThrough is a way to get nested relation directly, ie. all the posts for given country, but rather not to search for specific through model.
$user_id = 3;
$country = Country::find($country_id);
$country->posts()->where('users.id', '=', $user_id)->get();
$this->hasManyThrough('Post', 'User', 'country_id', 'user_id')->where(column,x);
What happen here is you get the collection in return you can put any condition you want at the end.

Categories