I have a Notification class with three Channels ('mail','sms','notification'),
I'm also Implements ShouldQueue for queuing Notification.
The problem is that i want to use queue only mail or only for two of them,in default queuing system sets on all of Channels.
What should i do for this?
Here is my Notification code:
class DocumentSendNotification extends Notification implements ShouldQueue
{
use Queueable;
protected $document;
public function __construct($document)
{
$this->document = $document;
}
public function via($notifiable)
{
return ['database','mail', SmsChannel::class];
}
public function toMail($notifiable)
{
//something
}
public function toArray($notifiable)
{
//somecode
}
public function toSms($notifiable)
{
//somecode
}
And here is the code that i call Notification class with delay:
Notification::send($users, (new DocumentSendNotification($event->document))->delay(Carbon::now()->addSeconds(30)));
Thanks in advance
Add this to your listener class:
public $queue = 'YOUR_QUEUE_NAME';
public function queue(QueueManager $handler, $method, $arguments)
{
$handler->push($method, $arguments, $this->queue);
}
if you know which method you want to broadcast do this:
in this example I want only to use database
public function via($notifiable)
{
return ['database'];
}
if you don't know which broadcast you should use (I mean it's dynamic and defined in the program and it is optional for the user or something else) do this :
public function via($notifiable)
{
$broadcasts = [];
collect($userNotificationWay)->each(function ($item, $key) use (&$broadcasts) {
$item && $key == 'get_email' ? array_push($broadcasts, 'mail' ) : null;
$item && $key == 'get_sms' ? array_push($broadcasts, SmsChannel::class) : null;
});
return $broadcasts;
}
if one of condition of above code solved to true the broadcast set for it, and call the method to send,
Hope will help
Related
Laravel Version: 8.78.1
PHP Version: 8.0.10
I've created a custom command to run on a schedule and email a notification.
My Command class handle method:
public function handle()
{
$sql = "SELECT * FROM Licences WHERE (Expired = 1)";
$list = DB::select($sql);
return (new NotifyExpiredLicences($list))->toMail('me#gmail.com');
}
My notification method:
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Clients with Expired Licences')
->markdown('vendor/notifications/expiredlicences',
['clients' => $this->list, 'toname' => 'Me']);
}
Whenever I test this by running it manually with php artisan email:expired-licences I get the following error Object of class Illuminate\Notifications\Messages\MailMessage could not be converted to int from my command class in the handle method.
However, the preview of my email works fine & displays as expected:
Route::get('/notification', function () {
return (new SendExpiredLicences())->handle();
});
If I remove the return statement from my handle() method, then although I get no errors, neither in my console or in storage\logs, also the preview stops working.
At this point I'm sure I've missed something important from the way this is supposed to be done, but after going through the Laravel docs and looking at online tutorials/examples, I've no idea what.
I've got everything working - though not entirely sure it's the "Laravel way".
If anyone's got suggestions for improving it - add a comment or new answer and I'll try it out.
Console\Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('email:expired-licences')
->weekdays()
->at('08:30');
}
App\Console\Commands\SendExpiredLicences.php:
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences';
protected $description = 'Email a list of expired licences to Admin';
private $mail;
public function _construct()
{
$clients = DB::select("[Insert SQL here]");
$this->mail = (new NotifyExpiredLicences($clients))->toMail('admin#example.com');
parent::__construct();
}
public function handle()
{
Mail::to('admin#example.com')->send($this->mail);
return 0;
}
public function preview()
{
return $this->mail;
}
}
App\Notifications\NotifyExpiredLicences.php:
class NotifyExpiredLicences extends Notification
{
public function __construct(protected $clients)
{
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new Mailable($this->clients));
}
}
App\Mail\ExpiredLicences.php:
class ExpiredLicences extends Mailable
{
public function __construct(private $clients)
{
}
public function build()
{
return $this
->subject('Clients with Expired Licences')
->markdown('emails/expiredlicences',
['clients' => $this->clients, 'toname' => 'Admin']);
}
}
resources\views\emails\expiredlicences.blade.php:
#component('mail::message')
# Hi {!! $toname !!},
#component('mail::table')
| Client | Expired |
| ------------- | --------:|
#foreach ($clients as $client)
|{!! $client->CompanyName !!} | {!! $client->Expired !!}|
#endforeach
#endcomponent
<hr />
Thanks, {!! config('app.name') !!}
#endcomponent
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
return (new SendExpiredLicences())->preview();
});
Ok just to save more commenting, here's what I'd recommend doing. This is all based on the Laravel docs, but there are multiple ways of doing it, including what you've used above. I don't really think of them as "right and wrong," more "common and uncommon."
Console\Kernel.php: I'd keep this mostly as-is, but pass the email to the command from a config file, rather than having it fixed in the command.
use App\Console\Commands\SendExpiredLicences;
…
protected function schedule(Schedule $schedule)
{
$recipient = config('myapp.expired.recipient');
$schedule->command(SendExpiredLicences::class, [$recipient])
->weekdays()
->at('08:30');
}
config/myapp.php:
<?php
return [
'expired' => [
'recipient' => 'admin#example.com',
],
];
App\Console\Commands\SendExpiredLicences.php: update the command to accept the email address as an argument, use on-demand notifications, and get rid of preview() method. Neither the command or the notification need to know about the client list, so don't build it yet.
<?php
namespace App\Console\Commands;
use App\Console\Command;
use App\Notifications\NotifyExpiredLicences;
use Illuminate\Support\Facade\Notification;
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences {recipient}';
protected $description = 'Email a list of expired licences to the given address';
public function handle()
{
$recip = $this->argument('recipient');
Notification::route('email', $recip)->notify(new NotifyExpiredLicences());
}
}
App\Notifications\NotifyExpiredLicences.php: the toMail() method should pass the notifiable object (i.e. the user getting notified) along, because the mailable will be responsible for adding the To address before the thing is sent.
<?php
namespace App\Notifications;
use App\Mail\ExpiredLicenses;
use Illuminate\Notifications\Notification;
class NotifyExpiredLicences extends Notification
{
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new ExpiredLicenses($notifiable));
}
}
App\Mail\ExpiredLicences.php: since the mail message actually needs the list of clients, this is where we build it. We get the recipient here, either from the user's email or the anonymous object.
<?php
namespace App\Mail;
use App\Models\Client;
use Illuminate\Notifications\AnonymousNotifiable;
class ExpiredLicences extends Mailable
{
private $email;
public function __construct(private $notifiable)
{
// this allows the notification to be sent to normal users
// not just on-demand
$this->email = $notifiable instanceof AnonymousNotifiable
? $notifiable->routeNotificationFor('mail')
: $notifiable->email;
}
public function build()
{
// or whatever your object is
$clients = Client::whereHas('licenses', fn($q)=>$q->whereExpired(1));
return $this
->subject('Clients with Expired Licences')
->markdown(
'emails.expiredlicences',
['clients' => $clients, 'toname' => $this->notifiable->name ?? 'Admin']
)
->to($this->email);
}
}
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
// create a dummy AnonymousNotifiable object for preview
$anon = Notification::route('email', 'no#example.com');
return (new ExpiredLicencesNotification())
->toMail($anon);
});
I am getting the following error when trying to pass data to a markdown mailable
Undefined variable: claim
I have the following code for a notification
class ChequeDiscrepancy extends Notification
{
use Queueable;
public $data;
public function __construct($data)
{
$this->data = $data;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Subject')
->markdown('emails.admin.banking.cheque_discrepancy');
}
public function toArray($notifiable)
{
return [
//
];
}
}
In my markdown template, I have
#component('mail::message')
# Cheque No: {{$data->id}}
#endcomponent
According to https://laravel.com/docs/8.x/mail#view-data I should be able to pass the data via public properties or via The with Method. This works for regular mail but doesn't seem to work for notifications.
Am I doing something wrong?
Thanks
Passing data is different between Notifications & Mailable classes.
In notifications you need to pass the data explicitly, like this
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Subject')
->markdown('emails.admin.banking.cheque_discrepancy', ['data'=>$this->data]);
}
You may read further here : Laravel Notifications
I'm working with this case where I need to instantiate an object after a form is submitted in a controller. Everything's working fine until I call this object (as a property) from another method. It appears to be null.
If I intentiate the object from constructor method, I have no problem at all.
I can't keep this object in session because of closure.
Here's what i got so far.
// Version with the object iniate within the constructor that's working
class SearchConsoleController extends Controller
{
private $console;
protected function __construct() {
$callback = route('searchconsole.callback') ;
$this->console = $this->setConsole(env('CLIENT_ID'), env('CLIENT_SECRET'), $callback);
}
private function setConsole($cliendId, $cliendSecret, $callback){
$console = new Console(new Google_Client(), $cliendId, $cliendSecret, $callback);
return $console;
}
public function index(Request $request) {
return view('searchconsole.index')->with('authUrl', $this->console->getAuthUrl());
}
public function callback(Request $request){
if ($request->has('code')) {
$this->console->acceptCode($request->get('code'));
return redirect()->action('SearchConsoleController#listSites', [$request]);
}
else{
die('error');
}
}
Now the version which i'm stucked wih
class SearchConsoleController extends Controller
{
private $console;
private $callback;
protected function __construct() {
$this->callback = route('searchconsole.callback') ;
}
private function setConsole($cliendId, $cliendSecret, $callback){
$console = new Console(new Google_Client(), $cliendId, $cliendSecret, $this->callback);
return $console;
}
public function index(Request $request) {
// VIEW WITH A FORM FROM WHICH I GET CLIENT_SECRET & CLIENT_ID var
return view('searchconsole.index');
}
public function getAuthUrl(Request $request) {
// FORM FROM INDEX IS SUBMITTED
$clientId = ($request->has('google-client-id')) ?
$request->get('google-client-id') :
null
;
$clientSecret = ($request->has('google-client-secret')) ?
$request->get('google-client-secret') :
null
;
$this->console = $this->setConsole($clientId, $clientSecret, $this->callback);
return $this->console->getAuthUrl();
}
public function callback(Request $request){
if ($request->has('code')) {
// ***** MY PROBLEM *********
$this->console->acceptCode($request->get('code')); // HERE $this->console IS NULL;
// *******************
return redirect()->action('SearchConsoleController#listSites', [$request]);
}
else{
die('error');
}
}
I just can't figure out how I can do this so console is still available
UPDATE :
following #iamab.in advice, i looked into Service Provider but i just dont know how i can instante the Console Object within the service provider.
Here's what i've done.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Helpers\Console;
use Google_Client;
use Illuminate\Support\Facades\Route;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(Console::class, function() {
$request = app(\Illuminate\Http\Request::class);
$clientId = ($request->has('google-client-id')) ?
$request->get('google-client-id') :
null
;
$clientSecret = ($request->has('google-client-secret')) ?
$request->get('google-client-secret') :
null
;
$callback = Route::get()->name('searchconsole.callback');
return new Console(new Google_Client(), $clientId, $clientSecret, $callback);
});
}
public function boot(){}
....
I just dont know how and where to implement it.
Thanks again
Update#2 :
okay my solution was working, I just didnt launch the correct app ..... 😅
I've been using backpack in Laravel but I want to replace action-domain-responder architecture with MVC.So I've created an Action which my route refers like below:
Route::get('post',[
'as' => 'post.index',
'uses' => 'Core\Post\Actions\ApiGetListOfPostsAction',
'operation' => 'list'
]);
class ApiGetListOfPostsAction extends BaseAction implements IAction
{
private $service;
public function __construct(ApiGetListOfPostsService $service)
{
$this->service = $service;
}
public function __invoke(Request $request): mixed
{
$data = $this->service->process();
return response()->json($data);
}
}
and my service has this code:
class ApiGetListOfPostsService extends CrudController
{
use ListOperation, CreateOperation, DeleteOperation, UpdateOperation;
public function setup()
{
CRUD::setModel(\App\Models\Post::class);
CRUD::setRoute(config('backpack.base.route_prefix') . '/post');
CRUD::setEntityNameStrings('post', 'posts');
}
protected function setupListOperation()
{
CRUD::column('title');
CRUD::column('content');
}
public function process()
{
return $this->index();
}
}
I've extended CrudController in my service class but I've got this error:
Call to a member function hasAccessOrFail() on null
which related to the ListOperation Trait and this code:
public function index()
{
$this->crud->hasAccessOrFail('list');
}
I need to send all requests to the Service class. How can I pass requests to the service class?
When I deleted middleware from CrudController I have no problem.
$this->middleware(function ($request, $next) {
$this->crud = app()->make('crud');
$this->crud->setRequest($request);
$this->setupDefaults();
$this->setup();
$this->setupConfigurationForCurrentOperation();
return $next($request);
});
I think your Action is missing something.
When using inheritance from a parent class, it might help to put this line in your constructor.
public function __construct(ApiGetListOfPostsService $service)
{
parent::__construct(); // <- Subclass constructor
$this->service = $service;
}
Doc: https://www.php.net/manual/en/language.oop5.decon.php
RegistrationController.php
use App\User;
use App\Post;
use App\Notifications\LatestPosts;
use App\Notifications\WelcomeEmail:
public function store()
{
auth()->login($user);
$allUsers = User::latest()->get();
$posts = Post::latest()->get();
$user->notify(new WelcomeEmail($user));
$allUsers->notify(new LatestPosts($posts));
return redirect(‘/dashboard’);
}
WelcomeEmail.php
use App\User;
class WelcomeEmail extends Notification
{
use Queueable:
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function toMail($notifiable)
{
$user = $this->user;
return (new MailMessage)
->subject(‘Thanks for registering’)
->markdown(‘emails.newusers.welcome’, compact(‘user’));
}
}
LatestPosts.php
use App\Post;
class LatestPosts extends Notification
{
use Queueable;
public $posts;
public function __construct(Post $posts)
{
$this->posts = $posts;
}
public function toMail($notifiable)
{
$posts = $this->posts;
return (new MailMessage)
->subject(‘Latest posts for you’)
->markdown(‘emails.posts.latestposts’, compact(‘posts’));
}
}
New users register successfully, welcome email is sent successfully but it gives me this error for sending latest posts to users.
Argument 1 passed to App\Notifications\LatestPosts::__construct() must be an instance of App\Post, instance of Illuminate\Database\Eloquent\Collection given
Basically, I want to send a list of posts to all users (I know it’s not efficient to send it while new users register but just want to see how it will work out even if I send it while new users register) Someone please help me out in this. What do I do? Thanks in advance.
In registration controller
use App\User;
use App\Post;
use App\Notifications\LatestPosts;
use App\Notifications\WelcomeEmail:
public function store()
{
auth()->login($user);
$allUsers = User::latest()->get();
$posts = Post::latest()->get();
$user->notify(new WelcomeEmail($user));
foreach($allUsers as $u){
$u->notify(new LatestPosts($posts));
}
return redirect(‘/dashboard’);
}
LatestPost
use App\Post;
use Illuminate\Database\Eloquent\Collection;
class LatestPosts extends Notification
{
use Queueable;
public $posts;
public function __construct(Collection $posts)
{
$this->posts = $posts;
}
public function toMail($notifiable)
{
$posts = $this->posts;
return (new MailMessage)
->subject(‘Latest posts for you’)
->markdown(‘emails.posts.latestposts’, compact(‘posts’));
}
}
You should change the signature of your constructor:
use App\Post;
use Illuminate\Database\Eloquent\Collection;
class LatestPosts extends Notification
{
use Queueable;
public $posts;
public function __construct(Collection $posts) // use `Collection`, not `Post`
{
$this->posts = $posts;
}
public function toMail($notifiable)
{
$posts = $this->posts;
return (new MailMessage)
->subject('Latest posts for you')
->markdown('emails.posts.latestposts', compact('posts'));
}
}