Laravel orderBy date is not working when using paginator - php

I am new to laravel and am struggling to figure this one out.I am trying to order my posts by date and also use the paginator. Whenever I remove the paginator. it orders by date, but adding it back in orders by id, I think?
My model
class Post extends Model
{
//
protected $dates = ['date'];
}
My controller method
public function show()
{
$posts = Post::orderBy('date','desc')->get();
return view('welcome',['posts' => Post::paginate(5)], compact('posts'));
}

You're essentially retrieving all posts and then paginating them, after retrieving all posts and ordering them.
Your code breaks down like this:
public function show()
{
// retrieve posts and order them by date
$posts = Post::orderBy('date','desc')->get();
// retrieve posts again and paginate them
return view('welcome',['posts' => Post::paginate(5)], compact('posts'));
}
You have two options --
Get posts, order them, paginate them, and store them in a variable that you pass to your view:
public function show()
{
$posts = Post::orderBy('date','desc')->paginate(5);
return view('welcome', compact('posts'));
}
Or get the posts, order them and paginate them while passing them to the view:
public function show()
{
return view('welcome',[ 'posts' => Post::orderBy('date','desc')->paginate(5) ]);
}

You can use other column/s specifically the created_at or updated_at. I tried my customized column datetime as my orderingBy but it doesn't worked, so I used another column(updated_at) wherein I just want to get the updated records descendingly and I noticed that it can be use as my solution and it works fine in my query.
->orderBy('updated_at','DESC')->get();
->orderBy('created_at','DESC')->get();

Related

Eloquent Laravel Fetch Data from multiple tables

I've spent two days trying to solve this but I can't figure how.
I have five tables
Product
Category
Category_Product
Order
Order_Product
From the view,clicking on a category button I have to fetch all his ordered products with the relative category.
I have the current models:
Product Model
class Product extends Model
{
public function categories() {
return $this->belongsToMany('App\Category');
}
public function orders() {
return $this->belongsTo('App\Order');
}
}
Category Model
public function products() {
return $this->belongsToMany('App\Product');
}
Order Model
public function products() {
return $this->belongsToMany('App\Product');
}
Now the problem is that I can't figure how to fetch the data from the current tables.When I press a button I'm able to fetch the category from the Product Table,but I want to fetch from the Ordered_Products. I really can't figure how.
With this I'm able to fetch all the categories from Product
if (request()->category) {
$products = Product::with('categories')->whereHas('categories', function ($query) {
$query->where('slug', request()->category);
})->get();
}
With this instead,I'm able to fetch the ordered products.
$products = DB::table('order_product')
->join('products', 'order_product.product_id','=', 'products.id')
->where('order_product.user_id','=',$user_id)
->get();
For the latter, there's a better way to do it, that's for sure. I'm sorry if it's a dumb question but I'm rather new with this framework. I am using Laravel 7.2.
Basically Eloquent Model doesn't encourage joining tables to retrieve data. It should be joined only for filtering results (So you need to drop field of other table using ->select('original_table.*'))
In this case, you should simply retrieve categories at first. Then retrieve related data using relation property accessing.
e.g.
$categories = Category::query()
->with('products')
->where('slug', request('category'))
->get();
$products = $categories->flatMap->products;
$pivots = $products->map->pivot;
Solved using whereHas two times:
$products = Product::with('categories')->whereHas('categories',function($query){
$query->where('slug',request()->category);
})->whereHas('orders',function($query){
$query->where('orders.user_id',Auth::id());
})->get();

What to do when paginator is not allowed on a collection?

