为啥 Laravel 总是在每个 Artisan 命令中调用 schedule()?

Posted

技术标签:

【中文标题】为啥 Laravel 总是在每个 Artisan 命令中调用 schedule()?【英文标题】:Why Laravel keeps calling schedule() with every Artisan Command?为什么 Laravel 总是在每个 Artisan 命令中调用 schedule()? 【发布时间】:2017-12-06 06:00:56 【问题描述】:

我有一张名为dc_user_meta 的表,我创建了一个工匠命令并将其安排在kernel.php 中。克隆存储库后,当我尝试运行 PHP artisan migrate 时,我收到此错误。

  [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)

不仅php artisan migrate,而且我根本无法运行任何工匠命令!我不知道为什么 PHP 每次尝试执行任何工匠命令时都会调用 schedule 方法。

在这种情况下,我可以做些什么来解决这个错误,就像这样在schedule 方法中覆盖我的逻辑。

if(Schema::hasTable('dc_user_meta'))
    // Code here

但从长远来看,我认为这并不好。解决此错误的正确方法是什么? 更新: 我只是尝试像这样覆盖kernel.php中的命令调用,但仍然没有成功!

if(Schema::hasTable('dc_user_meta'))
    $schedule->command('usage:update')->daily();

更新: 我得到了解决方案。但我不认为这是问题的答案。它解决了我的问题,但我认为这不是标准解决方案。我只是像这样被Command login覆盖。

if(Schema::hasTable('dc_user_meta'))
    // Command Logic

关于为什么 Laravel 使用每个 artisan 命令调用 schedule() 的任何具体答案,以及如果发生这种情况如何以标准方式解决错误!

【问题讨论】:

我无法重现它(L5.4)我做了一个命令调用一个不存在的表,但调用它时只得到一个错误。 @cbaconnier 在数据库中创建表,然后尝试运行“php artisan migrate:refresh” 你使用的是哪个版本的 Laravel? 我使用的是 laravel5.4 【参考方案1】:

你的错误是表dc_user_meta,而你的逻辑是表user_meta,你需要做Schema::hasTable('dc_user_meta')

【讨论】:

【参考方案2】:

我确信数据库中不存在表 dc_user_meta。

【讨论】:

如果您的代码没有任何错误,它应该可以工作。在您的代码中某处存在导致迁移失败的错误。执行:php artisan migrate -vvv 以查看完整堆栈并在此处发布 正在尝试。会告诉你会发生什么!【参考方案3】:

据我了解,您的表是“user_meta”而不是“dc_user_meta”,但您已经编写了使用表“dc_user_meta”的代码,因此出现错误提示“dc_user_meta”表未找到。

【讨论】:

它是dc_user_metadc_ 是前缀。只是为了给你看表的名字,我写了user_meta。实际表名是dc_user_meta,我为dc_user_meta编写了代码。【参考方案4】:

从技术上讲,调度方法是通过 Illuminate\Foundation\Console\Kernel 的构造函数调用的(这是 app\Console\Kernel.php 的父类)

因此,每次实例化控制台内核时,都会执行 schedule() 方法。

让我们看看在哪种情况下执行了什么($schedule->call() 可以替换为 $schedule->command() 或 $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');
    );


但是为什么每个命令都要执行 schedule 命令呢?

嗯,显然 php artisan schedule:run 本身就是一个控制台命令。所以它肯定需要有关预定命令的信息。

其他命令也可能需要有关计划命令的信息...例如,如果您想编写一个工匠命令 list:scheduledTasks。此命令将要求所有计划的命令都已添加到控制台计划列表中。

也许还有其他几个(内部)参数为什么调度函数必须每次都运行。 (我没有对源代码挖掘太深...) 不过...有关预定命令的信息可能对各种用例都有用。

【讨论】:

那么,编写与数据库一起使用的命令逻辑的最佳方法是什么,我们可以运行php artisan migratephp artisan migrate:refresh 我会将最后一个示例与 when() 方法一起使用。因此,仅在满足要求(在您的情况下存在特定表)时才执行该命令。如果您将代码封装在 $schedule->command() 或 $schedule->call() 中,则 php artisan migrate 和 php artisan migrate:refresh 完全不受影响 所以,这是摆脱这个错误的标准方法。在执行确切的命令逻辑之前检查表是否存在。对吗? 这是避免错误的唯一可能方法....但是如果您不使用示例中的 when() 函数。然后命令根本没有被调度(所以没有其他命令或进程有关于调度命令的信息)。如果您根本不需要,您的示例将起作用。 那么酷!这就是我想知道的!【参考方案5】:

如果还有人关心这个……

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


【讨论】:

以上是关于为啥 Laravel 总是在每个 Artisan 命令中调用 schedule()?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能使用 PHP artisan serve 运行我的 laravel 应用程序?

为啥 php artisan 使用 empty up 方法创建迁移类?

Laravel 的 Artisan 的无限递归

Laravel Php Artisan 服务错误

可以在生产 laravel 网站上运行 php artisan :clear 命令吗? [关闭]

Laravel Artisan Migrate 命令创建表但不将每个迁移文件填充到迁移表