How retrieve Mailgun Delivered Message in Laravel - php

In my Node.js application, I followed the Mailgun docs https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-with-smtp-or-api for sending an email similar to the following:
mailgun.messages().send(data, (error, body) => {
console.log(body);
// body is {id: some_mailgun_built_id, message: 'Queued. Thank You'}
// which I store the body.id in my database
});
The issue I am faced with is how can I access that same Mailgun response when I send an email with Laravel? The Mailgun docs don't provide any examples showing how to retrieve that data.
This is how I am sending emails with Laravel:
\Mail::to($recipients)->send(
// this just renders my blade file which formats my email
new SendEmail($email);
);
// ?? How to get Message was sent object here
If anyone knows of any solution it would be greatly appreciated!

Hello and Welcome to SO!
Laravel has two events for the emails as explained in the official documentation: MessageSending and MessageSent
You can follow the events official documentation in order to listen for these specific events:
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'Illuminate\Mail\Events\MessageSending' => [
'My\Email\Listener',
],
'Illuminate\Mail\Events\MessageSent' => [
'My\Other\Listener',
],
];
You will receive as input the Swift_message which contains a header that is the Mailgun ID you're looking for. Let's have a look at the MailgunTransport#send source code in order to understand what's going on behind the scenes:
/**
* {#inheritdoc}
*/
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{
// [...]
$response = $this->client->request(
'POST',
"https://{$this->endpoint}/v3/{$this->domain}/messages.mime",
$this->payload($message, $to)
);
$message->getHeaders()->addTextHeader(
'X-Mailgun-Message-ID', $this->getMessageId($response) // <-- HERE LARAVEL SETS THE MESSAGE ID
);
// [...]
}
Looking for the same key in your listener you can recover the message ID that mailgun assigned to your e-mail. Unfortunately you can't access the entire Mailgun response but with the API you can easily retrieve your message.

Related

How to show an alert message to a user with Event Listener

