Get posts foreach categories in Laravel - php

I am using Laravel 4
Here is my Controller:
public function getArticles()
{
$categories = Category::with(['posts' => function($query){
$query->orderBy('updated_at', 'DESC')->take(4)->get();
}])->get();
return View::make('articles', compact('categories'));
}
Here is my View:
#foreach($categories as $category)
<h1>{{$category->title}}</h1>
<ul>
#foreach($category->posts as $post)
<li>
{{ $post->title }}
</li>
#endforeach
</ul>
#endforeach
My Category Model:
class Category extends Basemodel {
public function posts(){
return $this->hasMany('Post');
}
}
And my Post Model:
class Post extends Basemodel {
public function category(){
return $this->belongsTo('Category');
}
}
So it displays correctly my 6 categories (titles), but it only displays 4 posts total, I would like to display the 4 latest posts for each category (24 posts total).
I tried many things, and searched online but couldn't find a way around it.

You can't do that using limit in the eager loading closure, because, as you noticed, it limits the other query, that fetches all the posts.
You can easily get single related model with eager loading for hasMany with a helper relation:
public function latestPost()
{
return $this->hasOne('Post')->latest();
}
but for n related models per each parent, you need a bit more complex solution - take a look at:
n per parent
1 per parent

Maybe you can get use $categories = Category::all() to get all categories first.
foreach $categories and get the latest four posts by $posts=$category->posts
merge the posts array into one array
pass categories and posts to the view.
Cheers.
I hope you can write the correct foreach. And when retrieving the posts, use $category->posts instead of $category->posts(), which results in an error.

Related

Get category name from category id through relationship. Laravel

I have looked through the forum, but the solutions I have seen so far, aren't aligning with the issues I'm getting, so, I was hoping someone more informed would help out.
So I have a Category modem and A post model and there relationship is as follows;
on post model:
public function postcategory(){
return $this->belongsTo(PostCategory::class);
}
on category model:
public function posts(){
return $this->hasMany(Post::class)->where('approved', 'true');
}
and I am using slugs to retrieve all the posts that belongs to a certain category slug, using this function:
public function cats($category){
$posts = PostCategory::where('category_slug', $category)->first()->posts;
$category = PostCategory::where('category_slug', $category)->first();
return view('posts', compact('posts', 'category'));
}
Now, I am trying to get the name of the category with the category id stored in the posts table. for example, if I have a category id of 1 and on the category table, if the id number 1 is PHP, how do I return the name PHP instead of the id 1?
Secondly, if I wanted to paginate the view where posts is being compacted to, how do I do that? I switched the code in the controller to this:
$posts = PostCategory::with('posts')->where('category_slug', $category)->paginate(15);
when I dd that line of code, it returns some values (with relations), but when I pass that to the view, I get errors.
Hopefully, someone see this and help me out. :D
on Category Model :
public function posts()
{
return $this->hasMany(Post::class);
}
On Controller :
public function cats($slug)
{
$category = PostCategory::whereSlug($slug)->firstorFail();
$posts= $category->posts()->where('approved', 'true')->paginate(15);
return view('category.show', compact('posts', 'category'));
}
On View :
#foreach($posts as $post)
$post->title
....
#endforeach
{{ $posts->links() }}

Pulling all categories and grouping them by parent id

I am working with Laravel data querying and I need a query that is going to group all the children of a parent when I take the categories.
the categories table has a name and a parent_id, the routes of the categories have the parent_id set as null, the query should return every category grouped by parent id and the parent should be the first node of every group.
If you only want to display the categories as parent child somewhere, you do not need to collect them like that, you can make a relationship within the model like
class Category {
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function parent()
{
return $this->hasMany(self::class, 'id', 'parent_id');
}
}
may be it will be one-to-many relationship instead of many-to-many depending on your requirement.
Now you can just get all the parents like
Category::whereNull('parent_id')->get();
or using a scope
Category::parent()->get(); and define the scope in the model
and loop through the parent category like
#foreach ( $categories as $category )
{{ $category->name }}
#foreach ( $category->children as $subCategory )
{{ $subCategory->name }}
#endforeach
#endofreach
and to retrieve parent with children you can use
Category::whereNull('parent_id')->with('children')->get();
or
Category::parent()->with('children')->get();
I have not tested the code, but roughly it will be like this.
contoller
$locations = OurLocation::groupBy('country_id')->with('children')->get();
model
public function children()
{
return $this->hasMany(OurLocation::class, 'country_id','country_id');
}
blade
#foreach($locations as $index=>$location)
#foreach($location->children as $children)
{{ $children->company_name }} <br>
#endforeach
#endforeach
When you get the returned collection from the query, you are able to use the ->groupBy() method where you can specify the field which the results should be grouped by.
Assuming your categories model is Category:
$categories = Category::all()->groupBy('parent_id')->toArray();

A correct query to get data from 3 tables and show it as a single element in laravel?

