Laravel - How to eager load many to many relationships - php

How would I eager load articles that belong to a category with article user, comments and comments user ?
Laravel version is 4.2.17
Using the query event listener, and dumping sql queries to the page. The HomeContoller method works fine, eager loads all of the articles without the N+1 problem.
However the getCategory method loads all the articles ok but does an sql query inside the foreach loop to get the article user, comments and comments user.
-- Database tables
articles
- id
- user_id
- body
comments
- id
- user_id
- article_id
- body
users
- id
- name
cat
- id
- name
article_cat
- id
- article_id
- cat_id
-- Models
class Article extends Eloquent {
public function user() {
return $this->belongsTo('User');
}
public function comments() {
return $this->hasMany('Comment');
}
}
class Comment extends Eloquent {
public function article() {
return $this->belongsTo('Article');
}
public function user() {
return $this->belongsTo('User');
}
}
class User extends Eloquent implements UserInterface, RemindableInterface {
public function articles() {
return $this->hasMany('article');
}
public function comments() {
return $this->hasMany('comment');
}
}
class Cat extends Eloquent {
public function articles() {
return $this->belongsToMany('Article');
}
}
-- Controllers
class HomeController extends BaseController {
// Get all articles
public function home() {
$articles = Article::with(['user', 'cats', 'comments', 'comments.user'])->get();
}
}
class CategoryController extends BaseController {
public function getCategory($categoryid) {
// How do I eager load articles the belong to this category with the article user, comments and comments user
$category = Cat::with('articles')
->find($categoryid)
->articles()
->get();
}
}
Edit
Have made some changes to the controller but it's still not eager loading, below is the new controller ...
public function getCategory($categoryid) {
$cat = Cat::where('id', $categoryid)->firstOrFail();
$category = Cat::with('articles')
->with('articles.user')
->with('articles.comments')
->find($categoryid)
->articles()
->orderBY('created_at', 'desc')
->take(Config::get('settings.num_posts'))
->get();
foreach ($category as $article) {
echo '<p> Article body = ' . $article->body . '</p>';
echo '<p> Article user = ' . $article->user->name . '</p>';
echo '<p> Article user photo = ' . $article->user->photo . '</p>';
echo '<p> Comments = ' . $article->comments . '</p>';
echo '<hr>';
}
exit();

Just use a dot to load in the related models.
$category = Cat::with('articles')
->with('articles.user')
->with('articles.comments')
->find($categoryid)
->get();
And then access your articles.
$category->articles;
Edit
You can always try eager loading within the relationship if you're always going to need the user on each comment or user, which I assume you will.
For comments.
public function comments() {
return $this->hasMany('Comment')->with('user');
}
And for articles.
public function articles() {
return $this->belongsToMany('Article')->with('user');
}

I've had similar issues and I resolved it by adding a foreign key to my models. So in your Comment model, it's best to add your foreign_key id like this:
class Comment extends Eloquent {
public function article() {
return $this->belongsTo('Article', 'article_id');
}
public function user() {
return $this->belongsTo('User', 'user_id');
}
}
Same with your user model.

Related

How to fetch data along with relationship data in Laravel using with keyword?

How can I fetch data along with blog category and blog tags from Blogs table using with in query.
Below is my model and controller code, I am getting Get Blogs Api Error instead of the blogs data.
Blog Controller
public function getBlogs()
{
try {
$blogs = Blog::where('status', 1)
->with('category')
->with('tag')
->with('user')
->with('comment')
->orderBy('id', 'desc')
->paginate(5);
return response()->json($blogs);
} catch (\Illuminate\Database\QueryException $e) {
$e = "Get Blogs Api Error";
return response()->json($e);
}
}
Blog Model
class Blog extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function category()
{
return $this->hasMany(Category::class);
}
public function tag()
{
return $this->hasMany(Tag::class);
}
public function comment()
{
return $this->hasMany(Comment::class);
}
}
User Model
public function blog_user()
{
return $this->hasMany(Blog::class);
}
Blog Category Model
public function blog_category()
{
return $this->belongsTo(Blog::class);
}
Blog Tag Model
public function blog_tag()
{
return $this->belongsTo(Blog::class);
}
Blog Comment Model
public function blog_comment()
{
return $this->belongsTo(Blog::class);
}
Database table structure
blogs table structure
blog_categories table structure
blog_tags table structure
First of all change names to plural. not singular. as you are using one to many. and use belongsToMany() method. not hasMany().
public function categories(){
return $this->belongsToMany(Category::class);
}
and change the name of pivot table to blog_category not blog_categories. It will work. and your BlogCategory model will look like this.
class BlogCategory extends Model {
protected $table = 'blog_category';
public function blog() {
return $this->belongsTo( Blog::class );
}
}
now you can get blogs like this.
$blogs = Blog::with( 'categories' )->get();
and this is how you will fetch blog for any category.
$category = BlogCategory::where( 'category_id', $category->id )->first();
dd( $category->blog );

How to query Eloquent relational tables based on json ids

