Why Laravel keeps calling schedule() with every Artisan Command? - php

I have one table called dc_user_meta and I've created one artisan command and scheduled it in kernel.php. Just after cloning the repository, when I try to run PHP artisan migrate, I get this error.
[Illuminate\Database\QueryException]
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.dc_user_meta' doesn't exist (SQL: select * from `dc_user_met
a` where `meta_key` = usage_in_days)
Not only php artisan migrate but I am unable to run any artisan command at all! I don't know why PHP keeps calling schedule method every time I try to execute any artisan command.
Here in this case, What I can do to solve this error is put the cover over my logic in schedule method just like this.
if(Schema::hasTable('dc_user_meta')){
// Code here
}
But I don't think it's good in Long run. What's the right way to solve this error?
UPDATE:
I just tried covering call to command in kernel.php just like this but still no success!
if(Schema::hasTable('dc_user_meta')){
$schedule->command('usage:update')->daily();
}
UPDATE:
I got the solution. But I don't think it's the answer to the question. It solves my problem but I don't think it's standard Solution. I just covered by Command login just like this.
if(Schema::hasTable('dc_user_meta')){
// Command Logic
}
Any specific answer to why Laravel calls schedule() with every artisan command and how to solve the error in a standard way if something like this happens!

Technically the schedule method ist called via the constructor of Illuminate\Foundation\Console\Kernel ( This is the parent class of app\Console\Kernel.php)
So every time the console Kernel is instantiated, the schedule() method gets executed.
Let's see what gets executed in which scenario ( $schedule->call() can be replaced with $schedule->command() or $schedule->exec()):
protected function schedule(Schedule $schedule)
{
// everything that is inside the schedule function is executed everytime the console kernel is booted.
// gets exectuted every time
\App\User::where('foo', 1)->get();
$schedule->call(function() {
// gets executed for every call to php artisan schedule:run
\App\User::where('foo', 1)->get();
});
$schedule->call(function() {
// gets executed for every call to php artisan schedule:run
// IF the closure in the when() function is true;
\App\User::where('foo', 1)->get();
})->when(function() {
// if true is returned the scheduled command or closure is executed otherwise it is skipped
\Schema::hasColumn('user', 'foo');
});
}
But why HAS the schedule command to be exectuted with every command?
Well, obviously php artisan schedule:run is a console command itself. So it definitely needs information about scheduled commands.
Also other commands could need information about scheduled commands... For example if you want to write an artisan command list:scheduledTasks. This command would require that all scheduled commands have been added to the console schedule list.
Maybe there are several other (internal) arguments why the schedule function has to run everytime. ( I did not dig too deep in the source code... )
Nevertheless... information about scheduled commands could be useful to a variety of use cases.

Your error is with table dc_user_meta while your logic is of table user_meta you need to do Schema::hasTable('dc_user_meta')

I'm convinced that table dc_user_meta doesn't exist in database.

As I understand, yor have table "user_meta" not "dc_user_meta" but you have written the code to use table "dc_user_meta" hence there is an error saying "dc_user_meta" table not found.

If anyone still cares about this...
<?php
# This is your app/Console/Kernel.php
use ...;
class Kernel extends ConsoleKernel {
# Other stuff...
protected function schedule(Schedule $schedule) {
if( in_array('schedule:run', $_SERVER['argv']) ){
# Your scheduler commands here...
}
}
}

Related

Laravel 5 - Setting Kernel to run test script at regular intervals. (Cron job)

In my laravel project, I have this file in my tests directory:
tom_test.php:
<?php
error_log("hello world");
I want to schedule this script to run every 'x' amount of time.
I've seen this question from cyber8200.
Unfortunately, I'm unsure how to make this test script to run.
Do I have to convert the test file to a class instead?
I would appreciate it if somebody would explicitly state what code needs to be added to the app/Console/Kernel.php file.
I suspect it should resemble some of the code in the cyber8200 question
UPDATE
Thanks to Tim Lewis' comment, I've managed to get the cron job running (I think).
Unfortunately, I cannot see the "hello world" message being logged to the console.
Here is what I've done
I added a command as follows:
public function handle()
{
error_log("hello");
// echo base_path();
exit;
include base_path().'/tests/Browser/tom_test.php';
}
and this schedule function to the kernel:
protected function schedule(Schedule $schedule)
{
$schedule->command('tom_test')
->everyMinute();
}
This is the result I get:
The job seems to be quitting after one run, and no message is logged to the console.

