So i have a section of code that creates a random code and sends it in a private message (on another website) to them, however id like to update it to also display the code in the success message
Models
public function createDepositRequest()
{
// Generate unique code
$code = Codes::generateCode();
$deposit = Deposits::create([
"accountID" => $this->id,
"code" => $code,
]);
echo $code;
dispatch(new CreateDepositRequest($deposit));
}
UserController
/**
* Creates a deposit request for an account
*
* #param int $accountID
* #throws UserErrorException
*/
protected function deposit(int $accountID)
{
// Get the account
$account = Accounts::find($accountID);
if ($account === null || $account->count() === 0)
throw new UserErrorException("That account doesn't exist");
// Verify that the submitting user owns this account
if (Auth::user()->nID != $account->nID)
throw new UserErrorException("You don't own that account, silly billy");
// Now call to create the deposit request
$account->createDepositRequest();
$this->output->addSuccess("Deposit request added successfully. Please check your inbox in-game for instructions");
}
Job (the thing that has it sent to the other site) ```
namespace App\Jobs;
use App\Classes\Nation;
use App\Classes\PWClient;
use App\Models\Accounts;
use App\Models\Deposits;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class CreateDepositRequest implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
/**
* Holds the deposit request
*
* #var Deposits
*/
protected $deposit;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(Deposits $deposit)
{
$this->deposit = $deposit;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$client = new PWClient();
$client->login();
// Get nation info
$nation = new Nation($this->deposit->account->nID);
// Setup message
$message = "Hi {$nation->leader},\n\n You've submitted a deposit request for the account named: {$this->deposit->account->name}.\n\nThe code for your deposit is: {$this->deposit->code}\n\nPlease send whatever money and resources you want to deposit into your account into the bank using the code above as the transaction, just like how you make a payment towards a loan.\n\nPlease note that the system checks for deposits at five past the hour, so if you make a deposit during that time, your deposit [b]will not count[/b]. So please try to avoid that. Additionally, this means that your deposit will not show up in your account until then. You will receive a confirmation email when your deposit is processed. If you not get a message within two hours, please contact us.";
// Now send them a message
$client->sendMessage($nation->leader, "Deposit Request", $message);
echo "{$this->deposit->code}";
}
}
So ive tried a dozen things but im reaching a level where im above my knowlage it would appear. You can see that in the models i have it just echo the code, and since the user controller actually sends the success message, im not sure how to return the code generated in models for use in the usercontroller.
So to clarify i made a screenshot https://prnt.sc/r42p0v
Attempts tried: to add
-return $code; error was "undefined variable"
I can't say absolutely for sure without seeing more of the code, but it looks like you should be able to do this...
In your model, after
dispatch(new CreateDepositRequest($deposit));
add
return $code;
Then in your controller, replace
$account->createDepositRequest();
with
$code = $account->createDepositRequest();
Having done that, you should be able to access the returned $code value in the next line as something like
$this->output->addSuccess("Deposit request added successfully. Your code is $code. Please check your inbox in-game for instructions");
The only thing that would throw a wrench in this would be if there's something going on in dispatch(new CreateDepositRequest($deposit)); that isn't fully shown in your code above.
Related
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.
Does anyone know of a way to throttle email notifications for a certain Exception in Laravel?
I made my app send me an email when there is a DB error by check for the QueryException. This is a rough example of what I did in Exception handler in Laravel:
class Handler{
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* #param \Exception $exception
* #return void
*/
public function report(Exception $e)
{
if($e instanceof QueryException){
if( App::environment(['production']) ){
Notification::route('mail', 'myemail#test.com')
->notify(new DbErrorNotification($e));
}
}
parent::report($e);
}
}
Short of tracking in DB, is there a way I could throttle DB errors by exception type so that I don't end up getting thousands of emails if there is a consistent DB error.
I looked at Swift Mailer's anti-flood and throttling plugins but those affect the system globally which I don't want to do.
Thank you in advance
There is a few way how you can achieve that. Right before dispatching a job you can add delay on it. Example:
use App\Http\Request;
use App\Jobs\SendEmail;
use App\Mail\VerifyEmail;
use Carbon\Carbon;
/**
* Store a newly created resource in storage.
*
* #param Request $request
* #return \Illuminate\Http\RedirectResponse
* #throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function store(Request $request)
{
$baseDelay = json_encode(now());
$getDelay = json_encode(
cache('jobs.' . SendEmail::class, $baseDelay)
);
$setDelay = Carbon::parse(
$getDelay->date
)->addSeconds(10);
cache([
'jobs.' . SendEmail::class => json_encode($setDelay)
], 5);
SendEmail::dispatch($user, new VerifyEmail($user))
->delay($setDelayTime);
}
Or if you don't like the idea about a job you can also delay it via Mail. Example:
Mail::to($user)->later($setDelayTime);
And finally via Redis Rate Limiting. Example:
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Redis;
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
Redis::throttle('SendEmail')
->allow(1)
->every(10)
->then(function () {
Mail::to($this->user)->send($this->mail);
}, function () {
return $this->release(10);
});
}
Allowing one email to be sent every ten seconds. The string SendEmail passed to the throttle() method is a name that uniquely identifies the type of job being rate-limited. You can set this to whatever you want.
The release() method is an inherited member of your job class and
instructs Laravel to release the job back onto the queue, with an
optional delay in seconds, in the event a lock cannot be obtained.
When the job is dispatched to the queue, Redis is instructed to only
run one SendEmail job every ten seconds.
Keep in mind that for all of this you need a Redis
Source: https://medium.com/#bastones/a-simple-guide-to-queuing-mail-in-laravel-f4ff94cdaa59
Currently the logic behind Resetting Password is that user must provide valid/registered e-mail to receive password recovery e-mail.
In my case I don't want to validate if the e-mail is registered or not due to security concerns and I want to just do the check in back-end and tell user that "If he has provided registered e-mail, he should get recovery e-mail shortly".
What I've done to achieve this is edited in vendor\laravel\framework\src\Illuminate\Auth\Passwords\PasswordBroker.php sendResetLink() method from this:
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
return static::RESET_LINK_SENT;
}
to this:
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
// if (is_null($user)) {
// return static::INVALID_USER;
// }
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
if(!is_null($user)) {
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
}
return static::RESET_LINK_SENT;
}
This hard-coded option is not the best solution because it will disappear after update.. so I would like to know how can I extend or implement this change within the project scope within App folder to preserve this change at all times?
P.S. I've tried solution mentioned here: Laravel 5.3 Password Broker Customization but it didn't work.. also directory tree differs and I couldn't understand where to put new PasswordBroker.php file.
Thanks in advance!
Here are the steps you need to follow.
Create a new custom PasswordResetsServiceProvider. I have a folder (namespace) called Extensions where I'll place this file:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as BasePasswordResetServiceProvider;
class PasswordResetServiceProvider extends BasePasswordResetServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerPasswordBroker();
}
/**
* Register the password broker instance.
*
* #return void
*/
protected function registerPasswordBroker()
{
$this->app->singleton('auth.password', function ($app) {
return new PasswordBrokerManager($app);
});
$this->app->bind('auth.password.broker', function ($app) {
return $app->make('auth.password')->broker();
});
}
}
As you can see this provider extends the base password reset provider. The only thing that changes is that we are returning a custom PasswordBrokerManager from the registerPasswordBroker method. Let's create a custom Broker manager in the same namespace:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordBrokerManager as BasePasswordBrokerManager;
class PasswordBrokerManager extends BasePasswordBrokerManager
{
/**
* Resolve the given broker.
*
* #param string $name
* #return \Illuminate\Contracts\Auth\PasswordBroker
*
* #throws \InvalidArgumentException
*/
protected function resolve($name)
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException(
"Password resetter [{$name}] is not defined."
);
}
// The password broker uses a token repository to validate tokens and send user
// password e-mails, as well as validating that password reset process as an
// aggregate service of sorts providing a convenient interface for resets.
return new PasswordBroker(
$this->createTokenRepository($config),
$this->app['auth']->createUserProvider($config['provider'] ?? null)
);
}
}
Again, this PasswordBrokerManager extends the base manager from laravel. The only difference here is the new resolve method which returns a new and custom PasswordBroker from the same namespace. So the last file we'll create a custom PasswordBroker in the same namespace:
<?php
namespace App\Extensions\Passwords;
use Illuminate\Auth\Passwords\PasswordBroker as BasePasswordBroker;
class PasswordBroker extends BasePasswordBroker
{
/**
* Send a password reset link to a user.
*
* #param array $credentials
* #return string
*/
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
// if (is_null($user)) {
// return static::INVALID_USER;
// }
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
if(!is_null($user)) {
$user->sendPasswordResetNotification(
$this->tokens->create($user)
);
}
return static::RESET_LINK_SENT;
}
}
As you can see we extend the default PasswordBroker class from Laravel and only override the method we need to override.
The final step is to simply replace the Laravel Default PasswordReset broker with ours. In the config/app.php file, change the line that registers the provider as such:
'providers' => [
...
// Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
App\Extensions\Passwords\PasswordResetServiceProvider::class,
...
]
That's all you need to register a custom password broker. Hope that helps.
The easiest solution here would be to place your customised code in app\Http\Controllers\Auth\ForgotPasswordController - this is the controller that pulls in the SendsPasswordResetEmails trait.
Your method overrides the one provided by that trait, so it will be called instead of the one in the trait. You could override the whole sendResetLinkEmail method with your code to always return the same response regardless of success.
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$response = $this->broker()->sendResetLink(
$request->only('email')
);
return back()->with('status', "If you've provided registered e-mail, you should get recovery e-mail shortly.");
}
You can just override the sendResetLinkFailedResponse method in your ForgetPasswordController class.
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
}
We'll just send the successful response even if the validation failed.
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();
I just created a newsletter in Laravel, where people can register with their email-adress and then somebody can send a mail to those who are registered.
Then I save those registrations in a table, together with the relating id of the sender of the newsletter (in my case its a seller, and buyers register for a newsletter)....
This is the code I use then so send a newsletter:
public function sendNewsletter(Request $request){
$newsletterText = $request->newsletterText;
$seller = Auth::user();
$sellerID = $seller->id;
$newsletterMailAdresses = Newsletter::where('seller_id', $sellerID)->pluck('mailAdress');
Mail::bcc($newsletterMailAdresses)->send(new newsletterMail($newsletterText));
return "<p style='color:green;'>Newsletter erfolgreich versandt</p>";
}
Its called with an AJAX request, but shouldn't change anything...
What I need now is to give the users the possibility to unsubscribe from the newsletter as well, via a link in the mail.
This is the corresponding mail class:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class newsletterMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public $newsletterText;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($newsletterText)
{
$this->newsletterText = $newsletterText;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$subject = "Newsletter ShoppingPortal";
return $this->view('emails.newsLetter')->subject($subject);
}
}
And that's the mail view:
Newsletter vom Shoppingportal: <br/><br/>
<div>
{!!$newsletterText!!}
</div>
<br/>
To unsubscribe from Newsletter, use this link: localhost/unsubscribe/....
And here's the thing missing, I need to create a link to unsubscribe, therefore I would need the sellerID in the view (what would be possible, could just pass it in like the newsletterText, but HOW can I pass in or get the current mail adress the email is sent to? As this mail goes to some recipents, its different in each instance of the view... How can I do this, so how can I get the mail adress in the view?
I got it,
just changed the sending of the mail to this:
foreach($newsletterMailAdresses as $mailAdress){
Mail::to($mailAdress)->send(new NewsletterMail($newsletterText, $mailAdress, $sellerID));
}
Now I have the current mail adress while sending.