So I have a basic forum set up and I have the relationships between models completed. However I am stuck trying to get the last user who posted in the sub category. This is how its laid out.
mysql tables
forum_category (this is used for the main category and also the sub category)
forum_post (this is used for the posts)
Here is my ForumCategory.php
namespace App\Models\Forum;
use Illuminate\Database\Eloquent\Model;
class ForumCategory extends Model {
protected $table = 'forum_category';
public static function category()
{
return ForumCategory::with('ForumSubCategory')->whereNull('parent_id')->get();
}
public function User()
{
return $this->belongsTo('App\User');
}
public function ForumSubCategory()
{
return $this->hasMany('App\Models\Forum\ForumSubCategory' , 'parent_id');
}
public function ForumPost()
{
return $this->hasMany('App\Models\Forum\ForumPost' , 'parent_id');
}
}
And here is my ForumSubCategory.php the sub category model even though they are using the same table...
namespace App\Models\Forum;
use App\User;
use Illuminate\Database\Eloquent\Model;
class ForumSubCategory extends Model
{
protected $table = 'forum_category';
public function ForumCategory()
{
return $this->belongsTo('App\Models\Forum\ForumCategory');
}
public function User()
{
return $this->belongsTo('App\User');
}
public function ForumPost()
{
return $this->hasMany('App\Models\Forum\ForumPost' , 'parent_id');
}
}
And here Is my ForumPost.php model
namespace App\Models\Forum;
use Illuminate\Database\Eloquent\Model;
class ForumPost extends Model
{
protected $table = 'forum_post';
public function User()
{
return $this->belongsTo('App\User');
}
public function ForumSubCategory()
{
return $this->belongsTo('App\Models\Forum\ForumSubCategory');
}
}
The controller is pretty basic, the index is
public function index() {
return view('forum.index', ['category' => ForumCategory::category()]);
}
Now at the moment I am currently getting my last user who posted with this
public static function LastPost($value)
{
$lastPost = ForumPost::whereParentId($value)->orderBy('created_at', 'DESC')->pluck('user_id')->first();
return User::whereId($lastPost)->pluck('username')->first();
}
Which I DO NOT want to do. I want to know the correct way of doing this :(
Here is my view
<ul>
#foreach ($category as $cat)
<li>
{{$cat->category_name}} - this is main category
{{$cat->category_description}} - this is main category description
#if ($cat->ForumSubCategory)
<ul>
#foreach ($cat->ForumSubCategory as $sub)
<li>
<a href="/forum/{{$cat->slug}}/{{$sub->slug}}"> - this is the category slug with the sub category slug
{{$sub->category_name}} - this is the sub category name
</a>
{{$sub->category_description}} - this is the sub category description
{{$sub->ForumPost->count()}} - this is how many posts are in the sub category
{{$sub->LastPost($sub->id)}} - this is how i am currently getting the last user who posted
</li>
#endforeach
</ul>
#endif
</li>
#endforeach
</ul>
Thanks for taking the time to look at this!
The fastest solution:
You can filter/order relationships, hence the function below.
latest() is an eloquent shortcut for orderBy('created_at','DESC')
ForumSubCategory Model:
class ForumSubCategory extends Model
{
...
public function latestPost()
{
return $this->hasOne('App\Models\Forum\ForumPost' , 'parent_id')->latest()->with('user');
}
}
In your view:
From:
{{$sub->LastPost($sub->id)}} - this is how i am currently getting the last user who posted
To:
#if($sub->latestPost)
#if($sub->latestPost->user)
{{$sub->latestPost->user->username or 'Username not set'}}
#else
{{'User Not Found'}}
#endif
#else
{{'No Last Post'}}
#endif
The most stable solution should be a service class around forum sub categories to take care of edge cases such as users being deleted, posts banned and so forth.
You could make use of dynamic properties. Check out Relationship Methods Vs Dynamic Properties
// Controller
public function lastPost()
{
$lastPost = ForumPost::whereParentId($value)->orderBy('created_at', 'DESC')->first();
$username = $lastPost->user->username;
return view('post.view', compact('username'));
}
Related
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');
}
This is my first laravel Application, and my first database based application so please be patient with me !!
I will try to be specific!!
Categories Table:
Id
Name
Timestamps
Posts table:
Id
title
body
slug
Category_id
timestamps
Lets say i have 4 catergories.
Laptops, computers,phones,tablets
I want when i go to /computers to be able to get all the posts that are specific to that category.
Posts Model
Category Model
Category Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Category;
class CatController extends Controller
{
public function getCategory($Category_id)
{
$posts = Post::where('Category_id',$Category_id);
return view('blog.index',['posts' => $posts]);
}
Route:
Route::get('computer/{Category_id}','CatController#getCategory');
I am really confused at the moment !!
Thanks everyone in advance!!
Define your Model
class Category extends Model
{
/**
* Get the posts.
*/
public function posts()
{
return $this->hasMany('App\Post', 'Category_id');
}
}
class Post extends Model
{
/**
* Get the category.
*/
public function category()
{
return $this->belongsTo('App\Category', 'Category_id');
}
}
Define your Controller
class CatController extends Controller
{
public function getCategory($Category_id)
{
$category = Category::find($Category_id);
if($category !== null){
$posts = $category->posts;
return view('blog.index',['posts' => $posts]);
}
}
I hope this will help you.
Route:
Route::get('categories/{category_id}/computers','CatController#show');
Controller:
public function show($category_id)
{
$category = Category::findOrFail($category_id);
if($category){
$posts = Post::where('Category_id',$category_id)->get();
return view('category.index', compact('posts'));
}
return view('errors.404');
}
Simply add this to your controller
public function category($id){
$data['posts'] = Post::where('status', 1)->where('category_id', $id)->orderBy('id', 'DESC')->get();
return view('frontEnd.home', $data);
}
I have three models:
Category
Post
Comment
These are their implementations:
class Category extends \Eloquent {
public function posts() {
return $this->hasMany('Post');
}
}
class Post extends \Eloquent {
public function category() {
return $this->belongsTo('Category');
}
public function comments() {
return $this->hasMany('Comment');
}
}
class Comment extends \Eloquent {
public function post() {
return $this->belongsTo('Post');
}
}
Is it possible, using eloquent, to get a list (array or collection) of all the comments (and only the comments, without posts) related to a Category?
(this means, given a category find all the related posts and then return all the related comments).
Thanks in advance!
Simply use hasManyThrough relation on your model Category :
class Category extends \Eloquent {
public function posts() {
return $this->hasMany(Post::class);
}
public function comments() {
return $this->hasManyThrough(Comment:class,Post::class);
}
}
After that, you can simply call $category->comments to have a Collection with all the comments related to a Category.
See https://laravel.com/docs/5.1/eloquent-relationships#has-many-through for extra information.
For Laravel 5.3 : https://laravel.com/docs/5.3/eloquent-relationships#has-many-through
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.
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');
}
}