How to get relationship counts without loading objects in laravel - php

I have a model customer and it has many projects. I want to find projects count without including its object.
Customer model includes:
public function numberOfProjects()
{
return $this->hasMany(Project::class)->count();
}
Query in my controller:
$customers = Customer::where(['is_active'=>1])
->with(['customerContactInformation'=> function ($query) {
$query->where('is_active',1);
}, 'numberOfProjects'])
->skip($skip)->take(10)
->get();
Its giving me error:Call to a member function addEagerConstraints() on integer

Try this
Customer Model
public function numberOfProjects()
{
return $this->hasMany(Project::class);
}
Controller
$customers = Customer::where(['is_active'=>1])
->with(['customerContactInformation'=> function ($query) {
$query->where('is_active',1);
}])
->withCount('numberOfProjects') //you can get count using this
->skip($skip)
->take(10)
->get();
That should be work
$customers = Customer::withCount('numberOfProjects')->get();
WithCount on the particular status
$customers = Customer::withCount([
'numberOfProjects',
'numberOfProjects as approved_count' => function ($query) {
$query->where('approved', true);
}
])
->get();

class Tutorial extends Model
{
function chapters()
{
return $this->hasMany('App\Chapter');
}
function videos()
{
return $this->hasManyThrough('App\Video', 'App\Chapter');
}
}
And then you can do:
Tutorial::withCount(['chapters', 'videos'])
Counting Related Models
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. For example:
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
You may add the "counts" for multiple relations as well as add constraints to the queries:
$posts = App\Post::withCount(['votes', 'comments' => function ($query) {
$query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;
You may also alias the relationship count result, allowing multiple counts on the same relationship:
$posts = App\Post::withCount([
'comments',
'comments as pending_comments_count' => function ($query) {
$query->where('approved', false);
}
])->get();
echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;
If you're combining withCount with a select statement, ensure that you call withCount after the select method:
$posts = App\Post::select(['title', 'body'])->withCount('comments');
echo $posts[0]->title;
echo $posts[0]->body;
echo $posts[0]->comments_count;

Related

Laravel Eloquent Model with relationship

I have implemented eloquent relationship in my code but Laravel unable to read the function that I created to map the eloquent relationship in the model.
User Model
public function products(){
return $this->hasMany(Product::class,'userid');
}
Product Model
public function users(){
return $this->belongsTo(User::class);
}
Product Controller
$products = Product::with('Users')->Users()->where('users.isActive',1)->get();
return view('product',compact('products'));
I keep getting error from the product controller, I also attached the error that I current encountered as below.
How can I get all the product and user data with the where condition such as "Users.isActive = 1".
Thanks.
You can use whereHas to filter from a relationship.
$products = Product::with('users')
->whereHas('users', function ($query) {
$query->where('isActive', 1);
})
->get();
Also it is generally a good idea to use singular noun for belongsTo relationship because it returns an object, not a collection.
public function user() {
return $this->belongsTo(User::class);
}
$products = Product::with('user')
->whereHas('user', function ($query) {
$query->where('isActive', 1);
})
->get();
EDIT
If you want to retrieve users with products you should query with User model.
$users = User::with('products')
->where('isActive', 1)
->get();
Then you can retrieve both users and products by
foreach($users as $user) {
$user->products;
// or
foreach($users->products as $product) {
$product;
}
}
You can use whereHas() method for this purpose. Here is the doc
$products = Product::with('users')->whereHas('users', function (Illuminate\Database\Eloquent\Builder $query) {
$query->where('isActive', 1);
})->get();
$users = $products->pluck('users');
return view('product',compact('products'));
You have a typo after the with, is users instead of Users and you're redundant about the Query Builder, remove the ->Users():
Before:
$products = Product::with('Users')->Users()->where('users.isActive',1)->get();
return view('product',compact('products'));
After:
$products = Product::with('users')->where('users.isActive',1)->get();
return view('product',compact('products'));
Fix that and all should work.

using 'And Where' in Many to Many relation

I have two models which have a many-to-many relationship.
class User extends Model
{
function cars()
{
return $this->belongsToMany(Car::class);
}
}
class Car extends Model
{
function users()
{
return $this->belongsToMany(User::class);
}
}
I want to get users who used a specific set of cars:
$car_selected = [1, 3, 6];
$users = User::when(count($car_selected) > 0, function ($q) use ($car_selected) {
$q->whereIn('cars.id', $car_selected);
})
->get();
This gives too many results because of the 'whereIn' condition; what I want is 'whereAnd' something.
I tried this, but no luck.
$users = User::when(count($car_selected) > 0, function ($q) use ($car_selected) {
foreach($car_selected as $xx) {
$q->where( 'cars.id', $xx);
}
})
->get();
How can I get all users which have a relationship to cars 1, 3, and 6?
Your code provided doesn't make a lot of sense, but based on your explanation, you want to find a user who has a relationship with cars 1 and 3 and 6. Using whereIn() gets you users with relationships with cars 1 or 3 or 6.
Your attempt with multiple where() filters wouldn't work, as this would be looking for a single row in the pivot table with multiple cars, which obviously wouldn't be possible. Instead, you need to nest multiple whereHas() relationship filters into a single where() group like this:
$users = User::where(function ($q) use ($car_selected) {
foreach ($car_selected as $car) {
$q->whereHas('cars', function ($query) use ($car) {
$query->where('car_id', $car);
});
}
})
->with(['cars' => function ($q) use ($car_selected) {
$q->whereIn('car_id', $car_selected);
}])
->get();
This is all assuming you've correctly set up your relationships and tables per Laravel standards.
Demo code is here: https://implode.io/anjLGG
You can use the whereHas method to query relationship:
use Illuminate\Database\Eloquent\Builder;
User::whereHas('cars', function (Builder $builder) use($car_selected)
{
$builder->whereIn( 'cars.id', $car_selected);
})->get();
For more infos, check the doc
as #miken32 explained, I need to nest multiple whereHas() relationship filters into a single where() group.
#miken32 proposed following..
$users = User::where(function ($q) use ($car_selected) {
foreach ($car_selected as $car) {
$q->whereHas('cars', function ($query) use ($car) {
$query->where('car_id', $car);
});
}
})
->with(['cars' => function ($q) use ($car_selected) {
$q->whereIn('car_id', $car_selected);
}])
->get();
And I guess following is enough .
$users = User::where(function ($q) use ($car_selected) {
foreach ($car_selected as $car) {
$q->whereHas('cars', function ($query) use ($car) {
$query->where('car_id', $car);
});
}
})->get();
Thanks.

Laravel sortByDesc and Paginate on a relationship

How can I paginate the result here and keep the sort order.
public function show(Language $language)
{
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}])->get()->sortByDesc('translations');
return view('language.show', compact('questions', 'language'));
}
Right now if I try to paginate by doing this, I get an error saying:
Call to undefined method Illuminate\Database\Eloquent\Builder::sortByDesc()
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}])->sortByDesc('translations')->Paginate(20);
I also tried this from this post: how to use pangination and sortByDesc fuction in laravel?
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}])->Paginate(20);
$questions->setCollection($questions->sortByDesc('translations'));
This sort of works because I get the paginated results in a sorted order but it's not what I want. I want all the questions to be first sorted by whether they have translations or not and then paginated.
Note, translations is not a column, it's just the result of the query so I can't use SQL as suggested in the post I linked.
The relationship in question model.
public function translations()
{
return $this->hasMany('App\QuestionTranslation');
}
Ordering the relationship will have no effect on the order of the main model. Relationships are retrieved via a second query which happens after the main model is retrieved.
You can join however:
public function show(Language $language)
{
$questions = Question::selectRaw('questions.*, translations.id')
->leftJoin('translations', function ($join) use ($language) {
$join->on('questions.id', '=', 'translations.question_id')
->andOn('translations.id', '=', $language->id);
})
->orderBy('translations.id') // NULLs should appear last
->paginate(20);
return view('language.show', compact('questions', 'language'));
}
private $limit = 10;
public function show(Language $language)
{
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}]);
$data = $questions->paginate($this->limit);
$data->sortByDesc('translations');
return view('language.show', $data);
}

