Paginate Eager Load Constraints - php

I am Using Laravel 4:
I am trying to paginate an Eager Loaded Constraint, essentially I have a Topics Model which has a hasMany relationship to a Comments Model. This is all well and good and no problems with that. However I would like to be able to display a paginated table of the Comments (of which there will be hundreds) on the admins edit topic view. To do this I thought I would use the following code:
$topic = Topics::with( array( 'comments' => function($query)
{
$query->orderby('created_at', 'DESC');
$query->paginate(10);
} ) )
->where('id', $id)
->first();
Now this will provide me $topic with the correct object and inspecting $topic->comments shows it has the correct linked comments and is limited to 10 results. The pagination also responds to the GET page parameter and will paginate, however trying to echo $topic->comments->links() results in an error of method not found.
My work around for this is to have a second query which counts the attached comments and then using the following in my view:
$paginator = Paginator::make($topic->comments->toarray(), $commentCount, 10);
echo $paginator->appends(array('tab' => 'comments'))->links();
Is this a bug?

When you call paginate() on your eager constraint it is configuring the query for you (setting the skip and take). The paginator that is generated will actually be discarded.
Since you're just getting a single topic with its comments why not do this (same number of database calls)...
$topic = Topic::find($id);
$comments = $topic->comments()->order_by('created_at DESC')->paginate(10);
Admittedly, you won't be able to use $topic->comments to get the paginator, and will have to use $comments instead, but that's not a terrible thing.

Related

How to filter laravel collection

I am trying to make a filter in laravel. This following filter works
$posts= Post::where('category',$request->category)->orderBy('id','desc')->paginate(10);
But when I try to do something like this
public function index(Request $request)
{
$posts= Post::where('category',$request->category)->get();
$posts->latest()->paginate(10);
dd($posts);
It doesn't work. Can someone explain why is this and provide me the code that works. My project have multiple filter.
Error
Because $posts = Post::all(); already execute a query.
Post::where('category',$request->category)->latest()->paginate(10)->get();
would be what you want.
A note:latest requires the created_at column
You should go
$posts = Post::where('category',$request->category)->latest()->paginate(10);
the get request is unnecessary as the paginate will execute the query.
The first one makes the query by pagination i.e fetch 10 records per constructed page
For the second one, based on observation, you most likely have encountered at least 2 errors:
The first, on the line that used the get method because that method requires at least one parameter.
Type error: Too few arguments to function Illuminate\Support\Collection::get()
The other since its a collection, and since there is nothing like paginate or latest method on collection therefore throws other errors. You should check Collection's Available methods to have a glimpse of the methods allowed on collection.
One of the best solutions is to simply order the result when making the query:
Blog::where('category',$request->category)
->orderBy('created_at', 'desc') //you may use also 'updated_at' also depends on your need
->paginate(10);
This way you have the latest coming first int the pagination and also having not worrying about paginating a collection

Returning all Laravel eloquent models tagged with a specific set of tags

I've got an Eloquent model Post with a belongsToMany(Tag::class) relationship. Now in the situation where I want to return or get all posts with a given set of tags, what's the most efficient Laravel-esq way of performing such a query?
For example; get all posts that have the tag bbq, or all posts that have the tags bbq AND beef.
I'd like to simply pass an array of tags in which can be of any count, if possible. I have tried the following and a number of different combinations with no luck, granted my SQL kung-fu isn't the greatest.
$posts = Post::whereHas('tags', function ($query) {
$query->whereIn('tag_types.name', ['bbq', 'beef']);
})->get();
Eventually found a solution. In this case it requires knowing the ID's of the tags in question, which is easy enough to query for. In this case I store the tag ID's in an array called $tag_ids and use the following Eloquent query (tag_types being the pivot table):
$posts = Post::join('tag_types', function ($join) use ($tag_ids) {
$join
->on('posts.id', '=', 'tag_types.post_id')
->whereIn('tag_types.tag_id', $tag_ids);
})
->groupBy('posts.id')
->havingRaw('count(distinct tag_types.tag_id) = ' . count($tag_ids))
->lists('post_id');
$posts now contains the ID's of all the posts that contain every tag ID listed in $tag_ids.
Hope this helps anyone facing a similar dilemma.

Laravel ORM "with" is returning all rows even with constraint

Using Laravel 4 Eloquent ORM, any idea why the following is pulling every row instead of just the one I'm asking for (book_id=3)?
if ($Book = Book::find(3)) {
return (Book::with(array('chapters.pages' => function($query)
{
$query->where('book_id', '=', 3 ); // hard-coded id for illustration purposes
})
)->get()->toJson());
}
The output I'm getting is every single book with every chapter and every page. I only want to pull that single book with chapters and pages.
According to the documentation I should be able to add a constraint. Seems like I'm following the docs pretty closely. Thanks in advance.
I think the problem is you are asking for Book::with() - but you have not put a constraint on the Book() itself. So you need to do Book::with()->where()
Does this work
Book::with(array('chapters.pages' => function($query)
{
$query->where('book_id', '=', 3 ); // hard-coded id for illustration purposes
})
)->where('id', '=', '3')->get()->toJson());
note the ->where() - which is putting the id constraint on the query.
Edit: if you have correctly defined relationships (such as HasMany() etc) - then this should work:
$books = Book::with('chapters.pages')->where('id', '=', '3')->get();
Because the relationship with mean it only gets the 'chapters/pages' for the book with id of 3 by defintion.

