Then you have to do that from the BookingList component which contains for loop. Listen there for all bookings and reload (call API to fetch new list) when required.
Wow, that is interesting.
created() {
window.Echo.private(`Bookings.all`)
.listen('BookingEvent', (e) => {
this.booking = e.booking;
// API call
});
},
How would a simple API call look like?
Just use axios or fetch.
axios.get('https://site.domian/api/bookings')
.then(response => this.bookings = response.data.data);
Note that this is just a snippet, I have no idea what are your routes and which data structure is returned.
Ok, that is new to me. Does this have to be an API call and how would I structure the API route. Do you have any documentation references, where I could read more about it?
All bookings are on the /home route, I just tried:
created() {
window.Echo.private(`Bookings.all`)
.listen('BookingEvent', (e) => {
this.booking = e.booking;
axios.get('/home')
.then(response => this.bookings = response.data.data);
});
},
which returns a Timestamp expired: Given timestamp , not within 600ms.
Thank you for helping so much @bugsysha
You structure it like normal routes. There is an routes/api.php file to which you can add your API routes. Since you are asking these kind of questions I do not want to throw too much info in your direction so I think first thing is to get it working. Later you can worry about structure and other stuff.
Thank you @bugsysha
But why is it so complex to push a new booking to a view, when listening to the Event at this view?
It is not complex. You are just trying to change something that was not supposed to be changed. Usually props should not be changed. When you figure out flow of data you will start thinking in that way and everything will feel natural from that point on.
Ok, unterstand, but now I moved the created() method to the the BookingListComponent, and it is not working. And I have not yet created an API route, and don't know what I have to pass to that route.
Through a simple HomeController, I compact $bookings = Booking::all(); to the /home route, and pass it as a prop to the BookingListComponent. Within the BookingComponent, I could listen to the Event, but we had the mutation error.
What would the next step be? Do I e.g. need to put the /home route into an auth:api middleware, in order to make it work?
You do not have to pass anything to that route.
public function index() // assuming that the name of the controller method is index
{
return Booking::all();
}
And do not pass data to BookingList component via props cause you are fetching it via API.
Sorry @bugsysha , this is my current setup:
Route::get('/home', 'HomeController@home')->name('home'); // Where I listen for bookings
// HomeController
public function home(){
$bookings_ = Booking::all();
$bookings = BookingResource::collection($bookings_);
return view('home.show', compact('bookings'));
}
show.blade.php (home)
<booking-list :bookings="{{$bookings->toJson()}}"></booking-list>
BookingListComponent
<template>
<div>
<div v-for="booking in bookings">
<Booking :booking="booking" />
</div>
</div>
</template>
<script>
export default {
props: ['bookings'],
created() {
window.Echo.private(`Bookings.all`)
.listen('BookingEvent', (e) => {
this.booking = e.booking;
axios.get('/api/bookings')
.then(response => this.bookings = response.data.data);
});
},
}
</script>
BookingComponent
<template>
<div>
<p>{{booking.id}}</p>
</div>
</template>
<script>
export default {
name: 'Booking'
props: ['booking'],
methods: {
},
}
</script>
web.php
Broadcast::channel('Bookings.all', function () {
return true;
});
api.php
Route::get('api/bookings', function (Booking $booking) {
return $booking;
});
What do I need to change?
Something like this. But there is so many moving parts that I doubt it will work on first try.
// HomeController
public function home()
{
return view('home.show');
}
<template>
<div>
<div v-for="booking in bookings">
<Booking :booking="booking" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
bookings: []
}
},
created() {
window.Echo.private(`Bookings.all`)
.listen('BookingEvent', (e) => {
this.fetchBookings();
});
},
methods: {
fetchBookings() {
axios.get('/api/bookings').then(response => this.bookings = response.data.data);
}
}
}
</script>
Route::get('bookings', function () {
return Booking::all();
});
So, if I understand it correctly the API route will allow us to trigger the bookings whenever we visit the home route, instead of passing the bookings through the home route?
In a nutshell, yes.
When you figure out flow of data you will start thinking in that way and everything will feel natural from that point on.
It makes perfect sense, now @bugsysha
After I create a Booking, it triggers the Event and shows the new booking alongside all others. When I refresh all bookings are gone. How can I show all bookings always, and then add the new one through the component and API?
Try following in your booking list component:
created() {
if (!this.bookings.lenght) {
this.fetchBookings();
}
window.Echo.private(`Bookings.all`)
.listen('BookingEvent', (e) => {
this.fetchBookings();
});
},
Nice, this one works. Thank you, again, for your patience @bugsysha
@splendidkeen you are very welcome.
How could I adjust the output of bookings to be broadcasted only to specific users who e.g. have a eloquent relationship to groups?
If I have something like:
$groups = Auth::user()->groups;
$bookings = optional($groups->map->bookings->reject->isEmpty()->flatten())->take(20);
Is there a way to implement such a logic to the channel, or api route?
@splendidkeen not sure what exactly are you asking so maybe this video will help better explain it than I can with words and my confusing flow of thoughts 🤞🏻
Thank you for the link, very helpful @bugsysha
Your flow of thoughts is not confusing at all. What I was thinking of, is maybe in this logic more clear:
When I have a Channel like our bookings.all, where Booking::all(), will be broadcasted, whenever the create method is triggered. If I am a user, and I have friends(), how could I restructure bookings.all, to only broadcast bookings from my friends()?
It comes onTeam() from the video very near, somehow, right?
That sounds more like friend bookings channel than bookings all in my mind. Just verify that the booking belongs to a authenticated user or to a friend of authenticated user and that is it.
Would then only the channel look different, and the api route stay the same?
Broadcast::channel('bookings-friends.{bookingId}', function (User $user, int $bookingId) {
return $user->id === Booking::findOrFail($bookingId)->user_id; // here we authenticate the user right?
});
How would that look like when we check on friends()?
public function friends(){
return $this->belongsToMany(User::class, 'friends', 'user_id', 'friend_id');
}
Would then only the channel look different, and the api route stay the same?
I would change the route also.
How would that look like when we check on friends()?
Something like...
$user->friends()->where('friend_id', $booking->user_id)->count();
$user->friends()->where('friend_id', $booking->user_id)->count();
Thank you @bugsysha
Would that be a good way to start:
Broadcast::channel('bookings-friends.{bookingId}', function (User $user, int $bookingId) {
$booking = Booking::findOrFail($bookingId);
return $user->friends()->where('friend_id', $booking->user_id)->count();
});
And how would you consider changing the api route? So I could start testing the changes. If there are any problems occurring, I would transfer this maybe to another thread, because this one is already solved.
Again, thank you for still communicating, here. It helps immensely. @bugsysha
Do not forget the first check cause you need it.
return $user->id === Booking::findOrFail($bookingId)->user_id
And how would you consider changing the api route?
Thank you @bugsysha
You are very welcome.
Please or to participate in this conversation.