Laravel Associate method is returning empty array in the view
Asked Answered
M

3

2

I am working on a simple social network, i am working on the replies section now. I associated the post/status model with user as below, but when i tried to access in the view it returns empty array [].

Reply function in Post Model

public function postReply(Request $request, $statusId){
        $this->validate($request, [
            "reply-{$statusId}" => 'required|max:1000',
        ],
        [
            'required' => 'The reply body is required'
        ]
        );

        $post = Post::notReply()->find($statusId);
        if(!$post){
            return redirect('/');
        }

        if(!Auth::user()->isFriendsWith($post->user) && Auth::user()->id !== $post->user->id){
            return redirect('/');
        }

        $post = Post::create([
            'body' => $request->input("reply-{$statusId}"),
        ]);
        $post->user()->associate(Auth::user());
        $post->replies()->save($post);


        return redirect()->back();
    }

Post Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $table = 'posts';
    protected $fillable = ['body'];
    public function user(){
        return $this->belongsTo('App\User');
    }

    public function likes(){
        return $this->hasMany('App\Like');
    }

    public function scopeNotReply($query){
        return $query->whereNull('parent_id');
    }

    public function replies(){
        return $this->hasMany('App\Post', 'parent_id');
    }
}

Posts Table

enter image description here

View Where i am accessing the Replies via $post->replies

<section class="row new-post">
        <div class="col-md-6 col-md-offset-3">
            <header><h3>What do you have to say?</h3></header>
            <form action="{{ url('createpost') }}" method="post">
                <div class="form-group">
                    <textarea class="form-control" name="body" id="new-post" rows="5" placeholder="Your Post"></textarea>
                </div>
                <button type="submit" class="btn btn-primary">Create Post</button>
                <input type="hidden" value="{{ Session::token() }}" name="_token">
            </form>
        </div>
    </section>
    <section class="row posts">
        <div class="col-md-6 col-md-offset-3">
            <header><h3>What other people say...</h3></header>
                @foreach($posts as $post)
                <article class="post media" data-postid="{{ $post->id }}">
                    <a class="pull-left" href="{{ url('user', $post->user->username) }}">
                        <img class="media-object" alt="" src="{{ $post->user->getAvatarUrl() }}">
                    </a>
                    <div class="media-body">
                      <h4 class="media-heading"><a href="{{ url('user', $post->user->username) }}"> {{ $post->user->username }}</a></h4>
                        <p>{{ $post->body }}</p>
                        <ul class="list-inline">
                            <li>{{ $post->created_at->diffForHumans() }}</li>
                            <li><a href="#">Like</a></li>
                            <li>10 likes</li>
                        </ul>
                        {{ $post->replies }}

                    </div>

                    <form role="form" action="{{ url('createpost',  $post->id )}}" method="post">
                            <div class="form-group{{ $errors->has("reply-{$post->id}") ? ' has-error': '' }}">
                                <textarea name="reply-{{ $post->id }}" class="form-control" rows="2" placeholder="Reply to this status"></textarea>
                                @if($errors->has("reply-{$post->id}"))
                                    <span class="help-block">{{ $errors->first("reply-{$post->id}") }} </span>
                                @endif
                            </div>
                            <input type="submit" value="Reply" class="btn btn-default btn-sm">
                            <input type="hidden" name="_token" value="{{ Session::token() }}">
                    </form>

                </article>

                @endforeach

        </div>

</section>

PS : I am using one table for the relationship i.e posts and in posts table there is a column name parent_id via which i am linking the relationship to the table with itself.

{{ $post->replies }} returns empty array. By logic it should return the replies of comment related to the replies .

If any thing else is needed, just mention i will share.

UPDATE : Note that the all the replies the user comment is stored in the database table posts with unique id i.e parent_id The only thing is that when i try to access it it returns empty array.

Mauro answered 8/8, 2016 at 10:4 Comment(7)
can you confirm if your table is populating correctly ?Kristinakristine
yes i have mentioned it in the update @KristinakristineMauro
which function is calling your view ?Kristinakristine
I mean where you are passing $post to view ?Kristinakristine
I try to fetch the replies with {{ $post->replies }} and from controller i pass it like if(Auth::check()){ $posts = Post::notReply()->where(function($query){ return $query->where('user_id', Auth::user() ->id)->orWhereIn('user_id', Auth::user()->friends()->lists('id')); })->orderBy('created_at', 'desc') ->get(); return view('timeline.index', compact('posts')); }Mauro
I can't see notReply() in your post model, where this method is defined ?Kristinakristine
Can you provide some table data?Forebrain
K
1

