Laravel Echo not listening - laravel

I am trying to create a real-time post using Laravel with ReactJS and Pusher. I have been able to create pusher events and i could see the events showing on the pusher site. However, Laravel echo does not listen to the event whenever it is created and i cannot figure out why or how to make it to listen.
App\Events\PostCreated
<?php
namespace App\Events;
use App\Post;
use App\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class PostCreated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $post;
public $user;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Post $post, User $user)
{
//
$this->post = $post;
$this->user = $user;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return [
new PrivateChannel('new-post'),
new PrivateChannel('App.User.' . $this->post->user->id),
];
}
/**
* #return array
*/
public function broadcastWith() {
return [
'post' => array_merge($this->post->toArray(), [
'user' => $this->post->user,
]),
'user' => $this->user,
];
}
}
broadcasting.php
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => getenv('PUSHER_APP_KEY'),
'secret' => getenv('PUSHER_APP_SECRET'),
'app_id' => getenv('PUSHER_APP_ID'),
'options' => [
'cluster' => getenv('PUSHER_APP_CLUSTER'),
'encrypted' => true,
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
.env
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
PUSHER_APP_ID=757605
PUSHER_APP_KEY=4100ca8b118192fd01b2
PUSHER_APP_SECRET=41f43d23204a3c7ae2a7
PUSHER_APP_CLUSTER=ap1
bootstrap.js
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
import Echo from 'laravel-echo'
window.Pusher = require('pusher-js');
//const client = require('pusher-js');
//import 'pusher-js/node';
window.Echo = new Echo({
broadcaster: 'pusher',
key: '4100ca8b118192fd01b2',
cluster: 'ap1',
encrypted: true
});
channels.php
<?php
/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/
use Illuminate\Support\Facades\Auth;
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Broadcast::channel('new-post', function ($user) {
return Auth::check();
});
PostController
public function create(Request $request, Post $post) {
$data = [];
$video_data = [];
if ($request->get('file')) {
foreach ($request->get('file') as $file) {
$name = md5(uniqid()) . '.' . explode('/', explode(':', substr($file, 0, strpos($file, ';')))[1])[1];
$upload = Uploader::upload($file, array('public_id' => $name));
array_push($data, $upload['secure_url']);
}
}
if ($request->get('video')) {
foreach ($request->get('video') as $file) {
$name = md5(uniqid() . '.' . explode('/', explode(':', substr($file, 0, strpos($file, ';')))[1]))[1];
$upload = Uploader::upload($file, array('public_id' => $name, 'resource_type' => 'video'));
array_push($video_data, $upload['secure_url']);
}
}
$image = !empty($data) ? json_encode($data) : null;
$video = !empty($video_data) ? json_encode($video_data) : null;
$body = $this->test_data($request->body);
// create post
$createdPost = $request->user()->posts()->create([
'body' => $body,
'image' => $image,
'video' => $video
]);
// broadcast
broadcast(new PostCreated($createdPost, $request->user()))->toOthers();
// return the response
return response()->json($post->with('user')->find($createdPost->id));
}
app.blade.php (App Layout)
<script>
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
'user' => [
'id' => Auth::check() ? Auth::user()->id : null,
'following' => Auth::check() ? Auth::user()->following()->pluck('users.id') : null
],
]);
?>
</script>
ReactJS (Frontend)
componentDidMount() {
Echo.private('new-post').listen('PostCreated', (e) => {
if (window.Laravel.user.following.includes(e.post.user_id)) {
this.setState({ posts: [e.post, ...this.state.posts] });
}
});
// this.interval = setInterval(()=>this.getPosts(), 10000);
}
I expect the followers of the user that created a new post to see the post instantly without reloading the page but instead, nothing happens.
The event is created but it is not listened to. What am i doing wrong?

try to set broadcastAs in your event class and explicitly set the broadcast name in reactjs part.
App\Events\PostCreated
<?php
namespace App\Events;
class PostCreated implements ShouldBroadcast
{
// other code
public function broadcastAs()
{
return 'PostCreated';
}
}
ReactJS (Frontend)
componentDidMount() {
Echo.private('new-post').listen('.PostCreated', (e) => {
if (window.Laravel.user.following.includes(e.post.user_id)) {
this.setState({ posts: [e.post, ...this.state.posts] });
}
});
// this.interval = setInterval(()=>this.getPosts(), 10000);
}
notice that I use .PostCreated ( and not PostCreated). By, default, laravel has convention on the broadcast name. I prefer explicit programming style so I usually set broadcastAs for all my events.

I was able to figure it out and noticed that i have to keep running the following command:
php artisan queue:listen

Related

How can i send a laravel email with SMTP without getting the .env data?