laravel pagination not working with search

i have this code in my controller
if (request('tag')) {
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->Posts;
} else {
$Posts = Blog::paginate(10);
}
return view('Panel.Posts',['Posts' => $Posts]);
and this is my model function
public function tag(){
return $this->belongsToMany(Tags::class,'Post_tag','Post_id');
}
It's a web application I am creating and it's a blog.
I'm trying to create a category.
I want to paginate my search method but I can't do it.
If I use paginate method it says posts relation is not found in paginate.
I tried this:
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->paginate(10)->Posts;
and this
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->Posts->paginate(10);
Can any one help me to fix this?
try my code
$Posts = Blog::whereHas('tag', function (Builder $query) {
$query->where('name', request('tag'));
})->paginate(25);
you can yse where ha and query builder instans a relation in models
Make sure Tags has relation relation in model. Also use ->posts instead ->Posts, but anyway:
There is no point of using separate query if relation is defined or not. Instead you need combination of when and whereHas.
$posts = Blog::when(request('tag'), function ($q, $tagName) {
return $q->whereHas('tag', function ($q) use ($tagName) {
return $q->where('name', $tagName);
});
})->paginate(10);
When fill apply what is inside it if condifion will match. So it is just replace for "if". Docs https://laravel.com/docs/7.x/queries#conditional-clauses
WhereHas will check if relation model exists and allow you to use "where" (or other queries) on that relation. Docs https://laravel.com/docs/7.x/eloquent-relationships#querying-relationship-existence
If you wanna still use "if" nad not "when":
$posts = Blog::query();
if (request('tag')) {
$posts = $posts->whereHas('tag', function ($q) {
return $q->where('name', request('tag'));
});
}
$posts = $posts->paginate(10);

