Laravel sortByDesc and Paginate on a relationship - php

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

Related

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

How to get relationship counts without loading objects in laravel

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;

Laravel Eloquent deep nested query

I'm still learning Laravel and I can't find the solution for this problem.
I need to get invoices(with expenses) that are related to specific Partner Type.
I tried this:
$p = Project::with(['invoices.partner.partnerType' => function($query){
$query->where('partnerTypeName', 'Lieferant');
}, 'expenses'
])->where('id', $id)
->first();
I want to select invoices for Lieferant, but I get all invoices for one project.
Project Model:
public function invoices()
{
return $this->hasMany('App\Invoice');
}
Invoice Model
public function expenses()
{
return $this->hasMany('App\Expense');
}
public function partner()
{
return $this->belongsTo('App\Partner');
}
Partner Model
public function partnerType()
{
return $this->belongsTo('App\PartnerType');
}
Edit: PartnerType Model
public function partners()
{
return $this->hasMany('App\Partner');
}
Edit 2: Database
Partner(partnerID, name, partnerTypeId)
PartnerType(partnerTypeId, partnerTypeName)
Project(projectID, name)
Invoice(invoiceID, name, projectID, partnerID)
Expenses(expenseID, invoiceID)
If your models look like that.
Should be like :
$p = Project::with(['invoices' => function($query){
$query->where('partnerTypeName', 'Lieferant')
->with(['expenses','partner' => function($q){
$q->with('partnerType');
}]);
}])->where('id', $id)
->first();
return dd($p);
The solution to your problem is to update your query like this:
$p = Project::with(['invoices' => function($query){
$query->with('expenses')->whereHas('partner.partnerType', function($q){
$q->where('partnerTypeName', 'Lieferant');
});
}])
->where('id', $id)
->first();
But a cleaner solution would be using a scope for your problem.
In your Invoice model.
// Invoice.php
public function scopeByPartnerType($query, $partnerType)
{
$query->whereHas('partner.partnerType', function($q) use ($partnerType) {
$q->where('partnerTypeName', $partnerType);
});
}
And then in your Project model, add another relation that will just get Invoices with a particular partner type.
// Project.php
public function lieferantInvoices()
{
return $this->hasMany('App\Invoices')->byPartnerType('Lieferant');
}
Now you can do just this:
$project->find($id)->load('lieferantInvoices');

Laravel newQuery()

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

Categories