Laravel newQuery() - php

I've been looking at the newQuery eloquent model and best case uses and I can see the benefit in searching / filtering products based on a search page however is it possible to call the newQuery on only the Users related products?
For example, I have 2 models.
User
Product
User has many products and I have the relationship defined on the user model.
public function products() {
return $this->hasMany('App\Product');
};
Now, previously if I wanted to filter all the products and take user out of the scenario I could use:
$query = (new \App\Product)->newQuery();
if($request->get('category')){
$query->whereHas('category',function($q) use($request){
$q->where('category_id',$request->get('category'));
});
}
$products = $query->get();
This is great and I like this method and I now want to have a similar functionality on only users products.
For example, id like to:
$products = (Auth::user()->products)->newQuery();
if($request->get('category')){
$products->whereHas('category',function($q) use($request){
$q->where('category_id',$request->get('category'));
});
}
$products = $query->get();
But I can't do this I get newQuery() method is not available.
Is there a better way of performing optional queries based on parameters?

Change your code to this to make it work:
$products = Product::where('user_id', auth()->id());
if (request('category')) {
$products = $products->whereHas('category', function($q) {
$q->where('category_id', request('category'));
});
}
$products = $products->get();
Alternatively, you could just load related products using lazy eager loading:
auth()->user()->load(['products' => function($q) {
if (request('category')) {
$q->whereHas('category', function($q) {
$q->where('category_id', request('category'));
});
}
}]);

Just for a bit of neatness you can use the when() method on the builder class too
auth()->user()->products()
->when($request->category, function ($query) use ($request) {
$query->whereCategoryId($request->category);
})->get();
or
Product::whereUserId(auth()->id())
->when($request->category, function ($query) use ($request) {
$query->whereCategoryId($request->category);
})->get();

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

Laravel 5.1 constrain and eager load multiple nested relations

I have four models: OwnerCompany, Owner, User, and Role.
I want to get all OwnerCompanys eager loading their Owner and also eager loading its Users who have the Role with the name 'admin'.
OwnerCompany::with('owner.users')->whereHas('owner.users.roles', function ($query) {
$query->where('name', 'admin');
})->get();
This loads in the models but doesn't constrain the users, they are all loaded.
Not tested . I think you are missing like or equal operator.
OwnerCompany::with('owner.users')->whereHas('owner.users.roles', function ($query) {
$query->where('name','like','admin');
})->get();
I try this, if work then let me know. not tested
1).
OwnerCompany::with('owner.users.roles')->whereHas('owner', function ($query) {
$query->whereHas('users', function($qr){
$qr->whereHas('roles', function($q){
$q->where('name', 'admin');
});
});
})->get();
OR
2).
OwnerCompany::with('owner.users.roles')->whereHas('owner.users.roles', function ($query) {
$query->where('name', 'admin');
})->get();
If I got it right, you want each OwnerCompany object to contain the Owner object, which will contain "admins". I would do something like this.
$ownerCompany = OwnerCompany::all();
return $ownerCompany->each(function ($company) {
$company->owner;
$company->owner->users->each(function ($user) {
return $user->hasRole('admin');
});
});
and inside User class:
public function role()
{
return $this->belongsTo(Role::class);
}
public function hasRole($role)
{
if ($this->role()->where('name', $role)->first()) {
return $this;
}
return null;
}
Unfortunately, I can't test it right now, so If you have any trouble let me know.

Categories