Suppose I have 3 tables, posts, post_images, and post_links.
post.id is a foreign key in both post_images and post_links.
Each post have multiple images.
I need a data which contains post, its images and its links as single element/array item. If there are 3 posts, I need 3 arrays with each array containing the posts images and links.
My code so far,
$data = DB::table('posts')
->join('post_images','posts.id' ,'=', 'post_images.post_id')
->join('post_links','posts.id' ,'=', 'post_links.post_id')
->select('posts.*')
->get();
with the above query I am getting all the records joined, If i have 3 records with 3 images each, I am getting 9 records, I just need 3 posts with its data as its sub arrays.
Any suggestion?
Here is the PostImage model
class PostImage extends Model
{
public function post() {
return $this->belongsTo(Post::class);
}
}
Here is the PostLink model
class PostLink extends Model
{
public function post() {
return $this->belongsTo(Post::class);
}
}
Here is the Post model
class Post extends Model
{
public function links() {
return $this->hasMany(PostLink::class);
}
public function images() {
return $this->hasMany(PostImage::class);
}
}
In the view you can reach everything you need.
#foreach ($posts as $post)
{$post->title} <br>
#foreach ($post->links as $link)
{$link->url} <br>
#endforeach
#foreach ($post->images as $image)
{$image->src} <br>
#endforeach
#endforeach
And if you want use less queries you could use eager loading to fetch all this data the first time. Eager Loading Laravel
Should look something like this
$posts = Post::with('images','links')->get();
if you already have relation in model you just have to use with method like
$data = PostModel::with('post_images','post_links')->get();
make it dd($data) and look at this. hope it will work.
References: https://laravel.com/docs/5.4/eloquent-relationships#eager-loading

laravel 5.1 getting related 5 news of each category in many-to-many relation

I got stuck here been trying from 2-3 hours.
I have a many to many relation:
class Category extends Model
{
public function news()
{
return $this->belongsToMany('App\News');
}
}
class News extends Model
{
public function categories()
{
return $this->belongsToMany('App\Category');
}
}
I am trying to get latest 5 news of the related categories:
$front_categories = Category::with(array(
'news'=>function($query){
$query->where('publish','1')->orderBy('created_at', 'desc')->take(5);}))
->where('in_front', 1)->get();
The above query is not working for me it give a total of five results instead of 5 result for each categories.
Based on what I know about Laravel, you could try doing it this way instead.
class Category {
public function recentNews()
{
return $this->news()->orderBy('created_by', 'DESC')
->take(5);
}
}
// Get your categories
$front_categories = Category::where('in_front', 1)->get();
// load the recent news for each category, this will be lazy loaded
// inside any loop that it's used in.
foreach ($front_categories as $category) {
$category->recentNews;
}
This has the same effect as Lê Trần Tiến Trung's answer and results in multiple queries. It also depends on if you're reusing this functionality or not. If it is a one-off, it may be better to put this somewhere else. Other ways could also be more dynamic, such as creating a method that returns the collection of categories and you can ask it for a certain number:
class CategoriesRepository {
public static function getFrontCategories(array $opts = []) {
$categories = Category::where('in_front', 1)->get();
if (!empty($opts) && isset($opts['withNewsCount']))
{
foreach ($categories as $category)
{
$category->recentNews = static::getRecentNewsForCategory(
$category->id,
$opts['withNewsCount']
);
}
}
return $categories;
}
}
$front_categories = CategoriesRepository::getFrontCategories([
'withNewsCount' => 5
]);
I think, Because you do eager loading a collection which has more than one record.
To solve it, you need to loop
$front_categories = Category::where('in_front', 1)->get();
foreach ($front_categories as $fCategory) {
$fCategory->load(['news' => function($query) {
$query->where('publish','1')->orderBy('created_at', 'desc')->take(5);
}]);
}
This solution will do many queries to DB. If you want to do with only 1 query, checkout this Using LIMIT within GROUP BY to get N results per group?

Laravel 5 - Count the number of posts that are assigned to each category in a blog

I was wondering what the cleanest way was to count the number of posts that are connected to a category in my blog.
Here is how the table relationship is set up.
What I have is a hasMany relationship from the Category to the Post models like this:
In Categories Model
public function blog_posts()
{
return $this->hasMany('App\Http\Models\Blog_Post', 'category_id');
}
And in the Blog_Post Model
public function blog_categories()
{
return $this->belongsTo('App\Http\Models\BlogCategories', 'category_id');
}
In effect all I want to do is be able to return to my view the total number of posts that each category has as shown below. Where x is the number of posts within each category.
cat1 (x)
cat2 (x)
cat3 (x)
It's not hard to count I know however as I only want a count I do not want to also retrieve the records as they are not required and I also do not want to create more queries than is necessary.
I have not completed the view as yet but probably a start would be to pass through the categories in a loop to display each and add the count at the same time?
#foreach ($categories as $category)
{!! $category->name !!} - {!! Count of posts here !!}
#endforeach
Hopefully that is clear(ish)!
Eager load the relation in your controller:
public function index()
{
$categories = Category::with('blog_posts')->get();
return view('categories.index', compact('categories'));
}
You can then use the count() method on the blog_posts relation when looping over categories in your view:
#foreach ($categories as $category)
<li>{{ $category->name }} ({{ $category->blog_posts->count() }})</li>
#endforeach
EDIT: Since Laravel 5.3, you can use withCount() to load counts of relations, i.e.
$categories = Category::withCount('blog_posts')->get();
This will make the count available via a property:
foreach ($categories as $category) {
$blog_posts_count = $category->blog_posts_count;
}
The nicest way to do it with eager loading support I know is to create a separate relation with the post count. Check this out:
public function blog_posts_count() {
return $this->hasOne('App\Http\Models\Blog_Post', 'category_id')
->selectRaw('category_id, count(*) as aggregate')
->groupBy('category_id');
}
public function getBlogPostsCountAttribute() {
if(!array_key_exists('blog_posts_count', $this->relations))
$this->load('blog_posts_count');
$related = $this->getRelation('blog_posts_count');
return $related ? (int) $related->aggregate : 0;
}
Usage is simple:
{{ $category->blog_posts_count }}

Categories