Laravel Task schedule with cron column in database - php

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.

Related

Custom views in Laravel 9 + Jetstream won't load without testing first

I have a new project I created recently using the latest Laravel 9 and Jetstream with Livewire. I've created some migrations and seeders and they run perfectly with Artisan.
Now I'm ready to start creating my homepage. I create a brand-new file called index.blade.php in the resources/views directory. I give it the contents of "Hello World." That's it. No boilerplate, no HTML, nothing. I update the main / route to point to index instead of welcome. I then create a Feature Test to make sure that getting the / route returns a status code of 200. It runs great. The page also loads fine in a browser displaying the "Hello World" text.
Now comes the part where I get confused. I change the "Hello World" text to just say "Hello". Refreshing the browser, I get the following message:
If I run the same Feature Test again, it still passes fine. Then, when I refresh the browser again after having run the test, it loads!
I have tried out many different procedures, but it seems like the page simply will not load whenever I make any change to it at all. However, any time I run the Feature Test, then reload the page in the browser, it'll show up fine. For reference, here's the test:
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class AppTest extends TestCase
{
/**
* Testing that the app's main page loads successfully.
*
* #return void
*/
public function test_app_home_page_loads_successfully(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
What on earth is causing this odd behavior?
As #IGP and #matiaslauriti pointed out in the comments, there is clearly a permissions issue with storage/framework/views. However, it is not with the directory itself, just a single file (out of dozens) in that directory.
I do not know how or why, but that one file (83b9b...) had an owner:group of "root:root". The rest of the files in that directory are owned by me and my group.
What makes this odd is that this stays true even when I run the Feature Test, so I do not know why running that Feature Test causes the view to load fine even while that file is still owned by "root:root".
Manually changing that file's owner:group does, indeed, fix the issue. My only concern now is not knowing how it got changed in the first place and, thus, not knowing how to prevent this from happening again in the future. If anyone has any ideas, I'm open. For now, however, I'll go ahead and close this.
Thank you, #IGP and #matiaslauriti.

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.

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

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...
}
}
}

How to get currently used Artisan console command name in Laravel 5?

Problem / What I've tried:
Getting the currently used controller and action in Laravel 5 is easy (but not as easy as it should be), however I'm stuck with getting the currently used artisan console command.
To fetch the controller name I do this:
$route = Route::getRoutes()->match(Request::capture());
$listAction = explode('\\', $route->getActionName());
$rawAction = end($listAction);
// controller name and action in a simple array
$controllerAndAction = explode('#', $rawAction);
But when calling from a console action, it always returns the default index controller's name ("IndexController" or so in Laravel). Does anybody know how to make this ?
By the way I've also worked throught Request::capture() but this still gives no info about the command.
The simplest way is to just to look at the arguments specified on the command line:
if (array_get(request()->server(), 'argv.1') === 'cache:clear') {
// do things
}
Yes, you can use $_SERVER directly, but I like to use the helper functions or the Facades, as those will give you the current data.
I go from the assumption that - during unit tests - the superglobals might not always reflect the currently tested request.
By the way: Obviously can also do array_get(request()->server('argv'), '1') or something alike. (request()->server('argv.1') doesnt work at this point). Or use \Request::server(). Depends on what you like most.
As per the Symfony\Component\Console\Command\Command class, the method to return the name of the command (eg. my:command) is:
$this->getName();
You should use it from within an Artisan command extending Illuminate\Console\Command (default on Artisan commands).
Remember that it will return only the command name and not the available parameters (eg. for the command signature my:command {--with-params=} it will only return my:command).
Reflection might be of help? Try this:
$var = new \ReflectionClass($this);
dd($var);

Where can I get a complete list of Laravel events (fired by the core libraries)?

I want to know what events are fired by Laravel core libraries. I want to get the complete list, such as laravel.query and laravel.done.
There are four events listed at the official docs, but I think Laravel has more events than these four!
Laravel doesn't actually fire as many events as you'd think. While it does make use of the Event system it's there for developers to use within there applications. Anyway, here's a list I compiled.
laravel.done
laravel.log
laravel.query
laravel.resolving
laravel.composing: {viewname}
laravel.started: {bundlename}
laravel.controller.factory
laravel.config.loader
laravel.language.loader
laravel.view.loader
laravel.view.engine
view.filter
eloquent.saving
eloquent.updated
eloquent.created
eloquent.saved
eloquent.deleting
eloquent.deleted
eloquent.booted: {$model}
eloquent.booting: {$model}
500
404
The 500 and 404 are both error related events. These are set in the routes.php file so you can see what the default listener is.
I'd like to point out that the eloquent.{event} have another variation containing the class name that is being updated.
eloquent.{event}: {classname}
I'm not going to say this is absolutely everything but it should be at least 99% of it.
In addition to Jason Lewis answer, I have few more to add. I simply searched for fire() function and came up with following list for Laravel 5,
$this->events->fire('auth.attempt', $payload);
$this->events->fire('auth.login', [$user, $remember]);
$this->events->fire('auth.logout', [$user]);
$this->events->fire('cache.'.$event, $payload);
$this->laravel['events']->fire('cache:clearing', [$storeName]);
$this->laravel['events']->fire('cache:cleared', [$storeName]);
$events->fire('artisan.start', [$this]);
$this->events->fire('illuminate.query', array($query, $bindings, $time, $this->getName()));
$this->events->fire('connection.'.$this->getName().'.'.$event, $this);
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
$this['events']->fire('locale.changed', array($locale));
$this['events']->fire($class = get_class($provider), array($provider)); //after provider registered.
$this->app['events']->fire('kernel.handled', [$request, $response]);
$this->dispatcher->fire('illuminate.log', compact('level', 'message', 'context'));
$this->events->fire('mailer.sending', array($message));
$this->events->fire('illuminate.queue.failed', array($connection, $job, $data));
$this->events->fire('illuminate.queue.stopping');
$this->events->fire('router.matched', [$route, $request]);
$this->events->fire('composing: '.$view->getName(), array($view));
$this->events->fire('creating: '.$view->getName(), array($view));
If you are debugging your Laravel application, you can get a full list of fired events in the console (for example when you are running unit tests or an artisan command) for the running process with the following snippet:
Event::listen('*', function ($event) {
echo $event."\n";
});
If you use the logger function it would go to an infinite loop.
Here are a few of them more, got them while dumping static::$events
laravel.config.loader
laravel.view.loader
laravel.language.loader
laravel.view.engine
404
Not really sure if overriding these would work, as they are internally called

Categories