It seems like your are not attaching the replies to your $post variable while passing it to your view.
We have to use with() method to join these two tables.
Try, the following :

if(Auth::check()){ 
            $posts = Post::notReply()
                    ->where(function($query){ 
                        return $query->where('user_id', Auth::user()->id)
                                ->orWhereIn('user_id', Auth::user()->friends()->lists('id'));
                    })
                    ->with('replies')//Here we are joining the replies to the post
                    ->orderBy('created_at', 'desc')
                    ->get();
                    return view('timeline.index', compact('posts')); 
                }

Now, in your View you would need to use 2 foreach loop like below:

@foreach($posts as $post)
    {{--Your Code --}}
    @foreach($post->replies as $reply)
        {{--Your Code --}}
    @endforeach
    {{--Your Code --}}
@endforeach

Update:

Problem is with the postReply() method. In old code you were overriding the $post variable, due to which the reply was getting parent_id as its own id.
So, replace your old code with using $reply variable.
old:

$post= Post::create([
   'body' => $request->input("reply-{$statusId}"),
]);
$post->user()->associate(Auth::user());
$post->replies()->save($post);

new:

$reply = Post::create([
   'body' => $request->input("reply-{$statusId}"),
]);
$post->user()->associate(Auth::user());
$post->replies()->save($reply);
Kristinakristine answered 12/8, 2016 at 5:38 Comment(8)
Still returning [] empty arrayMauro
try vardump once on $posts in viewKristinakristine
It returns [{"id":11,"user_id":2,"parent_id":null,"body":"This is my Stats i am GOG!","created_at":"2016-08-12 17:14:00","updated_at":"2016-08-12 17:14:00","replies":[]}, This result is for echoing {{ $posts }}Mauro
sorry for asking so many questions, what is notReply() method ? It's not in your model. Also, once try to remove your where() method, I think it might be a problem ...Kristinakristine
No problem you have right, you are helping me . It is scope method which will insure that the comment has no reply when a user is commenting.For more laravel.com/docs/5.2/eloquent#query-scopesMauro
which where() method i should remove? you mean in the posts controller which i am passing to view?Mauro
Hi @Iftikharuddin , I read about the scopes. Not sure, but is it possible that the your notNull scope is applying to your replies also ? So, could you try removing the notNull Scope?Kristinakristine
Not working dear. Can i have your contact so that i can send you the whole code?Mauro
M
1

You are using $post for both the parent post and the reply, hence in

$post->replies()->save($post);

you are actually setting $post to be a reply to itself.

Would also be worth noting that column id is UNSIGNED but user_id and parent_id are not. I presume that there are foreign keys set on these?

Edit

Try this with a new $reply variable.

public function postReply(Request $request, $statusId){
    $this->validate($request, [
        "reply-{$statusId}" => 'required|max:1000',
    ],
    [
        'required' => 'The reply body is required'
    ]
    );

    $post = Post::notReply()->find($statusId);
    if(!$post){
        return redirect('/');
    }

    if(!Auth::user()->isFriendsWith($post->user) && Auth::user()->id !== $post->user->id){
        return redirect('/');
    }

    $reply = Post::create([
        'body' => $request->input("reply-{$statusId}"),
    ]);
    $reply->user()->associate(Auth::user());
    $post->replies()->save($reply);


    return redirect()->back();
}
Mickey answered 8/8, 2016 at 10:30 Comment(11)
I am using associate method. This method will set the foreign key on the child model automatically. And i am using one table i.e posts for it.Mauro
The issue is that you are reusing the same variable name for what I think are two separate posts, the parent and the reply, hence the parent is getting overwritten with the replyMickey
still returning empty array [] , no replies.Mauro
I think return redirect()->back(); is a bit like clicking back on your browser, hence the view will have stale data, it would be better to redirect to your view explicitly unless there's a reason not to.Mickey
Also have you checked in your database to see that the correct row is being inserted?Mickey
yes @edlouth data is inserted in database. In simple words in the parent_id field the ID of the specific reply is present.Mauro
So this is probably a problem with the controller or view that shows the posts, I note that you've got {{ $post->replies }} directly in the view which is unlikely to work, you probably need a @foreach loop. Maybe post some detail of the controller? or do a var_dump() on $posts[0] to see if anything is there.Mickey
Yes @edlouth, i will use @foreach but if the $post->replies is empty how can i loop through ? Plus i tried that too but nothing is showing.Mauro
{{ $post->replies[0] }} is returning Undefined offset error.Mauro
Let us continue this discussion in chat.Mauro
Your solution was perfect i don't know why it was not working at the time i implemented but now it is working perfect.Mauro
K
1

It seems like your are not attaching the replies to your $post variable while passing it to your view.
We have to use with() method to join these two tables.
Try, the following :

if(Auth::check()){ 
            $posts = Post::notReply()
                    ->where(function($query){ 
                        return $query->where('user_id', Auth::user()->id)
                                ->orWhereIn('user_id', Auth::user()->friends()->lists('id'));
                    })
                    ->with('replies')//Here we are joining the replies to the post
                    ->orderBy('created_at', 'desc')
                    ->get();
                    return view('timeline.index', compact('posts')); 
                }

Now, in your View you would need to use 2 foreach loop like below:

@foreach($posts as $post)
    {{--Your Code --}}
    @foreach($post->replies as $reply)
        {{--Your Code --}}
    @endforeach
    {{--Your Code --}}
@endforeach

Update:

Problem is with the postReply() method. In old code you were overriding the $post variable, due to which the reply was getting parent_id as its own id.
So, replace your old code with using $reply variable.
old:

$post= Post::create([
   'body' => $request->input("reply-{$statusId}"),
]);
$post->user()->associate(Auth::user());
$post->replies()->save($post);

new:

$reply = Post::create([
   'body' => $request->input("reply-{$statusId}"),
]);
$post->user()->associate(Auth::user());
$post->replies()->save($reply);
Kristinakristine answered 12/8, 2016 at 5:38 Comment(8)
Still returning [] empty arrayMauro
try vardump once on $posts in viewKristinakristine
It returns [{"id":11,"user_id":2,"parent_id":null,"body":"This is my Stats i am GOG!","created_at":"2016-08-12 17:14:00","updated_at":"2016-08-12 17:14:00","replies":[]}, This result is for echoing {{ $posts }}Mauro
sorry for asking so many questions, what is notReply() method ? It's not in your model. Also, once try to remove your where() method, I think it might be a problem ...Kristinakristine
No problem you have right, you are helping me . It is scope method which will insure that the comment has no reply when a user is commenting.For more laravel.com/docs/5.2/eloquent#query-scopesMauro
which where() method i should remove? you mean in the posts controller which i am passing to view?Mauro
Hi @Iftikharuddin , I read about the scopes. Not sure, but is it possible that the your notNull scope is applying to your replies also ? So, could you try removing the notNull Scope?Kristinakristine
Not working dear. Can i have your contact so that i can send you the whole code?Mauro
A
0

You have setup your relations wrong. In your post model, instead of post having a self one-to-many relationship. It should be like this:

// App\User.php
public function replies()
{
    return $this->hasMany(App\Reply::class);
}

// App\Post.php
public function replies()
{
    return $this->hasMany(App\Reply::class);
}

// App\Reply.php
public function post()
{
    return $this->belongsTo(App\Post::class);
}

public function user()
{
    return $this->belongsTo(App\User::class);
}

public function comments()
{
    return $this->belongsTo(App\Reply::class, 'parent_id');
}
Alexis answered 10/8, 2016 at 6:8 Comment(6)
I am using associate method. This method will set the foreign key on the child model automatically. And i am using one table i.e posts for it And i don't have Reply Model so it throws error reply method not foundMauro
I got that but for that i have to create another table replies right?Mauro
Yes. Replies shall be in a separate table.Alexis
That's the point nah, i don't want to create other table. I am using one table for this relation ship. Is this possible? I am following this series ( youtube.com/…)Mauro
Dude use common sense 😉Alexis
Let us continue this discussion in chat.Mauro

© 2022 - 2024 — McMap. All rights reserved.