I need to fetch the SMTP data in the account of user that will be saved in the database. That way it couldn't pull the data from the .env file.
I'm using Laravel 8
For example, instead of looking for SMTP authentication in .env I would look in the database:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Mail;
use Config;
use App\Mail\EmailManager;
class NewsletterController extends Controller
{
public function testEmail(Request $request){
$array['view'] = 'emails.newsletter';
$array['subject'] = "SMTP Test";
$array['from'] = 'email#email.com';
$array['content'] = "This is a test email.";
Config::set('mail.host', 'smtp.mailtrap.io');
Config::set('mail.username', 'email#email.com');
Config::set('mail.password', 'd13ea2a29a5cee');
Config::set('mail.port', 587);
try {
Mail::to($request->email)->send(new EmailManager($array));
} catch (\Exception $e) {
dd($e);
}
flash(translate('An email has been sent.'))->success();
return back();
}
}
I solved.
I Created a file with configutations:
/app/Providers/MailConfigServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use App\Models\EmailSetting;
use Config;
class MailConfigServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
if (\Schema::hasTable('email_settings')) {
$mail = DB::table('email_settings')->first();
if ($mail) //checking if table is not empty
{
$config = array(
'driver' => $mail->driver,
'host' => $mail->host,
'port' => $mail->port,
'from' => $mail->from,
'encryption' => $mail->encryption,
'username' => $mail->username,
'password' => $mail->password,
'sendmail' => '/usr/sbin/sendmail -bs',
'pretend' => false,
);
Config::set('mail', $config);
}
}
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
And added this line on app/config
App\Providers\MailConfigServiceProvider::class

Laravel is not sending Pusher Events to my Vue.js frontend

I have a Vue.js separated project from a Laravel backend in many tutorials I see a Laravel + Vue.js but I have a Vue.js SPA separated and I don`t know what I doing wrong.
I already make the configuration on front end, this is it:
const pusher = new Pusher(configMain.PUSHER_APP_KEY, {
cluster: configMain.PUSHER_APP_CLUSTER
});
const pusherChannel = pusher.subscribe('teste');
pusherChannel.bind('my-event', function(data) {
console.log(data);
});
When I use the debug console in PUSHER and send an event, it works fine I receive it. But when I try to send an event from my backend Laravel project it doesn`t work.
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=********
PUSHER_APP_KEY=********
PUSHER_APP_SECRET=********
PUSHER_APP_CLUSTER=********
I already set the keys and change the driver, in config broadcasting i have this:
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'encrypted' => true,
],
],
I created an event in Laravel that is this:
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MyEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
private $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($message)
{
//
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('teste');
}
public function broadcastAs()
{
return 'my-event';
}
}
And when i execute this in my backend:
event(new MyEvent('hello world'));
dd("ENTROU");
My front doesn't receive it, I must do something that I don't see it?

Why CORS Error Access-Control-Allow-Credentials' header in the response is ''?

