Fairly new to Laravel and this is the first time I have worked with the Laravel scheduler and Jobs Queue.
I'm working on an app to retrieve data from a remote api and store it locally. I have a config file with an array of locations. Each location may have a different frequency for retrieving data. In my App\Console\Kernel class I am looping through the location array and scheduling a recurring job to collect data for the location.
I have created a job to retrieve the remote data and save it. The job class accepts a location parameter.
When I start the scheduler it loops through the location array and dispatches the job the correct number of times. The problem I am having is that each time my job runs it is using the location parameter from the last one in the locations array every time the job runs.
If I have 5 locations in my array the job runs 5 times but uses the location parameter that was at the end of my location array each time.
Scheduler
class Kernel extends ConsoleKernel
{
protected $job;
protected $queue;
protected $zip;
protected $locations;
protected $location;
protected $name;
protected $job_interval;
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$this->locations = config('weather-app.locations');
foreach( $this->locations as $this->name => $this->location ){
//print_r($location);
$this->job_interval = (is_null($this->location['interval']) ? config('weather-app.default_interval') : $this->location['interval']);
if( $this->validateInterval($this->job_interval)){
//$this->queue =
$this->job = (new GetWeatherDataByZip($this->location['zip'])); //->onQueue((string)$this->location['zip'] );
Log::info('Dispatching job for location: ' . $this->location['zip']);
$schedule->call(function() {
dispatch($this->job);
})->cron($this->job_interval);
} else {
$log_msg = 'Failed to initiate new data retrieval schedule for location ' . $this->name . ' - ' . $this->location['zip'] . ' - Invalid interval format';
Log::error($log_msg);
}
}
}
/**
* Register the Closure based commands for the application.
*
* @return void
*/
protected function commands()
{
require base_path('routes/console.php');
}
private function validateInterval($job_interval){
$data = [
'job_interval' => $job_interval
];
$cron_regex = "/^(((([\*]{1}){1})|((\*\/){0,1}(([0-9]{1}){1}|(([1-5]{1}){1}([0-9]{1}){1}){1}))) ((([\*]{1}){1})|((\*\/){0,1}(([0-9]{1}){1}|(([1]{1}){1}([0-9]{1}){1}){1}|([2]{1}){1}([0-3]{1}){1}))) ((([\*]{1}){1})|((\*\/){0,1}(([1-9]{1}){1}|(([1-2]{1}){1}([0-9]{1}){1}){1}|([3]{1}){1}([0-1]{1}){1}))) ((([\*]{1}){1})|((\*\/){0,1}(([1-9]{1}){1}|(([1-2]{1}){1}([0-9]{1}){1}){1}|([3]{1}){1}([0-1]{1}){1}))|(jan|feb|mar|apr|may|jun|jul|aug|sep|okt|nov|dec)) ((([\*]{1}){1})|((\*\/){0,1}(([0-7]{1}){1}))|(sun|mon|tue|wed|thu|fri|sat)))$/";
$rules = [
'job_interval' => [
'required',
'regex:' . $cron_regex
]
];
return $this->app['validator']->make($data, $rules);
}
}
Job - receives zip from construct
class GetWeatherDataByZip implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $zip;
protected $weather_client;
protected $weather_data;
public $tries;
public $timeout;
public $retry_after;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct( $zip=NULL )
{
$this->zip = $zip;
/**
* The number of times the job may be attempted.
*
* @var int
*/
$this->tries = config('weather-app.weatherApi.maxTries');;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
$this->timeout = config('weather-app.weatherApi.timeOut');
/**
* The number of seconds between failed job attempts.
*
* @var int
*/
$this->retry_after = config('weather-app.weatherApi.retryAfter');
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if(is_null($this->zip)){
throw new Exception('Missing required input parameter: zip.');
}
Log::info('Starting job for location ' . $this->zip);
$weather_client = new Client();
$endpoint = config('weather-app.weatherApi.endpoint')
. '?units=' . config('weather-app.weatherApi.units')
. '&' . config('weather-app.weatherApi.lookupParam') . '=' . $this->zip;
try {
$response = $weather_client->request('GET', $endpoint . '&' . config('weather-app.weatherApi.keyParam') . '=' . config('weather-app.weatherApi.key'), ['allow_redirects' => false, 'http_errors' => true] );
$response_body = json_decode($response->getBody());
$this->save($response_body);
} catch (ClientException $error) {
$response = $error->getResponse();
$response_code = $response->getStatusCode();
$response_reason = $response->getReasonPhrase();
switch(substr($response_code,0,1)){
case '3':
// Do something unique with Redirect Errors
$error_type = 'REDIRECT';
break;
case '4':
// Do something unique with Client Errors
$error_type = 'CLIENT';
break;
case '5':
// Do something unique with Server Errors
$error_type = 'SERVER';
break;
}
$log_msg = $error_type . ' ERROR: ' . $response_code . ' - ' . $response_reason . ' for request ' . $endpoint . ' - Attempt ' . $this->attempts() . ' of ' . $this->tries . ' - Retry in ' . $this->retry_after . ' seconds.';
Log::error($log_msg);
/**
* Retry the job.
*
* @return void
*/
if( $this->attempts() < $this->tries ) {
$this->release($this->retry_after);
} else {
//$this->delete();
$log_msg = 'ABORTING FAILED JOB for request ' . $endpoint;
Log::error($log_msg);
abort($response_code, $log_msg);
}
}
}
/**
* Save the data.
*
* @return void
*/
private function save($data) {
$weather_data = new WeatherDataModel;
$weather_data->name = $data->name;
$weather_data->zip = $this->zip;
$weather_data->conditions = $data->weather[0]->description;
$weather_data->conditions_short = $data->weather[0]->main;
$weather_data->pressure = $data->main->pressure;
$weather_data->temperature = $data->main->temp;
$weather_data->wind_direction = $data->wind->deg;
$weather_data->wind_speed = $data->wind->speed;
$weather_data->humidity = $data->main->humidity;
$weather_data->save();
}
}
Can anyone see where I may be going wrong?
I have been testing this on my local WAMP environment. Perhaps I need to test this on our CentOs server where cron is actually running?
It seems like this should work.