I have created an Event called UserWalletNewTransaction.php and added this to it:
public $transaction;
public function __construct($transaction) {
$this->$transaction = $transaction;
}
Now in order to fire this event at the Controller, I coded this:
$newTransaction = UserWalletTransaction::create(['user_id' => $user_id, 'wallet_id' => $wallet_id, 'creator_id' => $creator_id, 'amount' => $amount_add_value, 'description' => $trans_desc]);
event(new UserWalletNewTransaction($newTransaction));
Then at the Listener, UserWalletNotification.php, I tried:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
}
So the scenario is, when Admins create a new Transaction for a custom user, a new alert message must be sent for him/her to let him/her know that new transaction was added for him/her.
But I don't really know how to do that.. So if you know, please let me know, I would really appreciate that...
Thanks in advance.
If by alert you mean showing a message on the web interface, use flash data.
https://laravel.com/docs/5.8/session#flash-data
$newTransaction = UserWalletTransaction::create(...);
event(new UserWalletNewTransaction($newTransaction));
$request->session()->flash('status', 'Transaction done.');
return view(...)
<span>{{ session('status') }}</span>
If you mean sending an email, just use the Mail facade in your listener to send a mailable.
https://laravel.com/docs/5.8/mail#sending-mail
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
Mail::to($user)->send(new TransactionDoneMail($event->transaction)); // TransactionDoneMail being your mailable class, made with "php artisan make:email TransactionDoneMail"
}
There are nice examples on how to build a mailable class in the documentation.
https://laravel.com/docs/5.8/mail#writing-mailables
There are many different things you can do in terms of "alerting" the customer.
One route would be to send an email or text message in your event listener. See https://laravel.com/docs/5.8/mail for help doing it via email.
Another way would be using browser push notifications. You could use OneSignal for this. You would setup the front end to display an alert to a customer user asking if they would like to subscribe to push notifications. When they subscribe, you will get back an ID for that specific user. Make an API call to your Laravel app, and store that ID in the users table (you will need a migration). Then from within your event listener, you can make a call to OneSignal's API and send the user a notification, which will popup on their computer.
Here is an example of using OneSignal to send an event to a user via the API:
Your OneSignal service:
<?php
namespace App\Services;
use App\User;
use GuzzleHttp\Client;
class OneSignalService
{
public function sendNotificationToUser(User $user, string $title, string $message, string $url, string $subtitle = null)
{
if (!$user->one_signal_id) {
return;
}
$fields = [
'app_id' => config('services.onesignal.app_id'),
'include_player_ids' => [$user->one_signal_id],
'headings' => ['en' => $title],
'contents' => ['en' => $message],
'url' => $url,
];
if ($subtitle) {
$fields['subtitle'] = ['en' => $subtitle];
}
$client = new Client([
'base_uri' => 'https://onesignal.com/api/v1/',
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => 'Basic <<API_KEY>>',
]
]);
$client->request('POST', 'notifications', [
'json' => $fields
])
}
}
UserWalletNotification:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
$oneSignal = new OneSignalService();
$oneSignal->sendNotificationToUser($user, 'New Transaction', 'You have a new transaction', 'yourwebsite.com');
}
The way I would go about this would be via broadcasting, which would use websockets to instantly send the customer user an alert to their browser, in which you could then display a popup of some sort. You could install Laravel Echo Server, but to keep things simple you can use Pusher. Follow the guide to install on the front end of your website.
Then, create a private channel specific to a customer user "transaction.created.{{USER ID}}" and listen for it on your front end.
Within Laravel you will install the PHP Pusher SDK via composer.
Then within your .env file set:
BROADCAST_DRIVER=pusher
Next, open up channels.php within your routes directory in Laravel and add:
Broadcast::channel('transaction.created.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
This will verify authentication for your user to the private channel.
Create an Laravel Event:
<?php
namespace App\Events;
use App\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TransactionCreated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user = null;
public $transaction = null;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $user, UserWalletTransaction $transaction)
{
$this->user = $user;
$this->transaction = $transaction;
}
public function broadcastWith(): array
{
return $this->transaction->toArray(); //Or whatever information you want to send to the front end
}
public function broadcastAs(): string
{
return 'TransactionCreated';
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('transaction.created.' . $this->user->id);
}
}
Fire the event from UserWalletNotification:
public function handle(UserWalletNewTransaction $event) {
$uid = $event->transaction->user_id;
$user = User::find($uid);
// now sends alert message to the user
event(new TransactionCreated($user, $event->transaction));
}
Lastly, create some sort of popup and display it on the front end when your callback function for the private channel is hit.
If you need anymore help, feel free to comment.
What you want to do I believe, is asynchronous notifications.
Well, if you really mean flash messages - those who are stored in session - it will not be so easy.
Normal steps are create flash message for the user currently logged in on a website, stored in session that is unique for the current user. It can be shown only for this user.
What you want is to create flash message as the admin (from admin perspective) , then only to admin it can be shown.
I would do this, create new table, when these notification messages will be stored. Some table with columns like id, user_id, message, type, created_date, shown_date. Admins will put alert/notification messages for each user. Then create class (can be in controller for example) that will check this table for each user and if there is new not already shown message, show it normally in flash message for that current user. Dont forget to mark that message as shown. That is it.
So much for custom solution. I belive there must be some for example jQuery/other Jvascript plugins or Laravel plugins for asynchronous notifications, please check those.

Laravel - Sending customized Bugsnag error reports based on exception type?