I make #vue/cli 4.5.9 / axios app with data reading from Laravel 7
Backend REST(with "tymon/jwt-auth": "^1.0", "barryvdh/laravel-cors": "^1.0.5") API using axios
and I got error :
has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
But here I check that all Credentials are filled : https://prnt.sc/vsngs5
I have on client side:
console.log('+login settingCredentialsConfig::')
console.log(settingCredentialsConfig)
axios.post(apiUrl + '/login', userCredentials, settingCredentialsConfig)
.then((response) => {
if (typeof response.data.access_token === 'undefined' || typeof response.data.user === 'undefined') {
commit('auth_error') // call auth_error mutation to make changes to vuex store
bus.$emit('authLoggedError')
return
}
I import settingCredentialsConfig from setings file defined as :
export const settingCredentialsConfig = {
withCredentials:true,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials':true
}
}
and in src/main.js axios defined :
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from 'axios'
export const bus = new Vue()
axios.defaults.crossDomain = true
let apiUrl = process.env.VUE_APP_API_URL
Vue.config.productionTip = false
Vue.prototype.$http = axios
Vue.config.productionTip = false
import Vue2Filters from 'vue2-filters'
Vue.use(Vue2Filters)
new Vue({
router,
store,
bus,
render: h => h(App)
}).$mount('#app')
Vue.use({
install (Vue) {
Vue.prototype.$api = axios.create({
baseURL: apiUrl
})
}
})
In other vue page I have request to backend using the same settingCredentialsConfig var and
it works ok. But it does not refere auth of backend User model.
On backenbd part I have app/User.php :
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject; // JWT SUPPORT
class User extends Authenticatable implements JWTSubject // JWT SUPPORT
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function getJWTIdentifier() // JWT SUPPORT
{
return $this->getKey();
}
public function getJWTCustomClaims() // JWT SUPPORT
{
return [];
}
}
In app/Http/Controllers/API/AuthController.php :
<?php
namespace App\Http\Controllers\API;
use Auth;
use Config;
use DB;
use Validator;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
//use Illuminate\Support\Str;
class AuthController extends Controller
{
protected $maxAttempts = 1; // Default is 5
protected $decayMinutes = 1; // Default is 1
/**
* Create a new AuthController instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('jwt.auth', ['except' => ['login', 'register', 'activate']]);
}
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
\Log::info('LOGIN +1 $credentials ::');
if ($token = $this->guard('api')->attempt($credentials /*,['exp' => \Carbon\Carbon::now()->addHours(4)->timestamp]*/)) {
$loggedUser = $this->guard('api')->user();
\Log::info('LOGIN +2 $loggedUser ::');
return $this->respondWithToken($token);
}
\Log::info('LOGIN -3 ::');
return response()->json(['error' => 'Unauthorized'], 401);
}
public function getAuthenticatedUser()
{
try {
\Log::info('-1 getAuthenticatedUser ::');
if ( ! $user = JWTAuth::parseToken()->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
\Log::info('-2 getAuthenticatedUser ::');
return response()->json(['token_expired'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
\Log::info('-3 getAuthenticatedUser ::');
return response()->json(['token_invalid'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
\Log::info('-4 getAuthenticatedUser ::');
return response()->json(['token_absent'], $e->getStatusCode());
}
// the token is valid and we have found the user via the sub claim
return response()->json(compact('user'));
}
public function me()
{
return response()->json($this->guard('api')->user());
}
public function logout()
{
\Log::info('-1 logout ::' . print_r(-1, true));
$this->guard('api')->logout();
return response()->json(['message' => 'Successfully logged out']);
}
public function refresh()
{
return $this->respondWithToken($this->guard()->refresh());
}
protected function respondWithToken($token)
{
$loggedUser = $this->guard()->user();
return response()->json([
'access_token' => $token,
'user' => $loggedUser,
'token_type' => 'bearer',
'expires_in' => $this->guard('api')->factory()->getTTL() * 999360 // TOFIX
]);
}
public function guard()
{
return \Auth::Guard('api');
}
}
and in config/cors.php :
<?php
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
I generated file config/jwt.php with command :
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
and left it unchanged .
In file app/Http/Kernel.php :
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
...
What did I miss ?
Thanks!
Change the supports_credentials value in your config/cors.php file to true.

Laravel Vapor- Allow unauthenticated user perform an upload

By default Laravel Vapor requires the user be authenticated to perform an upload. I need to allow an unauthenticated perform an upload. How can I do this?
Thanks
I think if you add something like this in your UserPolicy, vapor should work
public function before($user)
{
if (Auth::guest()) {
return true;
}
}
We handled this by overriding the default vapor/signed-storage-url route's handling.
In routes/web.php.
Route::post('vapor/signed-storage-url', [Controllers\SignedStorageUrlController::class, 'store']);
In our SignedStorageUrlController, we use the store function with this check removed:
Gate::authorize('uploadFiles', [
$request->user(),
$bucket = $request->input('bucket') ?: $_ENV['AWS_BUCKET'],
]);
resulting in:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Laravel\Vapor\Http\Controllers\SignedStorageUrlController as VaporSignedStorageUrlController;
class SignedStorageUrlController extends VaporSignedStorageUrlController
{
public function store(Request $request)
{
$this->ensureEnvironmentVariablesAreAvailable($request);
$bucket = $request->input('bucket') ?: $_ENV['AWS_BUCKET'];
$client = $this->storageClient();
$uuid = (string) Str::uuid();
$signedRequest = $client->createPresignedRequest(
$this->createCommand($request, $client, $bucket, $key = ('tmp/'.$uuid)),
'+5 minutes'
);
$uri = $signedRequest->getUri();
return response()->json([
'uuid' => $uuid,
'bucket' => $bucket,
'key' => $key,
'url' => 'https://'.$uri->getHost().$uri->getPath().'?'.$uri->getQuery(),
'headers' => $this->headers($request, $signedRequest),
], 201);
}
}

Pusher receive event with null values

I'm using laravel 5.8 version. I'm sending a message to pusher but put pusher receive event {"message": null}.
Here is my broadcasting.php file
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
// 'cluster' => env('PUSHER_APP_CLUSTER'),
// 'useTLS' => true,
'cluster' => 'ap2',
'useTLS' => true
],
],
There is my chatevent.php file.
namespace App\Events
use Illuminate\Broadcasting\Channel;
use Illuminate\Foundation\Auth\User;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ChatEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
public $user;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($message, User $user)
{
$this->$message = $message;
$this->$user = $user;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
There is my bootstrap.js file.
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// encrypted: truebroadcaster: 'pusher',
broadcaster: 'pusher',
key:'REDACTED',
cluster: 'ap2',
encrypted: true
});
My controller function where I'm sending message to pusher.
public function send()
{
$message = 'hello';
$user = User::find(Auth::id());
event(new ChatEvent($message, $user));
}
}
How to fix it????
I think you write the constructor of your event class wrong.
Here you put extra $ before message and user variable
public function __construct($message, User $user)
{
$this->$message = $message;
$this->$user = $user;
}
It should be like following one.
public function __construct($message, User $user)
{
$this->message = $message;
$this->user = $user;
}

Resources