I am writing Laravel json api using MySQL. What I am trying to do, is whenever user creates record, I want to perform some operation on this record every 24 hours, until some changes are made by admin on this record. So averagely this job will be repeated 10-15 times. this is my job:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Http\Models\Orders\RoadOrder;
class RoadOrderEprirationHandler implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
protected $order_id;
public function __construct($order_id)
{
$this->order_id = $order_id;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$order = RoadOrder::findOrFail($this->order_id);
//do some tasks and if I need to run this cycle again :
$roadOrderEprirationHandler = new RoadOrderEprirationHandler($order->id);
$roadOrderEprirationHandler->dispatch($order->id)->delay(now()->addHours(24));
}
}
So as you can see, I initiate job after previous job was finished if admin didn't modified record and I need to run cycle again. after several cycles, I won't run job again, so it will be finished.
My question:
Is some problem in this implementation? maybe I am doing something wrong or maybe there may occur some type of memory leak (because I create job inside job again and again). I can write this but I need to be sure that this implementation doesn't leads to some type of bugs or errors.
Why don't create a separate table in your DB for that purpose?
You can keep order_ids with status (checked by admin / not checked). Or just put new order_id there and delete it after admin check. So you will only use your jobs to see is there any unchecked ids. Or some kind of that.
I think this approach is more predictable and less buggy.
Related
I've got the tasks table, this table has status and deadline columns. How can status be changed to "expired" automatically when current date will become greater than task's deadline date? Is there any realtime event listeners in Laravel?
I guess that's how event listener class should look like, but I'm not sure what to do next.
<?php
namespace App\Events;
use App\Models\Task;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class DeadlineExpired
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* The task instance.
*
* #var \App\Models\Task
*/
public $task;
/**
* Create a new event instance.
*
* #param \App\Models\Task $task
* #return void
*/
public function __construct(Task $task)
{
$this->task = $task;
}
}
Since you're checking only the date. Your cron needs to run only once at midnight. Use Laravel Scheduler to do your Job.
First create a class
class UpdateTasks
{
public function __invoke()
{
// do your task here...e.g.,
Tasks::whereDate('deadline','<',today())->update(['status'=>'expired']);
}
}
Then in your app\Console\Kernel.php, schedule method-
$schedule->call(new UpdateTasks())->daily();
Finally configure a cron job at your server to run the schedule command daily at midnight.
php artisan schedule:run
There are Realtime event listeners but thes require a action to fire. Example are when a model is Created, Updated or Deleted then these events fire.
There is no built in "listener" to ping every model waiting for a field you defined to change.
If there is further logic you would like to fire when the Task becomes expired (like send email) then your best would be to run a check for any new expired Tasks using the scheduler.
The Scheduler runs every minute - set by cron.
I am implementing Queue JOBS, in my Laravel Project for the first time. But I am facing some difficulties on it, as after php artisan queue:work, noting is showing on the terminal.
Let Me describe, the procedure I have tried.
My Controller Function, from where I am trying to fire the Job Queue:
use App\Jobs\InitiateRecharge;
/
/*** Other Codes are here....
/
public function testQueueJob(){
InitiateRecharge::dispatch(1)->onQueue('initrecharge');
return 1;
}
My JOB Queue Class:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class InitiateRecharge implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $reportid;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct($reportid)
{
$this->reportid = $reportid;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
sleep(10);
\Log::info('hello....');
}
}
I have also migrated the jobs table, and rows are also getting inserted on that.
It's is not giving any error, but the Job expected output is also not coming .. and in the terminal noting getting changed.
Please, anyone, help me
Thank You in advance :)
try php artisan queue:listen --tries=1
I am using a php script to scrape websites (hQuery by Duzun). I have a form that has an input where someone can paste the URL they want scraped and a submit button to trigger the scraping. How can I set up the code below so that when a user clicks submit, the hQuery script picks up the URL they have put in the input and runs the script.
So far I have tried using the action() helper in Laravel to trigger a method in a ScraperController but that doesn't seem to be working. I get an error saying the postScrape method has not been defined.
<form action="{{ action('ScraperController#postScrape') }}" method="POST">
route::post('/', 'ScraperController#postScrape');
I am hoping that the user can click the button, their URL is passed to the script and it can then scrape the website they have linked to.
Because scraping is a potentially resource heavy and long-running task, you should delegate this to a queued job. You can read about queues and jobs here: https://laravel.com/docs/master/queues
I'd recommend saving the user's scrape request as a model entry. That way you can track the status (running, errored, completed, etc.), display it in a listing of tasks, attribute system load to users, and so on. Something like ScrapeTask (id, user_id, url, status, timestamps, and whatever other fields you'll need).
Once you have your model and your queue set up, you will then create a job class. This can be done by running php artisan make:job ScrapeWebsite, or you can create it manually.
app/Jobs/ScrapeWebsite.php
<?php
namespace App\Jobs;
use App\ScrapeTask;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ScrapeWebsite implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** #var ScrapeTask */
protected $scrapeTask;
/**
* Create a new job instance.
*
* #param ScrapeTask $scrapeTask
*/
public function __construct(ScrapeTask $scrapeTask)
{
$this->scrapeTask = $scrapeTask;
// also inject any necessary third party libraries, etc.
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
// scrape the website at $this->scrapeTask->url
}
}
Your controller method would look something like this:
app/Http/Controllers/ScraperController.php
<?php
namespace App\Http\Controllers;
use Auth;
use App\ScrapeTask;
use App\Jobs\ScrapeWebsite;
use Illuminate\Http\Request;
class ScraperController extends Controller
{
public function postScrape(Request $request)
{
// perform input validation, etc.
// create a new scrape task
$scrapeTask = ScrapeTask::create([
'user_id' => Auth::id(),
'url' => $request->input('url'),
]);
// dispatch the ScrapeWebsite job to the queue
dispatch(new ScrapeWebsite($scrapeTask));
// redirect to a scrape status monitoring page (or do whatever...)
return redirect()->route('scrape.monitor', $scrapeTask->id);
}
}
I would highly recommend setting up failed job tracking (https://laravel.com/docs/master/queues#dealing-with-failed-jobs) to make sure that you know when things aren't working.
I know how to use ShouldQueue my question is about why does it work the way it does.
I need to edit how my new Job is stored in the database, and therefore am digging through Laravel's internals.
The job I want to edit is launched from the following event listener:
<?php
namespace App\Listeners;
use App\Events\NewMail;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Jobs\SendEmail;
use Carbon\Carbon;
class NewMailListener implements ShouldQueue
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param NewMail $event
* #return void
*/
public function handle(NewMail $event)
{
$addressee = $event->user->name;
$address = $event->user->email;
$type = "NewMail";
$job = (new SendEmail($type,$addressee,$address))->delay(Carbon::now()->addMinutes(10));
dispatch($job);
}
}
What I don't understand is how the ShouldQueue magic works, because in the source code it appears to do nothing.
<?php
namespace Illuminate\Contracts\Queue;
interface ShouldQueue
{
//
}
I understand it is a contract but it's not defining anything... so what it is doing exactly? Is there some auto-loading happening from the namespace?
I wasn't sure what an interface was exactly, so I looked at this: PHP Docs: Interfaces and came away with the impression that even if it is for decoupling, and interface should be defining something, which I don't see in ShouldQueue.
The top comment on that PHP docs page says this:
An INTERFACE is provided so you can describe a set of functions and
then hide the final implementation of those functions in an
implementing class. This allows you to change the IMPLEMENTATION of
those functions without changing how you use it.
But where is this description of functions here?
PS - I know this interface/contract is being used to queue the event listener itself, not the job that I want to edit. But I'm hoping understanding how the queue interacts with the event listener will better inform me as to how it works for the jobs.
Internally Laravel checks if Job or Mailable or Notification etc implements ShouldQueue interface. For example:
if ($job instanceof ShouldQueue) {
https://github.com/laravel/framework/blob/5.5/src/Illuminate/Console/Scheduling/Schedule.php#L86
I have such function in my trait:
public function cupPlayMatch(Season $season, $round_id)
{
foreach($season->cups as $cup)
{
$this->cupPlay($cup, $round_id);
}
}
And second cup isstarting playing when first cup finished. How I can start playing all my cups simultaneous?
For the most part, PHP is "synchronous", that means that you theoretically can't make "simultaneous calls" to any function.
However, some workarounds exist to make this work.
PHP is a script language. So when you launch this in your console:
php -r "echo 'Hello World';"
A PHP process is launched, and anything that happen in this process is executed synchronously.
So the solution here, is to launch various PHP processes in order to be able to run functions simultaneously.
Imagine an SQL table where you put all the functions that you want to execute simultaneously. You could then run 10 php processes that would actually work "at the same time".
Laravel provides a solution to this problem out of the box. And as #Anton Gildebrand mentioned it in the comments, it's called "Queues".
You can find the documentation here: https://laravel.com/docs/5.5/queues
The laravel way of doing it, is to create "jobs". Each Job represents a function that you want to execute. Here, your Job would be cupPlay.
Here is the basic example of a job copy pasted from the documentation:
<?php
namespace App\Jobs;
use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $podcast;
/**
* Create a new job instance.
*
* #param Podcast $podcast
* #return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
/**
* Execute the job.
*
* #param AudioProcessor $processor
* #return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
}
When you'll have configured your worker driver to run your queues, you'll just need to launch:
php artisan queue:work --queue=high,default
from the command line and it will execute your tasks.
And you can execute as many workers as you want, depending on your needs...
I hope this helps!