I have two entities: Post (posts) and Tag (tags). They both are in many-to-many relationship. So I have a pivot table called PostTag (post_tag). I want to list all the tags [including a) pivot table and b) post title] which belong to those posts whose author is the logged in user. So I did something like this:
$tags = collect();
$posts = Post::where('user_id', auth()->id())->with('tags')->get();
$posts->each(function($post, $key) use ($tags){
$post->tags->each(function($tag, $key) use ($tags, $post) {
$tag->post_title = $post->title;
$tags->push($tag);
});
});
return $tags;
However, I also need to paginate the result. So I attempted to return this instead:
return $tags->paginate(10);
But paginate is not a method of Collection (Maybe of Builder)
The relationship methods are:
// Post.php
public function tags() {
return $this->belongsToMany(Tag::class)->withPivot('updated_at');
}
// Tag.php
public function posts(){
return $this->belongsToMany(Post::class);
}
I have a feeling that there must be some easier way of doing it which I may not know:
PostTag::someQueryThatFetchesThoseTagsWithPostTitle();
// If I could do something like this, paginate() would have been available
Tags::query()->where('posts.user_id', auth()->id())
->join('post_tag', 'post_tag.tag_id', '=', 'tags.id')
->join('posts', 'post_tag.post_id', '=', 'posts.id')
->selectRaw('tags.*, posts.title as post_title')
->paginate(10);
You can just optimize your query in order to return what you want selecting what you need.
This should be even faster.
You can create your own pagination with LengthAwarePaginator with this piece of code I'm using in my projects sometimes.
//Get current page form url e.g. &page=6
$currentPage = LengthAwarePaginator::resolveCurrentPage();
//Number of results in pagination
$paginate = 10;
//Slice the collection to get the items to display in current page
$currentPageSearchResults = $tags->slice(($currentPage - 1) * $paginate, $paginate)->all();
//Create our paginator and pass it to the view
$paginatedSearchResults = new LengthAwarePaginator($currentPageSearchResults, $tags->count(), $paginate);
Where $paginatedSearchResults returns pagination object.

Call to undefined relationship on model laravel using scope

I'm trying to get 5 posts for each category so I did a little search and ends up here Getting n Posts per category
But I'm getting a weird Call to undefined relationship on model when using with scope but it all works fine If I don't use a scope. Here is the Category Model
//Relationship with posts
public function posts(){
return $this->hasMany('App\Post');
}
scopeNPerGroup
public function scopeNPerGroup($query, $group, $n = 10)
{
// queried table
$table = ($this->getTable());
// initialize MySQL variables inline
$query->from( \DB::raw("(SELECT #rank:=0, #group:=0) as vars, {$table}") );
// if no columns already selected, let's select *
if ( ! $query->getQuery()->columns)
{
$query->select("{$table}.*");
}
// make sure column aliases are unique
$groupAlias = 'group_'.md5(time());
$rankAlias = 'rank_'.md5(time());
// apply mysql variables
$query->addSelect(\DB::raw(
"#rank := IF(#group = {$group}, #rank+1, 1) as {$rankAlias}, #group := {$group} as {$groupAlias}"
));
// make sure first order clause is the group order
$query->getQuery()->orders = (array) $query->getQuery()->orders;
array_unshift($query->getQuery()->orders, ['column' => $group, 'direction' => 'asc']);
// prepare subquery
$subQuery = $query->toSql();
// prepare new main base Query\Builder
$newBase = $this->newQuery()
->from(\DB::raw("({$subQuery}) as {$table}"))
->mergeBindings($query->getQuery())
->where($rankAlias, '<=', $n)
->getQuery();
// replace underlying builder to get rid of previous clauses
$query->setQuery($newBase);
}
Calling Npergroup with relation
public function latestposts()
{
return $this->posts()->latest()->nPerGroup('category_id', 5);
}
Post Model Relationship
//Post belongs to Category
public function category(){
return $this->belongsTo('App\Category');
}
In my category controller I'm calling latestposts through
$categories = Category::with('latestposts')->get();
But I'm getting the error: Call to undefined relationship on model
What I want is:
Get the N number of posts per each category but I'm completely lost at this point. Any help would be appreciated
Reference:
Tweaking Eloquent relations – how to get N related models per parent ?
I am giving this answer based on your purpose that you want 5 posts per category.
So you have Category Model and Post Model.
And in Category Model you have relation with Post model like this
//Relationship with posts
public function posts(){
return $this->hasMany('App\Post');
}
And in Post Model you have relation with Category model like this
//Post belongs to Category
public function category(){
return $this->belongsTo('App\Category');
}
I show your question you have done SQL queries.
Instead of that, You can use two approaches
1) Give condition while eagar loading
$categories = Category::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc')->take(5);
}])->get();
Note: This approach will only work when you take only one result of parent child using first() method.
To get n number of posts per category Use this.
First, you can retrieve all categories with
$categories = Category::all();
Then you can use foreach loop and in all $category you have to give assign new attribute in it like here latestposts,
foreach ($categories as $category)
{
$category->latestposts = $category->posts()->orderBy('created_at','desc')->take(5)->get();
}
After this foreach loop you will get latest 5 posts in all categories.
Try this in your code and comment your queries and reviews.