I have a Laravel 7 app that uses a 3rd party PHP package to integrate with a service (Convirza). I bootstrapped and bound the service into the container via my own service provider App\Providers\ConvirzaServiceProvider. My problem is that I need to send error logs from the service to Bugsnag (error reporting service)... and depending on the Exception thrown from the service, I need to add metadata to the report. I do NOT want to filter exceptions from the base App\Exceptions\Handler class because I want everything to be wrapped into the Service Provider.
For example, when the Skidaatl\Convirza\Exceptions\BadRequestException gets thrown, I want to add the request parameters as metadata to the report. Another example would be if the Skidaatl\Convirza\Exceptions\InvalidTokenException gets thrown, I want to add the token as metadata to my report.
I would rather not add a try/catch block every time I call the service. How would I go about this?
Here is my Service Provider:
class ConvirzaServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->commands([
Commands\UpdateCampaignsCommand::class,
Commands\UpdateGroupsCommand::class,
Commands\GenerateReportsCommand::class
]);
}
$this->app['events']->subscribe(EventHandler::class);
}
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->loadRoutesFrom(__DIR__.'/routes.php');
$this->loadMigrationsFrom(__DIR__.'/database/migrations');
$this->app->singleton('convirza', function(Container $app) {
$config = config('convirza.client');
$config = Config::create($config);
return new Convirza($config);
});
$this->app->bind(ReportBuilder::class, function(Container $app) {
return new ReportBuilder($app->get('convirza'));
});
$this->app->alias('convirza', Convirza::class);
}
}
I suggest using Bugsnag's registerCallback to inspect the report and add metadata based on specific parameters of the report.
For example, you can get the context of your exception and even the stacktrace information. Additionally, you can access the originalError to get information including the error message. Using that, you can create logic in a callback to add metadata.
E.g.
public function boot()
{
Bugsnag::registerCallback(function ($report) {
$stacktrace = $report->getStacktrace();
$frames = &$stacktrace->getFrames();
// Adds metadata for `log_error` frame if present
if ($frames[0]['method'] == 'log_error') {
$report->setMetaData([
'account' => [
'token' => '12345',
]
}
]);
}

Laravel Broadcasting, how to auth to a presence channel?

I'm trying to join a presence channel (Public channels work well), but I can't get this to work:
Vue code:
mounted(){
Echo.join('game.' + "0").here((users) => {
alert("In the channel!");
})
.joining((user) => {
console.log("Someone entered");
})
.leaving((user) => {
console.log(user.name);
})
.listen('GameEvent', (e) => {
console.log("Hey")
});
Echo.channel('NewSentence')
.listen('NewSentence',(sentence)=>{
alert("HOLA");
});
}
I'm trying to join the channel "game.0". As I'm using Laravel Passport I need to authenticate myself with a token, and that is working. Sending the auth request for Laravel Echo returns a key, but the JavaScript events are not triggering .here(), .listening() ....
BroadcastService provider boot function:
public function boot() {
Broadcast::routes(["middleware" => "auth:api"]);
require base_path('routes/channels.php');
}
channels.php
Broadcast::channel('game.0', function ($user,$id) {
return ['id' => $user->id];
});
The auth route:
Route::post('/broadcasting/auth', function(Request $request){
$pusher = new Pusher\Pusher(
env('PUSHER_APP_KEY'),
env('PUSHER_APP_SECRET'),
env('PUSHER_APP_ID'),
array(
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => false,
'host' => '127.0.0.1',
'port' => 6001,
'scheme' => 'http',
)
);
return $pusher->socket_auth($request->request->get('channel_name'),$request->request->get('socket_id'));
});
Do I need to do something extra to make it work? This is the auth request:
EDIT:
GameEvent event:
class GameEvent implements ShouldBroadcastNow {
use Dispatchable, InteractsWithSockets, SerializesModels;
public $gameEvent;
public $gameId;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($gameEvent, $gameId) {
//
$this->gameEvent = $gameEvent;
$this->gameId = $gameId;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\PresenceChannel|array
*/
public function broadcastOn() {
//return new PrivateChannel('channel-name');
return new PresenceChannel('game.0');
}
public function broadcastWith() {
return $this->gameEvent;
}
}
EDIT:
I've hardcoded the names: 'game.0' is now hardcoded in the routes/channels.php route, in the Echo connection and in the GameEvent. I also removed broadcastAs(). After entering the laravel-websockets debugging dashboard I found that the channel I want to subscribe doesn't even appear. It looks like it won't start a connection, but I can't figure out what it going on.
I hardcoded the
The problem here seems to be that the Echo is not listening on proper channel. First of all the Echo.join is using channel game.0 in which 0 is a user's id, and i don't think that there is actually a user with id 0. Secondly, you are broadcasting as
GameEvent
and Echo is connecting to channel named game.{id} I suggest that you either remove the broadcastAs() function from your event file or listen on GameEvent. Also use the websocket dashboard for testing this. The dashboard will be available at
/laravel-websockets
route automatically, which is available only for local environment so make sure that environment is local in your .env.
Use the debugging dashboard provided by laravel-websockets to send data to channels, first connect to your web socket within the dashboard then just enter the channel name, event name and data in JSON format and hit send on the dashboard.
Try finding out if that helps with resolving your problem.
I also recommend thoroughly reading laravel's official documentation on broadcasting as well as laravel-websockets debugging dashboard guide.
Also update what you got in result to this question.

laravel and react pusher setup

I am working on a web project.
I have laravel as my back-end reactjs as my front-end
I have users, posts comments in my DB.
I wanted to implement pusher so i can have this real time posts showing up once any user posted a new post.
I am very close to achieve this behavior.
on laravel
i have this event
class NewPostsCast implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $post;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($post)
{
$this->post = $post;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('posts');
}
}
and once the post stored to my database I trigger this event
event(new NewPostsCast($output));
on react
let channel = pusher.subscribe("posts");
// i guess error is here because this function never called
// it's only called at startup
channel.bind("NewPostsCast", post => {
console.log("pusher::: ", post);
});
might be useful
from pusher logs
Pusher : No callbacks on posts for App\Events\NewPostsCast
however pusher returns this log also
Pusher : Event recd : {"event":"App\\Events\\NewPostsCast","channel":"posts","data":{"post":{"id":19,"title":"sd","body":"eksdl","tags":"[\"ds\",\"dsf\"]","user_id":1,"created_at":"2019-04-18 14:22:00","updated_at":"2019-04-18 14:22:00","votes":0,"user":{"id":1,"name":"user1","username":"user1","email":"user1#health.io","address":null,"state":null,"country":null,"gender":"female","phone":null,"avatar":"http://healthqo.api/public/profile_pics/default/female.png","created_at":"2019-04-09 08:30:12","updated_at":"2019-04-09 08:30:12"}}}}
in case anyone is still struggling with the same problem
from pusher logs it says (No Callbacks on posts for App\Events\NewPostsCast)
that quite solves the problem
since react and laravel are separated in my project (NewPostsCast) is not defined
so i had to change first parameter of the bind function
like so:
FROM
let channel = pusher.subscribe("posts");
channel.bind("NewPostsCast", post => {
console.log("pusher::: ", post);
});
TO
let channel = pusher.subscribe("posts");
channel.bind("App\\Events\\NewPostsCast", post => {
console.log("pusher::: ", post);
});
not sure why double double back slashes but it gives me a syntax error with single back slash

Laravel listener only sends when I dd a variable in the class being called

I have a custom class in Laravel that tracks the analytics of my app through Segment (using this package for php: https://github.com/AltThree/Segment).
Here is a snippet of my class and a function I am calling through my listener to track a login:
class Tracking {
private function segmentTrack(User $user, string $event, array $properties = null) {
$segment = Segment::track([
"userId" => $user->id,
"event" => $event,
"properties" => $properties
]);
dd($segment);
}
/**
* Handle Login tracking
*
* #param User $user
* #return void
*/
public function login (User $user) {
$this->segmentTrack($user, "Login");
}
}
Notice the dd in the segmentTrack function. When I run the Laravel queue and I then trigger the Tracking->login() event through my app, the listener goes off fine and with the dd function, it will send that data to Segment and I can see it in their live debugger, all is well.
However, when I remove that dd, and the listener goes off and shows as successful - the data is never seen in Segment.
Can someone tell me what i'm missing? This is my first time using the Laravel queue system so a little confused why it might not be working.
For queued jobs, use:
Segment::track($payload);
Segment::flush();

Categories