Querying on related models using Laravel 4 and Eloquent

Using Laravel 4 I have the following models and relations: Event which hasMany Record which hasMany Item. What I would like to do is something like this
Item::where('events.event_type_id', 2)->paginate(50);
This of cause doesn't work as Eloquent doesn't JOIN the models together when retrieving the records. So how do I go about this without just writing the SQL myself (which I would like to avoid as I want to use pagination).
What you want is eager loading.
It works like this if you want to specify additional constraints:
Item::with(array('events' => function($query) {
return $query->where('event_type_id', 2);
}))->paginate(50);
There is a pull request pending here https://github.com/laravel/framework/pull/1951.
This will allow you to use a constraint on the has() method, something like this:
$results = Foo::has(array('bars' => function($query)
{
$query->where('title', 'LIKE', '%baz%');
}))
->with('bars')
->get();
The idea being you only return Foos that have related Bars that contain the string 'baz' in its title column.
It's also discussed here: https://github.com/laravel/framework/issues/1166. Hopefully it will be merged in soon. Works fine for me when I update my local copy of the Builder class with the updated code in the pull request.

Querying relations with a where clause on the child table

I have been using the has method to query the existence of data in a related table and that is working just fine. Using the code example on the Laravel web site, you can do the following to get any post that has comments associated with it.
$posts = Post::has('comments')->get();
What I would like to be able to do, as an example, is query for any post which has comments from the last thirty days. Since the has method only has the ability to count related rows I am not sure of a good way to do this.
Ok, this is what I could come up with. It might not be the best solution, but it does work and doesn't seem too polluted. First, we'll need to add reference to Carbon and the Eloquent\Collection classes.
use \Carbon\Carbon;
use \Illuminate\Database\Eloquent\Collection;
Carbon does come with Laravel as a dependency, out of the box, but it would be smart to add it to your own project's dependencies anyway. Then, this code should do it:
// Gather all distinct 'post_id' from Comments made in the last 30 days
// along with its Post objects
$comments = Comment::with('Post')
->where('created_at', '>', Carbon::now()->subMonth())
->distinct()
->get(array('post_id'));
// Since we only want Posts, loop through the Comments collection and add
// the Posts to a new collection
$posts = new Collection;
foreach ($comments as $comment) {
$posts->add($comment->post);
}
From the Laravel 4 Documentation:
$posts= Post::with(array('comments' => function($query)
{
$query->where('published', '>=', 'date- 1 month'); //look date functions up
}))->get();
See Eager Load Constraints under laravel 4 documentation

Categories