I have a articles table with a field named article_categories.
I have a categories table with id field.
In my Article Model I have defined:
public function category(){
return $this->hasMany(Category::class,'id','article_categories');
}
the categories in the articles table are saved as json like ["1","3","24"]
in my ArticleController I want to retrieve all categories of a specific article.
In my edit function in the ArticleController I have this function:
public function edit(Article $article)
{
$category_ids = implode(',',json_decode($article->article_categories)) ; //this gives me 1,3,24
/////////
// HERE SHOULD COME THE QUERY WHICH I DON'T KNOW
// $article_categories = ??????????
////////
$categories = Category::all();
return view('article.edit', compact('articles','categories','article_categories'));
}
From what I've researched, there is no way to do this with Laravel but I don't know if this is true or not. I'm using Laravel 8.
Can anyone help me?
First of all you should know that you violated a many-to-many relation in a single column and that's not valid. What you should do is a pivot table article_category or category_article and in both Article and Category models you will define a many-to-many relation
like so
class Article extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
class Category extends Model
{
public function articles()
{
return $this->belongsToMany(Article::class);
}
}
And in your controller you can do
public function edit(Article $article)
{
$categories = Category::all();
return view('article.edit', compact('article','categories'));
}
And in the view you will have direct access to $article->categories
In your current situation you can do this
public function edit(Article $article)
{
$category_ids = json_decode($article->article_categories)
$article_categories = Category::whereIn('id', $category_ids);
$categories = Category::all();
return view('article.edit', compact('article','categories','article_categories'));
}

Laravel, four model relationship

I have four models: Animal(which can only be "dog" or "cat"), Category, Breed and Post.
Table data:
Animal:
- id
- name
Category:
- id
- name
- animal_id
Breed:
- id
- name
- category_id
Post:
- id
- breed_id
What I need is to get all Posts that are about dogs. Have any ideas?
Update:
My models:
class Animal extends Model
{
public function categories()
{
return $this->hasMany(Category::class)->orderBy('name', 'ASC');
}
}
class Category extends Model
{
public function animal()
{
return $this->belongsTo(Animal::class);
}
public function breeds()
{
$this->hasMany(Breed::class)->orderBy('name', 'ASC');
}
}
class Breed extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function posts()
{
return $this->hasMany(Posts::class);
}
}
class Posts extends Model
{
public function breed()
{
return $this->belongsTo(Breed::class);
}
}
First of all you need to properly set up the relations in the eloquent models,
here is an example of code,
For Post Eloquent Model
And post must have the column animal_id
public function animal(){
return $this->belongsTo(Animal::class);
}
Then you have to query like
$posts = Post::whereHas('animal',function($q){
return $q->where('name','dog');});
After that you will get the posts which belongs to animal data where animal name is dog. And if you want to query with your current database structure that is not recommended because of complex and long query.

Laravel: Cannot get parent of a category

I am working on a classified ads platform and i am having a problem with a relation. Although i think i've set up everything right there is still this issue.
The scenario is this:
I have a model Category (App\Category) with the folowing relations to form the subcategories
class Category extends Model
{
public function parent() {
return $this->belongsTo('App\Category', 'parent_id');
}
public function children() {
return $this->hasMany('App\Category', 'parent_id');
}
public function adverts() {
return $this->hasMany('App\Advert');
}
}
I also have a model Advert (App\Advert) with the following relations
class Advert extends Model
{
public function category() {
return $this->belongsTo('App\Category');
}
public function user() {
return $this->belongsTo('App\User');
}
}
And finally I have a model User (App\Advert) with the following relations
class User extends Authenticatable
{
public function adverts() {
return $this->hasMany('App\Advert');
}
}
I am collecting the adverts in a collection within a controller like this
$adverts = $user->adverts()->with('category')->with('category.parent')->get();
The problem i am facing is when i try to output the adverts and the category they belong along with the corresponding subcategory i am experiencing this issue
Trying to get property of non-object (View:
..\resources\views\user\profile.blade.php)
On the following line
$ad->category->parent->name . ' / ' . $ad->category->name
When i dd the collection i can see all the adverts and the relations together with the category and the subcategory ... but when i try to output it like this i got this error Trying to get property of non-object
Help anyone?
Thanks
I am not sure if this helps anything, but I am posting it as an answer to make the code more readable, thats all. Not expecting any credits ;)
In one of my models I set up the child / parents relationships like this:
/**
* Get the Categories associated with the parent's `id`
*/
public function children()
{
return $this->hasMany('App\Category', 'parent_id', 'id');
}
/**
* Get the parent associated with the Category`s parent_id`
*/
public function parent()
{
return $this->belongsTo('App\Category');
}

Picking the right type of relationship using Laravel and Eloquent

I have three tables in my database.
Posts
Authors
Categories
When viewing an Author page I want to be able to view all of the author's Posts and also the category of the post.
When viewing a Category index page I want to be able to view all of the Posts for that category and also include the Author with each Post.
When viewing a Post I want to be able to include the Category and Author
What type of relationship can I use to achieve this?
One to one, One to Many, Many to many, or polymorphic
Thanks in advance.
You can create your relations like this:
class Post extends Eloquent {
public funcion category()
{
return $this->belongsTo('Category');
}
public funcion author()
{
return $this->belongsTo('User');
}
}
class Author extends Eloquent {
public funcion posts()
{
return $this->hasMany('Post');
}
}
class Category extends Eloquent {
public funcion posts()
{
return $this->hasMany('Post');
}
}
And then use them this way:
$author = Author::find(1);
foreach($author->posts as $post)
{
echo $post->title;
echo $post->author->name;
}
$category = Category::find(1);
foreach($category->posts as $post)
{
echo $post->title;
echo $post->author->name;
}
$post = Post::find(1);
echo $post->category->title;
echo $post->author->name;
I have done something like this :
db table =
users : id name
debates : id post user_id
now in model
class User extends SentryUserModel {
public function debates()
{
return $this->hasMany('Debate', 'user_id');
}
}
and
class Debate extends Eloquent {
public function user()
{
return $this->belongsTo('User', 'id');
}
}
now in query
$debate= Debate::find(1);
echo $debates->user->name;
echo $debates->user->id;
it is giving a null result.
Changing this two solve the problem . (Do now know why we cant use foreign key here. If anyone know this please do inform ).
class User extends SentryUserModel {
public function debates()
{
return $this->hasMany('Debate');
}
}
and
class Debate extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
}

Categories