Laravel Mock should be called at least once but called 0 times

I have an artisan command that fires a job called PasswordResetJob which iterates as it calls a method forcePasswordReset in a repository class OrgRepository, the method updates a user's table. The whole process works fine.
Now I'm trying to write a Laravel test to mock the OrgRepository class and assert that the forcePasswordReset method is called at least once, which should be the case, based on the conditions I provided to the test. In the test, I call the artisan command to fire job; (I'm using sync queue for testing) this works fine as the job gets called and the user's table gets updated as I can view my database updates directly.
However, the test fails with the error: Mockery\Exception\InvalidCountException : Method forcePasswordReset() from Mockery_2_Repositories_OrgRepository should be called
at least 1 times but called 0 times.
The artisan call within the test is:
Artisan::call('shisiah:implement-org-password-reset');
I have tried to make the artisan call before, as well as after this mock initialization, but I still get the same errors. Here is the mock initialization within the test
$this->spy(OrgRepository::class, function ($mock) {
$mock->shouldHaveReceived('forcePasswordReset');
});
What am I missing? I have gone through the documentation and searched through Google for hours. Please let me know if you need any additional information to help. I'm using Laravel version 6.0
edit
I pass the OrgRepository class into the handle method of the job class, like this:
public function handle(OrgRepository $repository)
{
//get orgs
$orgs = Org::where('status', true)->get();
foreach ($orgs as $org){
$repository->forcePasswordReset($org);
}
}
The problem is that you are initializing your spy after your job has already run, which means during the job it will use the real class instead of the spy.
You have to do something like this in your test:
$spy = $this->spy(OrgRepository::class);
// run your job
$spy->shouldHaveReceived('forcePasswordReset');
We tell laravel to use the spy instead of the repository, run the job and then assert that the method was called.
Jeffrey Way explains it pretty well in this screencast.

Laravel Task schedule with cron column in database

I have a "reportSchedule" model which contains the report name and a cron_request column such as */15 * * * *.
I want to be able to adjust the cron within the database and affect the times which the report is requested. For example, the following is working from directly within the console/Kernel.php:
ReportSchedule::all()->each(function(ReportSchedule $reportSchedule) use($schedule){
if(isset($reportSchedule->cron_request)){
$schedule->call(function() use ($reportSchedule) {
ReportRequestNow::dispatch($reportSchedule);
})->cron($reportSchedule->cron_request);
}
});
However, having the model called from directly within the kernel causes other issues. For example database migrations now do not work and errors are thrown when caching the routes or running route:list. In general, it does not seem to like it!
So my idea was either create a seeder job or put this into its own schedule, however neither work.
// Doesnt work - the every minute schuedle is called but ReportRequestNow is never reached.
$schedule->call(function() use($schedule){
ReportSchedule::all()->each(function(ReportSchedule $reportSchedule) use($schedule){
if(isset($reportSchedule->cron_request)){
$schedule->call(function() use ($reportSchedule) {
ReportRequestNow::dispatch($reportSchedule);
})->cron($reportSchedule->cron_request);
}
});
})->everyMinute();
// Also does not work
$schedule->job(new ReportScheduleSeeder(), 'high')->everyMinute();
Can anyone suggest a why this does not work or how to get it working?
However, having the model called from directly within the kernel
causes other issues. For example database migrations now do not work
and errors are thrown when caching the routes or running route:list.
In general, it does not seem to like it!
Seems that there's some syntax errors (maybe some classes aren't listed in use?)
Have you checked laravel and PHP logs? Most likely there will be some explanations.