Filtering Laravel resource collections is there a better way

I am experimenting with creating a Restful API in Laravel and I am making use of the resources feature designed for API output. I have created the following models:
Book, Author and Category. I also created a resource for each of these models.
There is a one to many relationship between author and book and a many to many between category and book which has a pivot table.
I can return a collection of all books easily with the following:
return BookResource::collection(Book::with(['author', 'categories'])->paginate(10));
But I want to easily filter by author and category so I have implemented it in the following way inside my controller:
public function index(request $request)
{
//if no filer params are passed in return all books with author and categories
if (!$request->has('author') && !$request->has('category')) {
return BookResource::collection(Book::with(['author', 'categories'])->paginate(10));
}
//author param is passed in
if($request->has('author') && !$request->has('category')){
$authorName = $request->author;
return BookResource::collection(Book::whereHas('author', function ($query) use ($authorName) {
$query->where('name', $authorName);
})->get());
}
//category param is passed in
if(!$request->has('author') && $request->has('category')){
$categoryName = $request->category;
return BookResource::collection(Book::whereHas('categories', function ($query) use ($categoryName) {
$query->where('name', $categoryName);
})->get());
}
}
Is there a better more efficient way of returning the BookResource collection filtered by author and category?
Please try to implement this way. Hope this helps. Thanks.
public function index(){
$author = request ('author', null);
$category = request ('category', null);
$books = Book::with(['author', 'categories'])->when($author, function ($query) use ($author) {
return $query->whereHas('author', function ($query) use ($author){
$query->where('name', $author);
});
})->when($category, function ($query) use ($category) {
return $query->whereHas('categories', function ($query) use ($category) {
$query->where('name', $category);
});
})->paginate(10);
return BookResource::collection($books);
}

Categories