I have these 2 linked models: jobs and job_translations. A job have many translations. So in my job model, there is :
/**
* Get the translations for the job.
*/
public function translations()
{
return $this->hasMany('App\Models\JobTranslation');
}
In my controller, I want to build a query dynamically like that :
$query = Job::query();
if ($request->has('translation')) {
$query->translations()->where('external_translation', 'ilike', '%'.$request->translation.'%');
}
$jobs = $query->paginate(10);
I have this error :
Call to undefined method Illuminate\Database\Eloquent\Builder::translations()
Is it possible to do such a dynamic query with Eloquent?
Yes, it is possible. What you are looking for is whereHas('translations', $callback) instead of translations():
$query = Job::query();
if ($request->has('translation')) {
$query->whereHas('translations', function ($query) use ($request) {
$query->where('external_translation', 'ilike', '%'.$request->translation.'%');
});
}
$jobs = $query->paginate(10);
Your query can be improved further by using when($condition, $callback) instead of an if:
$jobs = Job::query()
->when($request->translation, function ($query, $translation) {
$query->whereHas('translations', function ($query) use ($translation) {
$query->where('external_translation', 'ilike', "%{$translation}%");
});
})
->paginate(10);
the issue you should do eager loading to detected on next chained query like this:
$query = Job::with('translations')->query();
Related
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);
I'm building a website with laravel.
I use these models:
Post
Countrytag
Citytag
Categorytag
A post can have many countrytags, citytags or categorytags and vice versa.
I would like to search posts by tags.
I use this function:
public function blogsearchresults(Request $request)
{
$attributes=request()->validate([
'countrytag_id'=>'required_without_all:citytag_id,categorytag_id',
'citytag_id'=>'required_without_all:countrytag_id,categorytag_id',
'categorytag_id'=>'required_without_all:countrytag_id,citytag_id'
]);
$posts=Post::all();
if($request->has('countrytag_id')) {
$countryid=$attributes['countrytag_id'];
$posts =$posts->whereHas('countrytags', function ($query) use ($countryid){
$query->wherein('countrytag_id', $countryid);
});
}
if($request->has('citytag_id')) {
$cityid=$attributes['citytag_id'];
$posts=$posts->whereHas('citytags', function ($query2) use ($cityid){
$query2->wherein('citytag_id', $cityid);
});
}
if($request->has('categorytag_id')) {
$categoryid=$attributes['categorytag_id'];
$posts=$posts->whereHas('categorytags', function ($query3) use ($categoryid){
$query3->wherein('categorytag_id', $categoryid);
});
}
$posts=$posts->paginate();
return view('pages.blog.blogsearchresults', compact('posts'));
}
But I get this error:
Method Illuminate\Database\Eloquent\Collection::whereHas does not exist.
Could you please help me in solving this issue?
Thank you
the method all() returns a collection, you can't use query builder methods on it. (also when you query build, you dont need to assign the value $posts over and over)
public function blogsearchresults(Request $request)
{
$attributes = request()->validate([
'countrytag_id'=>'required_without_all:citytag_id,categorytag_id',
'citytag_id'=>'required_without_all:countrytag_id,categorytag_id',
'categorytag_id'=>'required_without_all:countrytag_id,citytag_id'
]);
$postQuery = Post::query();
if($request->has('countrytag_id')) {
$countryid = $attributes['countrytag_id'];
$postQuery->whereHas('countrytags', function ($query) use ($countryid){
$query->wherein('countrytag_id', $countryid);
});
}
if($request->has('citytag_id')) {
$cityid = $attributes['citytag_id'];
$postQuery->whereHas('citytags', function ($query2) use ($cityid){
$query2->wherein('citytag_id', $cityid);
});
}
if($request->has('categorytag_id')) {
$categoryid = $attributes['categorytag_id'];
$postQuery->whereHas('categorytags', function ($query3) use ($categoryid){
$query3->wherein('categorytag_id', $categoryid);
});
}
$posts = $postQuery->paginate();
return view('pages.blog.blogsearchresults', compact('posts'));
}
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);
}
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();
I have the following set for relations in my eloquent models/db:
Student has many PhdReport
PhdReport has one Link
I need to get Students where the (single) most recent PhdReport (by the attribute date_to) is more than 6 months and the Link connected to the PhdReport has a status of 'complete'.
I am trying to do this using eloquent relationships and I'm pretty new to the concept of querying relationships so I would like to know if there's a better approach to the one I'm taking.
Here's the relevant code so far:
PhdReport.php
public function link()
{
return $this->belongsTo(\App\Models\Link::class);
}
Student.php
public function phdReport()
{
return $this->hasMany(\App\Models\PhdReport::class);
}
public function latestPhdReport()
{
return $this->hasOne(\App\Models\PhdReport::class)->latest('date_to');
}
/* this doesn't work! */
public function lastPhdReportSixMonthsAgo()
{
$sixMonthsAgo = \Carbon\Carbon::now()->subMonth(6);
return $this->whereHas('latestPhdReport', function ($query) use ($sixMonthsAgo) {
$query->where('date_to', '<=', $sixMonthsAgo);
});
}
This is my best shot at it so far but I am unsure if the first whereHas applies to the second whereHas also?
$sixMonthsAgo = \Carbon\Carbon::now()->subMonth(6);
$students = $this->student
->whereHas('phdReport.link', function ($query) {
$query->where('status', 'complete');
})
->whereHas('latestPhdReport', function ($query) use ($sixMonthsAgo) {
$query->where('date_to', '<=', $sixMonthsAgo);
})
->get();
If I run:
$students = $this->student
->has('lastPhdReportSixMonthsAgo')
->get();
I get:
BadMethodCallException in Builder.php line 1994:
Call to undefined method Illuminate\Database\Query\Builder::getRelated()
Any advice would be much appreciated!
This is my current solution, I haven't had time to refactor yet but I'm sure it can be improved by doing only a query and no filter. Will edit when I get time but thought I'd put it up as it might help someone else. Much credit to #JarekTkaczyk
$monthsAgo = \Carbon\Carbon::now()->subMonth($months);
// Query 1: students with a completed report
$studentsWithCompletedLink = $studentsWithCompletedLink
->whereHas('phdReport.link', function ($query) {
$query->where('status', 'complete');
})
->get();
if ($months != 0) {
// filter through to get students where the report date_to is more than $months months ago
$studentsWithReport = $studentsWithCompletedLink->filter(function ($student) use ($monthsAgo) {
return $student->latestReport->date_to <= $monthsAgo;
});
}
Student.php
public function latestPhdReport()
{
return $this->hasOne(\App\Models\PhdReport::class)->latest('date_to');
}