Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

socieboy's avatar

Event Broadcasting, return post with user relation

When I do the broadcasting everything looks good! the posts are broadcasted but how can I access to the relation this post belongs to user on the client side to display the owner of the post?

0 likes
27 replies
mstnorris's avatar

Do you mean when you have:

Post model

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

PostsController Controller

public function show($id) {
    $post = Post::find($id);

    $post->load('user');

    return view('posts.show', compact('post'));
}

posts/show.blade.php View

$post->user()->name;
socieboy's avatar

@mstnorris

Thats what i mean, when i said broadcasting events.

this.pusherChannel.bind('App\Post', function(post) {
            console.log(post.user.id); 
});
socieboy's avatar

I got the Post object.

And I have already set the relationship and it works fine i tested already.

I don't know how to retrieve or return broadcasting the event the Post object with his User object.

romanbican's avatar

Add this method.

/**
 * Get the data to broadcast.
 *
 * @return array
 */
public function broadcastWith()
{
    return [
        'user' => $this->post->user,
    ];
}
fetch404's avatar

Just send the event as normal. The broadcasting feature serializes the public properties into JSON, so if you have a Post object defined as "$post" that has a defined relationship to User that is defined as "user", it will serialize that into JSON, allowing you to access the property you need by referencing "data.post.user.[property/relation name]"

Edit: I'm fairly sure this works. It worked for me, you shouldn't need to add broadcastWith(). That's for appending data to the master JSON (if you added "user" to broadcastWith(), and the original data was "post", the JSON would be "{post: {'blah': 'blah', 'test': 'test'}, user: {'name': 'Joe'}}", not "{post: {'blah': 'blah', 'test': 'test', user: {'name': 'Joe'}}}".

socieboy's avatar

@fetch404 yeah i want need is like

{post: {'blah': 'blah', 'test': 'test', user: {'name': 'Joe'}}}

How do I do reference to the user object on the javascript?

mstnorris's avatar

@socieboy if you followed what I put above, you will have access to the User as I already showed how to "lazy" load it.

socieboy's avatar

@mstnorris ok but i should get the user like that right??

this.pusherChannel.bind('App\Post', function(post, user) {
            console.log(user); 
});
mstnorris's avatar

No because User will be attached to Post, unless you want to run separate queries, which isn't usually the best idea.

fetch404's avatar

No, you don't.

this.pusherChannel.bind('App\\Events\\WhateverHappened', function(data) {
            console.log(data.post.user); 
});

You need to replace "App\Events\WhateverHappened" with your event's name.

1 like
socieboy's avatar

@mstnorris I used the code

public function broadcastWith()
{
    return [
            'user' => $this->post->user,
    ];
}

But i lose the post object.

fetch404's avatar

@socieboy Remove broadcastWith() altogether and make sure that the post property is public in your event class.

mstnorris's avatar

That is because you're not sending the Post through. Just the User.

socieboy's avatar

@mstnorris well... i need both!

@fetch404 I removed the broadcastWith() method and yeah the property $post is public

    public $post;

    public function __construct($post)
    {
        $this->post = $post;
    }

This works fine so far

this.pusherChannel.bind('App\\Events\\Posts', function(data) {
            console.log(data.post); 
});
mstnorris's avatar

@socieboy did you add this as I showed earlier?

public function show($id) {
    $post = Post::find($id);

    $post->load('user'); // this line in particular

    return view('posts.show', compact('post'));
}
socieboy's avatar

I think i should make and array with the information that i need then use the broadcastWith() method!

socieboy's avatar
socieboy
OP
Best Answer
Level 14

That's what i did

public function broadcastWith()
    {
        return [
            'post' => $this->prepareData(),
        ];
    }

    protected function prepareData()
    {
        return [
            'content'   => $this->post->content,
            'username'  => $this->post->user->name,
            'avatar'    => $this->post->user->avatar,
            'created_at' => $this->post->created_at,
        ];
    }

and it works fine !

2 likes
fetch404's avatar

Proof that broadcastWith() will NOT achieve what you want (from the Laravel Broadcasting code, BroadcastEvent.php):

    /**
     * Get the payload for the given event.
     *
     * @param  mixed  $event
     * @return array
     */
    protected function getPayloadFromEvent($event)
    {
        if (method_exists($event, 'broadcastWith')) {
            return $event->broadcastWith();
        }

        $payload = [];
        foreach ((new ReflectionClass($event))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
            $payload[$property->getName()] = $this->formatProperty($property->getValue($event));
        }

        return $payload;
    }