Dispatching a Laravel job in scheduler, pre-5.5

I'm used to Laravel 5.5+ where you can call $schedule->job(new ExampleJob); to fire jobs, which is not available in 5.4. I'm attempting to do something like this:
$schedule->call(function () {
dispatch(new AppointmentReminder);
})->dailyAt('08:00');
but the job is not firing. I've verified that this is being called at the correct time. I'm guessing the dispatch() method is not available in App\Console\Kernal.php? Does anyone know of the official way of dispatching jobs in 5.4's scheduler? This is a legacy code base and all of the jobs are inline in the Kernal.php which is a total mess. Not to mention this is a rather involved job.
I did try use Illuminate\Foundation\Bus\DispatchesJobs;/use DispatchesJobs; and then $this->dispatch(new AppointmentReminder()); in Kernal.php, but that did not seem to do the trick either. Also, (new AppointmentReminder())->dispatch(); does not work. Thanks!
You can create a new console command:
php artisan make:command CommandName
add it to App\Console\Kernal.php:
protected $commands = [
Commands\CommandName::class,
];
and make you schedual call it:
$schedule->command('CommandName')->dailyAt('08:00');
and inside your command in the "handle" function, dispatch the job.

Laravel queue keep processing multiple times for a job

Below is what's happening when i run php artisan queue:listen and at my job table only have one job
and this is my code :
public function handle(Xero $xero)
{
$this->getAndCreateXeroSnapshotID();
$this->importInvoices($xero);
$this->importBankTransaction($xero);
$this->importBankStatement($xero);
$this->importBalanceSheet($xero);
$this->importProfitAndLoss($xero);
}
In order for a job to leave the queue, it must reach the end of the handle function -- without errors and exceptions.
There must be something breaking inside one or more of your functions.
If an exception is thrown while the job is being processed, the job will automatically be released back onto the queue so it may be attempted again. https://laravel.com/docs/5.8/queues
The same behavior can be achieved with
$this->release()
If you can't figure out what is breaking, you can set your job to run only once. If an error is thrown, the job will be considered failed and will be put in the failed jobs queue.
The maximum number of attempts is defined by the --tries switch used
on the queue:work Artisan command. https://laravel.com/docs/5.8/queues
php artisan queue:work --tries=1
If you are using the database queue, (awesome for debugging) run this command to create the failed queue table
php artisan queue:failed
Finally, to find out what is wrong with your code. You can catch and log the error.
public function handle(Xero $xero)
{
try{
$this->getAndCreateXeroSnapshotID();
$this->importInvoices($xero);
$this->importBankTransaction($xero);
$this->importBankStatement($xero);
$this->importBalanceSheet($xero);
$this->importProfitAndLoss($xero);
}catch(\Exception $e){
Log::error($e->getMessage());
}
}
You could also set your error log channel to be slack, bugsnag or whatever. Just be sure to check it. Please don't be offended, it's normal to screw up when dealing with laravel queues. How do you think I got here?
Laravel try to run the job again and again.
php artisan queue:work --tries=3
Upper command will only try to run the jobs 3 times.
Hope this helps
In my case the problem was the payload, I've created the variable private, but it needs to by protected.
class EventJob implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
// payload
protected $command;
// Maximum tries of this event
public $tries = 5;
public function __construct(CommandInterface $command)
{
$this->command = $command;
}
public function handle()
{
$event = I_Event::create([
'event_type_id' => $command->getEventTypeId(),
'sender_url' => $command->getSenderUrl(),
'sender_ip' => $command->getSenderIp()
]);
return $event;
}
}
The solution that worked for me to delete the job after pushing them into the queue.
Consider the e.g.
class SomeController extends Controller{
public function uploadProductCsv(){
//process file here and push the code inot Queue
Queue::push('SomeController#processFile', $someDataArray);
}
public function processFile($job, $data){
//logic to process the data
$job->delete(); //after complete the process delete the job
}
}
Note: This is implemented for laravel 4.2

Categories