Laravel pusher Illuminate \ Broadcasting \ BroadcastException No message - laravel

I'am using Laravel 5.5 with pusher to make a real time notification , the notification made from the Api
after i made the configuration
in the Api
public function store(Request $request)
{
$advertising = Advertising::create($request->all());
$admins = \App\Admin::all();
\Notification::send( $admins, new \App\Notifications\AdvertisingAdded($advertising) );
return $advertising;
}
in AdvertisingAdded
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\BroadcastMessage;
use App\Advertising;
class AdvertisingAdded extends Notification
{
use Queueable;
//-must be public fir pusher
public $advertising;
public function __construct(Advertising $advertising)
{
$this->advertising = $advertising;
}
public function via($notifiable)
{
return ['database','broadcast'];
}
public function toArray($notifiable)
{
return [
'msg' => 'Advertising '.$this->advertising->title_ar.' is added ',
'advertising_id' => $this->advertising->id
];
}
public function toBroadcast($notifiable)
{
return new BroadcastMessage([
'msg' => 'Advertising '.$this->advertising->title_ar.' is added ',
'advertising_id' => $this->advertising->id
]);
}
}
when i post from postman i get an error
Illuminate \ Broadcasting \ BroadcastException No message
error image
i followed this video https://www.youtube.com/watch?v=i6Rdkv-DLwk

i solve my problem by : making the encrypted: false

Add curl options to broadcasting.php
`'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,
'curl_options' => [
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
]
],`

I solved this problem. In config/broadcasting.php use this code
'options' => [
'cluster' => 'eu',
'useTLS' => false
],
make useTLS false

In laravel 7, configured like below to config/broadcasting.php and and run artisan command cache:clear. solved for me.
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => 'mt1',
'useTLS' => false,
],
],

I solved my problem by setting my .env file
Set:
APP_URL=http://localhost
DB_HOST=localhost
And run
php artisan config:cache

Related

Laravel - Redis error when dispatching a job

On one of the systems that I take care of, some times some jobs don't get dispatched due to a connection problem with Redis and this ends up returning an error to the user, on our side we can ignore this error and just miss this job, I looked for how to deal with it on Google and I didn't find anything about it.
public function sendMessage(Request $request, Model $model)
{
// Do the necessary stuff
ResolveMessageBilling::dispatch($model, $request->all());
return response()->json([
'message' => 'The message was succesfully sent'
], 200);
}
This is the error we are getting: RedisException - socket error on read socket
How to ignore the error if it occurs? A simple try/catch can resolve the issue?
public function sendMessage(Request $request, Model $model)
{
// Do the necessary stuff
try {
ResolveMessageBilling::dispatch($model, $request->all());
} catch(\Exception $e) {}
return response()->json([
'message' => 'The message was succesfully sent'
], 200);
}
If you want to bypass ANY error, you should use \Throwable instead of \Exception
public function sendMessage(Request $request, Model $model)
{
// Do the necessary stuff
try {
ResolveMessageBilling::dispatch($model, $request->all());
} catch(\Throwable $e) {}
return response()->json([
'message' => 'The message was succesfully sent'
], 200);
}
see Error Hierarchy: https://www.php.net/manual/en/language.errors.php7.php
If you want to bypass only the \RedisException, you should be able to use:
public function sendMessage(Request $request, Model $model)
{
// Do the necessary stuff
try {
ResolveMessageBilling::dispatch($model, $request->all());
} catch(\RedisException $e) {}
return response()->json([
'message' => 'The message was succesfully sent'
], 200);
}
If you don't want to setup Redis just want to fixed/remove errors only, follow this article: https://laravel.com/docs/7.x/errors
IF you want to Setup Redis(config -> detabase.php) properly, follow few step like this:
'redis' => [
'client' => 'predis',
// Keep Default as is you want to use both redis and sentinel for different service(cache, queue)'
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
// Create a custom connection to use redis sentinel
'cache_sentinel' => [
// Set the Sentinel Host from Environment (optinal you can hardcode if want to use in prod only)
env('CACHE_REDIS_SENTINEL_1'),
env('CACHE_REDIS_SENTINEL_2'),
env('CACHE_REDIS_SENTINEL_3'),
'options' => [
'replication' => 'sentinel',
'service' => 'cachemaster'),
'parameters' => [
'password' => env('REDIS_PASSWORD', null),
'database' => 0,
],
],
],
],
if you needs to Redis sentinal cache, can create new cache connection to use the above sentinal connection like this:
'stores' = [
//Default config
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
// Custom cache connection(according to you)
'sentinel_redis' => [
'driver' => 'redis',
'connection' => 'cache_sentinel',
],
In laravel app, you can easily use via cache facade:
Cache::store('sentinel_redis')->get('key');
After config Redis properly test again with the clear server cache

how to override pusher config values?

I need to use different Pusher account for a specific part of my web app, so I tried to override the config using the following:
public function send(Request $request)
{
$general_pusher = config('broadcasting.connections.pusher');
$message = Message::create([
'from' => auth()->id(),
'to' => $request->contact_id,
'text' => $request->text
]);
\Config::set('broadcasting.connections.pusher', config('cfg.internal_chat_pusher'));
logger(config('broadcasting.connections.pusher'));
broadcast(new NewMessage($message));
\Config::set('broadcasting.connections.pusher', config('cfg.internal_chat_pusher'));
return response()->json($message);
}
then, I tried to test using a random value for which Laravel should raise an error, but no error and the messages get sent and received, but using the original/old pusher config values.
as you can see I used the logger instruction which give me the following:
[2020-09-19 14:06:09] local.DEBUG: array (
'driver' => 'pusher',
'key' => '1',
'secret' => '1',
'app_id' => '1',
'options' =>
array (
'cluster' => '1',
'useTLS' => true,
),
)
but, even though the logger command output the values that should give the error, I get no error, and messages get sent and received perfectly.
how to use different pusher account for a specific event?
Update:
I tried to edit the brodcasting.php as follow:
'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,
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
'internal_chat_pusher' => [
'driver' => 'pusher',
'key' => env('INTERNAL_CHAT_PUSHER_APP_KEY'),
'secret' => env('INTERNAL_CHAT_PUSHER_APP_SECRET'),
'app_id' => env('INTERNAL_CHAT_PUSHER_APP_ID'),
'options' => [
'cluster' => env('INTERNAL_CHAT_PUSHER_APP_CLUSTER', 'ap2'),
'useTLS' => true,
]
],
],
];
then in the send function inside the controller:
public function send(Request $request)
{
$message = Message::create([
'from' => auth()->id(),
'to' => $request->contact_id,
'text' => $request->text
]);
Broadcast::driver('internal_chat_pusher');
broadcast(new NewMessage($message));
return response()->json($message);
}
}
but still, the messages are sent using the original/old account configs.
Both of these options should work, but having a separate driver in your brodcasting.php is a cleaner way to go.
You've got the code right, but from the command line you must also remember to:
php artisan queue:restart
This will take the new code into account. Been burned by this a dozen times or more myself.