Displaying posts of users the user follows through Laravel relationships

I would like to display the posts of everyone the current user follows, ordered by date desc.
I have a many to many relationship supplying all the people the user is following.
$users = User::find(Auth::user()->id)->follow()->get();
I have a one to many relationship displaying the posts for any user.
$updates = App\User::find(?????)->updates()->orderBy('created_at', 'desc')->get();
The question mark's shows where the followers ID's need to be placed.
I can put the above query inside the for each loop but that obviously works its way through each follower rather than all posts in date order.
I suspect I may need to set a new relationship and work from the beginning. Can anyone advise.
User Model
public function updates()
{
return $this->hasMany('App\update');
}
/**
* User following relationship
*/
// Get all users we are following
public function follow()
{
return $this->belongsToMany('App\User', 'user_follows', 'user_id', 'follow_id')->withTimestamps()->withPivot('id');;;
}
// This function allows us to get a list of users following us
public function followers()
{
return $this->belongsToMany('App\User', 'user_follows', 'follow_id', 'user_id')->withTimestamps();;
}
}
Update Model
public function user_update()
{
return $this->belongsTo('App\User');
}
Thank you.
Since you want the posts, it is probably going to be easier starting a query on the Post model, and then filter the posts based on their relationships.
Assuming your Post model has an author relationship to the User that created the post, and the User has a follower relationship to all the Users that are following it, you could do:
$userId = Auth::user()->id;
$posts = \App\Post::whereHas('author.follower', function ($q) use ($userId) {
return $q->where('id', $userId);
})
->latest() // built in helper method for orderBy('created_at', 'desc')
->get();
Now, $posts will be a collection of your Post models that were authored by a user that is being followed by your authenticated user.

Laravel - Eager load many-to-many, get one record only (not a collection)

My posts have images (many-to many since images can have other relations as well). In my pivot table I have a boolean field called 'featured' which designates the main image for that post. I want to display in the posts index page all the posts associated with the current user. I only want to get one image from the DB and that should be the featured image. Currently I can only get the featured images as a collection. The reason for this is if the user has lots of posts I don't want to go ahead and retrieve the featured image for all their posts (N+1) but rather using eager loading get the featured imaged with only 2 queries.
\\Post Model
public function images() {
return $this->belongsToMany(Image::class);
}
public function image(){
return $this->images()->where('featured', '=', true)->first();
}
public function featured_image(){
return $this->images()->where('featured', '=', true);
}
\\Controller
$user = Auth::user();
$posts = $user->posts()->with('image')->get();
// throws error
//Call to undefined method Illuminate\Database\Query\Builder::addEagerConstraints()
// if I do this
$posts = $user->posts()->with('featured_image')->get();
// I get all the user's posts, I get the featured image but as a collection even if I only have one record there
How can I do this?
I think this is probably the solution you want:
\\Post Model
public function images() {
return $this->belongsToMany(Image::class);
}
public function getFeaturedImageAttribute() {
return $this->images->where('featured', true)->first();
}
\\Controller
$user = Auth::user();
$posts = $user->posts()->with('images')->get();
In the resulting collection of posts, each post will have a 'featured_image' attribute that can be accessed like this:
foreach ( $posts as $post )
{
$featured_image = $post->featured_image;
// do something with the image...
}
IMPORTANT: Because the accessor method uses '$this->images' instead of '$this->images()', it will run using the eager loaded 'images' Collection's where() and first() methods instead of the query builder. This results in a decent chunk of PHP processing but no new queries.
If you are limited to using only two queries, you can use the following code to achieve your goal:
$posts = $user->posts;
$idOfPosts = $posts->pluck('id');
$featuredImages = Image::whereIn('post_id', $idOfPosts)->where('featured', true)->get();
enter code here
While this solution is not an Eager Loading approach, it does resolve the N+1 query problem.

Categories