I'm working on task scheduling to send sms notification to users at a specific date or time. Is it possible to pass data from controller to kernel schedule? Let's say:
protected function schedule(Schedule $schedule)
{
$schedule->command('sms:send')->dailyAt($time);
}
If it is possible, any tips on how to do it? Thank you very much.
You can call controller to Console/Commands/Yourclass
like this in handle() function-
protected $signature = 'showing:rating'; //your command
//you can write and call logic here in handle function
public function handle()
{
$rating = (new CronController())->showingRating();
}
And then you can call your command in Kernel#schedule() function like this -
protected function schedule(Schedule $schedule)
{
$schedule->command('showing:rating')
->hourly();
}
Hope this will help you.
Related
I would like to schedule jobs from other parts of the code. So I created my own scheduler class which gets Illuminate\Console\Scheduling\Schedule::class injected as constructor parameter. The scheduler class is resolved with app(..::class).
Everything works fine however jobs scheduled on this instance are never actually scheduled.
One idea was maybe registering the Illuminate\Console\Scheduling\Schedule as singleton.
Ex.:
class JobA extends Job
{
private $taskList;
public function __construct(TaskList $taskList)
{
$this->taskList = $taskList;
}
public function handle()
{
$this->taskList->run();
}
}
class TaskList
{
private $tasks = [
TaskA::class,
TaskB::class,
...
];
public function run()
{
foreach($this->tasks as $task) {
// resolve $task and call it's own run method..
}
}
public function addToSchedule(Schedule $schedule)
{
$scheduler->job(new JobA($this))->everyFiveMinutes();
}
}
The Illuminate\Console\Scheduling\Schedule class is already defined as a Singleton. What would be interesting to understand is that when you say other parts of your code, do you refer to the request lifecycle code or console code? Both have different Kernels and different applications and scheduling was meant to be part of the Console / CLI part of Laravel
I've just learned of model observers and would like to move some of my logic from controller to observer. Here's what I have:
AppServiceProvider.php
public function boot()
{
WorkOrder::observe(WorkOrderObserver::class);
}
WorkOrderObserver.php
namespace App\Observers;
use App\Site;
use App\WorkOrder;
use Carbon\Carbon;
use App\WorkOrderNumber;
class WorkOrderObserver
{
public function creating(WorkOrder $workOrder)
{
$branchOfficeId = Site::findOrFail($request->site_id)->branch_office_id;
$today = Carbon::today('America/Los_Angeles');
$todaysWorkOrderCount = WorkOrder::where('created_at_pst', '>=', $today)->count();
$workOrder->work_order_number = (new WorkOrderNumber)
->createWorkOrderNumber($branchOfficeId, $todaysWorkOrderCount);
$workOrder->completed_by = null;
$workOrder->status_id = 1;
$workOrder->work_order_billing_status_id = 1;
$workOrder->created_at_pst = Carbon::now()->timezone('America/Los_Angeles')
->toDateTimeString();
}
}
Problem is accessing the request from within the observer. I don't see anything in the docs. I found one thread here that refers to this and it suggested using the request helper function. I tried request('site_id') but it was empty.
This is so simple I'm a bit embarrassed I posted it. Anyway, in case someone finds this thread, here's the solution. In your observer, add a constructor that accepts the request and sets a property.
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
You can request object using app helper function of Laravel.
protected $request;
public function __construct(WorkOrderNumber $workorder)
{
$this->request = app('request');
}
get request data in observer laravel
request() helper should work:
if (request()->has('password')) {
$user->password = bcrypt(request()->password);
}
ref: https://www.codegrepper.com/code-examples/php/get+request+data+in+observer+laravel
enter code hereI'm trying to dispatch queue from another queue in laravel rightnow. So for example I have created a job like this :
class CandidateQueue extends Job implements SelfHandling
{
protected $request;
public function __construct($request)
{
$this->request = $request;
}
public function handle()
{
....
$this->dispatch($queueEmail);
}
}
The problem is when executing $this->dispatch(), laravel said "Call to undefined method ....::dispatch()". So How do I trigger this queue from current queue ?
Thanks in advance
Old question, but how I did was instead of
$this->dipatch(new queueEmail);
I did
dispatch(new queueEmail);
I've created a terminable middleware that sends a request to Google Analytics. One of the attributes I send is the server response time. Here's how I do it:
In \App\Http\Kernel I add the SendAnalytics middleware:
class Kernel extends HttpKernel {
...
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
...
'App\Http\Middleware\SendAnalytics',
];
}
And the SendAnalytics middleware looks like:
class SendAnalytics implements TerminableMiddleware {
protected $startTime;
public function __construct() {
$this->startTime = microtime(true);
}
public function handle($request, Closure $next) {
return $next($request);
}
public function terminate($request, $response) {
$responseTime = microtime(true) - $this->startTime;
/* I send a request to Google here, using their Measurement Protocol */
// Dying for debugging purposes
dd($responseTime); // Always prints 0.0
}
}
But this always shows 0.0. What would be the correct way to show the server response time?
I used microtime(true) - LARAVEL_START. Seems to give a fairly accurate response time.
As Bogdan mentions in the comment:
The LARAVEL_START constant is defined in bootstrap/autoload.php which
is the very first file included from public/index.php, so this makes
it the first statement to be executed. If you place the middleware
last on the list, its terminate method will be the last one executed
before app->terminate() will be called, so you should get a pretty
good computation of execution time.
I noticed that in a single request life cycle middleware instance may be initialized more than once. The second time is right before terminate call. That would explain zero time result (on my machine it was not zero but pretty close to it, while the actual request time was more like 200ms). The handle method was obviously called only once and this is where start time must be recorded.
class SendAnalytics implements TerminableMiddleware {
protected $startTime;
public function handle($request, Closure $next) {
$this->startTime = microtime(true);
return $next($request);
}
...
}
I'm wondering what the best way to add things like weekends to the available schedule constraints:
Illuminate\Console\Scheduling\Event.php
public function weekdays()
{
return $this->spliceIntoPosition(5, '1-5');
}
and its logical opposite:
public function weekends()
{
return $this->days(array( '0','6'));
}
Where would I include these things so that they're not overwritten with a framework update?
First of all, if all you are missing is the weekends() method, you can achieve that by calling days(6,7) on your event.
If you need to add some more logic to the scheduler, please go on reading.
I had a look at the code and while Laravel doesn't offer a way to extend the Scheduler, and specifically its scheduled Events, with additional methoods, it's still possible to apply additional constraints from your Kernel::schedule().
Depending on your needs, there are 2 ways to do it.
If you want to set some custom CRON expression for an event, you can simply use its cron() method:
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
//scheduled code
})->cron('0 1 2 * * *')->daily();
}
If you need to apply some CRON constraints using existing methods, but need to modify it later the way weekdays() does using spliceIntoPosition, you can access it by calling getExpression(), modify it, and then set using cron().
protected function schedule(Schedule $schedule)
{
$event = $schedule->call(function () {
//scheduled code
});
$scheduledAt = $event->getExpression(); //get cron expression
...; //modify the $scheduledAt expression
$event->cron($scheduledAt); // set the new schedule for that even
}
If you want to reuse the logic for multiple events, you can add helper functions in your Kernel.php, e.g.:
protected function specialSchedule(\Illuminate\Console\Scheduling\Event $event) {
$scheduledAt = $event->getExpression();
...; // modify $scheduledAt expression
$event->cron($scheduledAt);
return $event;
}
Then you can reuse that logic when defining the schedule:
protected function schedule(Schedule $schedule)
{
$this->specialSchedule($schedule->call(function () {
//scheduled code
}));
}
UPDATE:
There is one more way to do that - a bit more complex, as it requires you to provide your own Schedule and Event classes, but also more flexible.
First, implement your own Event class and add there the new methods:
class CustomEvent extends \Illuminate\Console\Scheduling\CallbackEvent {
public function weekends() {
return $this->days(6,7);
}
}
Then your own Schedule class, so that it creates CustomEvent objects:
class CustomSchedule extends \Illuminate\Console\Scheduling\Schedule
{
public function call($callback, array $parameters = [])
{
$this->events[] = $event = new CustomEvent($callback, $parameters);
return $event;
}
public function exec($command, array $parameters = [])
{
if (count($parameters)) {
$command .= ' '.$this->compileParameters($parameters);
}
$this->events[] = $event = new Event($command);
return $event;
}
}
Lastly, in your Kernel.php you need too make sure your new schedule class is used for scheduling:
protected function defineConsoleSchedule()
{
$this->app->instance(
'Illuminate\Console\Scheduling\Schedule', $schedule = new Schedule
);
$this->schedule($schedule);
}
Folowing #jedrzej.kurylo's answer, i did this on laravel 5.8:
php artisan make:model CustomCallbackEvent
php artisan make:model CustomEvent
php artisan make:model CustomSchedule
In CustomCallbackEvent:
use Illuminate\Console\Scheduling\CallbackEvent;
use Illuminate\Console\Scheduling\EventMutex;
class CustomCallbackEvent extends CallbackEvent
{
public function __construct(EventMutex $mutex, $callback, array $parameters = [])
{
parent::__construct($mutex, $callback, $parameters);
}
}
In CustomSchedule:
use Illuminate\Console\Scheduling\Schedule;
class CustomSchedule extends Schedule
{
public function call($callback, array $parameters = [])
{
$this->events[] = $event = new CustomCallbackEvent(
$this->eventMutex,
$callback,
$parameters
);
return $event;
}
public function exec($command, array $parameters = [])
{
if (count($parameters)) {
$command .= ' '.$this->compileParameters($parameters);
}
$this->events[] = $event = new CustomEvent($this->eventMutex, $command, $this->timezone);
return $event;
}
}
In CustomEvent:
use Illuminate\Console\Scheduling\Event;
class CustomEvent extends Event
{
public function myFunction()
{
//your logic here
}
}
In Kernel.php:
protected function defineConsoleSchedule()
{
$this->app->instance(
'Illuminate\Console\Scheduling\Schedule', $schedule = new CustomSchedule
);
$this->schedule($schedule);
}
The Illuminate\Console\Scheduling\Event class uses the Macroable trait. It means that you can dynamically add methods to the class without inheriting it.
First of all you have to register it in the boot method:
use Illuminate\Console\Scheduling\Event;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Event::macro(
'weekends',
function () {
/** #var Event $this */
return $this->days([0, 6]);
}
);
}
}
Then you can use it as any other method:
$schedule->command('do-work')->weekends();
For more details about macros see https://asklagbox.com/blog/laravel-macros