pusher can't listen callback events, it throw BroadcastException

I try to test pusher with my application , connection is setup succefuly but pusher can't listen my app events.
I am using laravel 7
broadcasting.php
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => 'ap2',
'encrypted' => true,
'useTLS' => true,
// 'curl_options' => [
// CURLOPT_SSL_VERIFYHOST => 0,
// CURLOPT_SSL_VERIFYPEER => 0,
// ],
],
],
.env file
APP_URL=http://localhost
DB_HOST=localhost
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=XXXXX
PUSHER_APP_KEY=XXXXX
PUSHER_APP_SECRET=XXXXX
PUSHER_APP_CLUSTER=ap2
EventClass
class LikeEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $id;
public $type;
public function __construct($id,$type)
{
$this->id = $id;
$this->type = $type;
}
public function broadcastOn()
{
//return new PrivateChannel('likeChannel'); //I try both
return new Channel('likeChannel');
}
public function broadcastAs()
{
return 'LikeEvent';
}
}
channels.php
Broadcast::channel('likeChannel', function () {
return true;
});
I try to call event in controller by
broadcast(new LikeEvent($reply->id,1))->toOthers();
//broadcast(new LikeEvent($reply->id,1));
//broadcast(new TestEvent("hello pusher"));
//event(new LikeEvent($reply->id,1));
it always throw BroadcastException
"message": "",
"exception": "Illuminate\\Broadcasting\\BroadcastException",
"file": "C:\\wamp64\\www\\rtforum.test\\vendor\\laravel\\framework\\src\\Illuminate\\Broadcasting\\Broadcasters\\PusherBroadcaster.php",
"line": 121,
I solve my problem by making encryption false
'options' => [
'cluster' => 'ap2',
'encrypted' => false,
]

How to test sendgrid email with mailtrap for testing?