See the reference to broadcastWith()? That means that whatever you pass into there will be the returned payload. It does not actually append the data to the payload.

Also, I recommend using an event name that is different than "Posts". "Posts" what?

EDIT: I see you got it working.

1 like
romanbican's avatar

@fetch404 Are you sure that you have access to relations after you serialize model? Because I just test it and I don't think so...

So it seems like method broadcastWith() is the only option here.

JamesMills's avatar

Just a quick note if anyone else finds this. When you broadcast your event you may pass a full Model with relationships but when the item is taken off the queue and broadcasted then a new fresh instance of the Model is called from the database. This mean that any relationships you thought you had passed will no longer be there.

The easiest way to solve this will be to use the

class Lead extends Model

protected $with = [
    'user',
];
3 likes
aplomblife's avatar

I put what @JamesMills stated in my model:

<?php

namespace App\Models;

use Auth;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    //

    protected $fillable = ['body'];

    protected $appends = ['likeCount', 'likedByCurrentUser', 'canBeLikedByCurrentUser'];

    protected $with = [
    'user',
    ];
......

In addition, @socieboy solution:

Here is my event:

class PostWasCreated implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public $post;
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(Post $post)
    {
        $this->post = $post;
    }

    public function broadcastWith()
        {
            return [
                'post' => $this->prepareData(),
            ];
        }

        protected function prepareData()
        {
            return [
                'apple'   => $this->post->load(['user']),
                'user'   => "hello"
            ];
        }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('postsIT');
    }
}

I used the word apple just to see if the payload would be different going through instead of post as it should be.

my controller method:

    public function store(Request $request)
    {
      $this->validate($request, [
        'body' => 'required'
      ]);

      $post = $request->user()->posts()->create([
        'body' => $request->body
      ]);

      broadcast(new PostWasCreated($post))->toOthers();

      return response()->json([
        'data' => $post->load(['user'])
      ], 200);
    }

Here is the data and error I'm getting in the browser:

Pusher : Event recd : {"event":"App\\Events\\PostWasCreated","data":{"post":{"id":96,"user_id":2,"body":"rigged","created_at":"2016-10-28 14:05:23","updated_at":"2016-10-28 14:05:23","likeCount":0,"likedByCurrentUser":false,"canBeLikedByCurrentUser":false,"likes":[]}},"channel":"private-postsIT"}
vue.common.js?4a36:1019 [Vue warn]: Error when evaluating expression "post.user.name": TypeError: Cannot read property 'name' of undefined (found in component: <post>)warn @ vue.common.js?4a36:1019Watcher.get @ ?d41d:3176Watcher @ ?d41d:3158Directive._bind @ ?d41d:8345linkAndCapture @ ?d41d:6928compositeLinkFn @ ?d41d:6897Vue._compile @ ?d41d:8611Vue.$mount @ ?d41d:9446Vue._init @ ?d41d:2468Post @ VM9390:2build @ ?d41d:5837mountComponent @ ?d41d:5754(anonymous function) @ ?d41d:5716(anonymous function) @ ?d41d:5734cb @ vue.common.js?4a36:367Vue._resolveComponent @ ?d41d:8869resolveComponent @ ?d41d:5736setComponent @ ?d41d:5715bind @ ?d41d:5675Directive._bind @ ?d41d:8325linkAndCapture @ ?d41d:6928compositeLinkFn @ ?d41d:6897Fragment @ ?d41d:3757FragmentFactory.create @ ?d41d:3974create @ ?d41d:4210diff @ ?d41d:4109update @ ?d41d:4042_update @ ?d41d:8337Watcher.run @ ?d41d:3340runBatcherQueue @ ?d41d:3071flushBatcherQueue @ ?d41d:3041nextTickHandler @ vue.common.js?4a36:445
vue.common.js?4a36:1019 

[Vue warn]: Error when evaluating expression ""/channel/"+(post.user.channel[0].slug)": TypeError: Cannot read property 'channel' of undefined (found in component: <post>)

Why am I not getting apple and user in the response?

this is how my vue look like:


  events: {
    'child-msg': function (postIt) {
      this.posts.unshift(postIt);
  
    }
}
  Echo.private('postsIT').listen('PostWasCreated', (e) => {

        this.$dispatch('child-msg', e);
      });
camiant's avatar

in broadcastWith() simply force the object $post to "take on board" also the relations you need

    public function broadcastWith()
    {
        $this->post->user;
        $this->post->reads;
        ...

        return [
            'post' => $this->post
        ];
    }

Please or to participate in this conversation.