In my Laravel 7.6 app I use sendgrid for email sending with code in control like :
\Mail
::to($newContactUs->author_email)->
send(new SendgridMail('emails/contact_us_was_sent', $newContactUs->author_email, '', $subject, $additiveVars, $attachFiles));
with class in app/Mail/SendgridMail.php :
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use Sichikawa\LaravelSendgridDriver\SendGrid;
use App\Settings;
use App\Http\Traits\funcsTrait;
class SendgridMail extends Mailable
{
use Queueable, SerializesModels;
use SendGrid;
use funcsTrait;
private $m_view_name;
private $m_to;
private $m_cc;
private $m_subject;
private $m_additiveVars;
private $m_attachFiles;
public function __construct( $view_name, $to= [], $cc= '', $subject= '', $additiveVars= [], $attachFiles= [] )
{
$this->m_view_name= $view_name;
$this->m_to= $to;
$this->m_cc= $cc;
$this->m_subject= $subject;
$all_emails_copy = \Config::get('app.all_emails_copy');
if ( empty($this->m_cc) and !empty($all_emails_copy)) {
$this->m_cc= $all_emails_copy;
}
$additiveVars['site_home_url'] = \URL::to('/');
$additiveVars['site_name'] = Settings::getValue('site_name');
$additiveVars['noreply_email'] = Settings::getValue('noreply_email');
$additiveVars['support_signature'] = Settings::getValue('support_signature');
$additiveVars['medium_slogan_img_url'] = config('app.url').config('app.medium_slogan_img_url');
$this->m_additiveVars= $additiveVars;
$this->m_attachFiles= $attachFiles;
}
/**
* Build the message.
*
* #return $this
*/
public function build( )
{
$mailObject= $this
->view( $this->m_view_name)
->subject($this->m_subject)
->to([$this->m_to])
->cc([$this->m_cc])
->with( $this->m_additiveVars )
->sendgrid( $this->m_additiveVars );
foreach( $this->m_attachFiles as $next_attach_file) {
if ( file_exists($next_attach_file) ) {
$mailObject->attach($next_attach_file);
}
}
return $mailObject;
}
}
and template resources/views/emails/contact_us_was_sent.blade.php:
...
<div class="wrapper">
#inject('viewFuncs', 'App\library\viewFuncs')
<h4 class="email_title">
Hello, {!! $to_user_name !!} !
</h4>
...
#include( 'emails.app_footer')
#include( 'emails.emails_style')
</div>
and it works for me now, but now with "Multiple Mail Drivers" I added mailtrap to my .env :
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=NNNNNNNN
MAIL_PASSWORD=NNNNNNNN
and I want to use mailtrap while testing the app using the same
template resources/views/emails/contact_us_was_sent.blade.php
and switching from mailtrap to sendgrid as easy as possible.
I tried something like :
\Mail::mailer('smtp')
->to($newContactUs->author_email)
->send( \Mail('emails/contact_us_was_sent', $newContactUs->author_email, '', $subject, $additiveVars, $attachFiles) );
But got error as \Mail does not support templates.
Are there something to use support templates for mail Method? Some wrapper?
Updated:
Priorly I worked with sendgrid and for this in file config/mail.php I
wrote all sendgrid parameters.
Now I want to write 2 emeil servers and fi=or this in .env I wrote:
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=mailtrip_id
MAIL_PASSWORD=mailtrip_password
SENDGRID_HOST=smtp.sendgrid.net
SENDGRID_PORT=587
SENDGRID_ENCRYPTION=tls
SENDGRID_USERNAME=sendgrid_user
SENDGRID_PASSWORD=sendgrid_user_password
and I remade config/mail.php (I got a sample from):
<?php
return [
'driver' => env('MAIL_DRIVER', 'smtp'),
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello#example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'sendmail' => '/usr/sbin/sendmail -bs',
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
'log_channel' => env('MAIL_LOG_CHANNEL'),
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST'),
'port' => env('MAIL_PORT'),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
],
'sendgrid' => [
'transport' => 'sendgrid',
'host' => env('SENDGRID_HOST', 'smtp.sendgrid.net'),
'port' => env('SENDGRID_PORT', 587),
'encryption' => env('SENDGRID_ENCRYPTION', 'tls'),
'username' => env('SENDGRID_USERNAME'),
'password' => env('SENDGRID_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
],
'ses' => [
'transport' => 'ses',
],
'mailgun' => [
'transport' => 'mailgun',
],
'postmark' => [
'transport' => 'postmark',
],
'sendmail' => [
'transport' => 'sendmail',
'path' => '/usr/sbin/sendmail -bs',
],
'log' => [
'transport' => 'log',
'channel' => env('MAIL_LOG_CHANNEL'),
],
'array' => [
'transport' => 'array',
],
],
];
I am not sure that this config file is valid? Are mail config parameters are read from mailers array ?
Looks like default(mailtrip) mail config is used always. Is it invalid format ?
In my control Ido:
$email_mode= 'live';
// $email_mode= 'debug';
if( $email_mode== 'debug' ) {
\Log::info( '-10 Send to mailtrap ::' );
\Mail
::mailer('smtp')
->to('myemail#yahoo.com') // DEBUG
->send(new TestEmail); //
\Log::info( '-10 Send to mailtrap AFTER::' );
}
// sendgrid
if( $email_mode== 'live') {
\Log::info( '-11 Send to sendgrid ::' );
\Mail
::mailer('sendgrid')
->to('myemail#yahoo.com') // DEBUG
->send(new SendgridMail('emails/contact_us_was_sent', $newContactUs->author_email, '', $subject, $additiveVars, $attachFiles));
\Log::info( '-11 Send to sendgrid AFTER::' );
}
I check in logs that live flow is run but anyway I got email at mailtrap.
Thanks!
The short answer is you are using \Mail() which is a native php function
see: https://www.php.net/manual/en/function.mail.php and try to embed that in a laravel Mailer.
This is not how you should use it, this is really not advised.
Some more detail:
As you write mailables, already you should consider, the Mailable should not define the driver, so it's not encouraged to name it SendGridMailA.. see it as MailA send with Mail::mailer($mailDriver)
See:https://laravel.com/docs/7.x/mail#writing-mailables
Long answer, see this video, explains how to implement multiple mail drivers:
https://www.youtube.com/watch?v=HCONO0cwsoI
However it looks like you using it as a debug method. This is not why multiple mail drivers where introduced in laravel 7. It's more like you should use a different driver for bulk mails for example and for password resets...
That makes sense..
For debuging, it more usefull to make the default driver, depend on the APP_DEBUG configuration for example, or introduce an own ENV var, to toggle your production app in debug mode...
I found decision with modifying config/mail.php :
<?php
return [
'default' => env('MAIL_MAILER', 'sendgrid'),
'mailers' => [
'mailtrap' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST'),
'port' => env('MAIL_PORT'),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
],
'sendgrid' => [
'transport' => 'smtp',
'host' => env('SENDGRID_HOST', 'smtp.sendgrid.net'),
'port' => env('SENDGRID_PORT', 587),
'encryption' => env('SENDGRID_ENCRYPTION', 'tls'),
'username' => env('SENDGRID_USERNAME'),
'password' => env('SENDGRID_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
],
and having 2 groups 'mailtrap' and 'sendgrid' in .env I wrote in control :
\Mail
::mailer(true ? 'sendgrid' : 'mailtrap')
->to($newContactUs->author_email)
->send(new SendgridMail(
'emails/contact_us_was_sent',
$newContactUs->author_email, '', $subject,
$additiveVars,
$attachFiles)
);

How to login to a different database and continue working with default one?

Imagine you have this inside .env file:
DB_CONNECTION=global
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=global
DB_USERNAME=dbuser
DB_PASSWORD=password
And you have one more connection inside config/database.php called tenant.
Now when I try to log in I switch the connection to tenant and I use the username and password from that database, I can log in, log out and all works fine.
But now I want to be able to log in with a user that doesn't exist in this tenant DB.
I started to overwrite the login() function like this:
Tip: remember that at this point the connection is set to tenant!
public function login(Request $request)
{
$parts = explode(':', $request['username']);
if ($parts[0] === 'global') {
\DB::purge(\DB::getDatabaseName());
\DB::reconnect('global');
\Schema::connection('global')->getConnection()->reconnect();
}
::::::::::::::::::
But this doesn't work... It doesn't switch the connection
I want to log in with a user from global DB and continue to work with tenant DB...
Is this even possible?
Update:
I already solved the part of how to switch the DB connection...
The problem is only authentication to a different one!
To avoid having to play with the default database connection, you should create two different model per database authenticatable user with each one its own $connection declared on the model
class UserGlobal extends User
{
protected $connection = 'global';
}
class UserTennant extends User
{
protected $connection = 'tennant';
}
You will need to do multi-authentification middlewares or use laravel's auth using this guide laravel simple multi-auth to have an idea.
// config/auth.php
<?php
[...]
'guards' => [
[...]
'global' => [
'driver' => 'session',
'provider' => 'globals',
],
'tennant' => [
'driver' => 'session',
'provider' => 'tennants',
],
],
[...]
'providers' => [
[...]
'globals' => [
'driver' => 'eloquent',
'model' => App\UserGlobal::class,
],
'tennants' => [
'driver' => 'eloquent',
'model' => App\UserTennant::class,
],
],
You need also to declare both database connection in config/database.php
'connections' => [
'global' => [
'driver' => 'mysql',
'host' => env('DB_HOST_GLOBAL', '127.0.0.1'),
...
],
'tennant' => [
'driver' => 'mysql',
'host' => env('DB_HOST_TENNANT', '127.0.0.1'),
...
],
]
What login would look like
public function login(Request $request)
{
$tennant= $this->loginGuard($request->get('email'), $request->get('password'), auth('tennant'));
$global= $this->loginGuard($request->get('email'), $request->get('password'), auth('global'));
if (!$tennant && !$global) {
return 'wrong credential';
}
return 'welcome';
}
private function loginGuard($email, $password, $guard)
{
$token = $guard->attempt(['email' => $email, 'password' => $password]);
if (!$token || !$guard->user()->isLoggingIn()) {
return null;
}
return $guard->user();
}

Resources