跳转到主要内容

前言

在本教程中,我们将使用 revolution/laravel-console-starter 构建一个包含自定义 artisan 命令的 Laravel 应用。 使用这个 starter kit 的好处:
  • 快速搭建 —— 无需复杂配置,就能立即开始开发控制台应用
  • Laravel 生态 —— 可以直接使用任务调度、数据库、通知等 Laravel 丰富的功能
  • Artisan 命令 —— 借助 Laravel 强大的 Artisan 系统,轻松创建和管理自定义命令
  • 易于测试 —— 可以用 Laravel 的测试框架为命令编写测试

前置条件

请先安装以下软件。

项目搭建

1

创建新项目

在终端中执行以下命令。
laravel new my-app --using=revolution/laravel-console-starter --no-interaction
这会创建 my-app 目录,并搭建好控制台应用的基础结构。安装时会自动完成:
  • .env.example 生成 .env 文件
  • 执行 php artisan key:generate 设置应用密钥
主要目录结构:
my-app/
├── app/Console/Commands/   # 自定义命令
├── .github/workflows/
│   └── cron.yml            # GitHub Actions 定时示例
├── config/                 # 应用配置
└── routes/console.php      # 命令注册
2

确认环境配置

默认情况下,邮件发送被配置为日志输出(MAIL_MAILER=log)。如果需要真正发送邮件,请在 .env 中配置 Mailgun、Postmark、SES 等服务。

创建控制台命令

1

生成命令

通过 make:command Artisan 命令创建新命令。
php artisan make:command YourCommandName --command=your:command
  • YourCommandName —— 要创建的命令类名(例如:SendDailyReport
  • your:command —— 命令的调用名(例如:report:send-daily
会生成 app/Console/Commands/YourCommandName.php
2

实现命令

编辑生成的文件来实现逻辑。下面是一个 Hello World 命令的示例。
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class HelloWorldCommand extends Command
{
    protected $signature = 'hello:world';
    protected $description = 'Displays a hello world message in the log';

    public function handle(): int
    {
        Log::info('Hello, World from Artisan Command!');
        $this->info('Hello, World message has been logged.');
        return Command::SUCCESS;
    }
}
$signature 中定义命令的调用名,在 handle() 方法中编写逻辑。
3

执行命令

php artisan hello:world
执行后,storage/logs/laravel.log 和控制台都会输出对应消息。

用 GitHub Actions 做任务调度

借助 GitHub Actions,不用服务器上的 cron 任务,也能定期执行命令。
1

查看 workflow 文件

Starter kit 中已预置了 .github/workflows/cron.yml
name: cron

on:
  schedule:
    - cron: '0 0 * * *'  # 每天 UTC 0 点

jobs:
  cron:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: shivammathur/setup-php@v2
        with:
          php-version: 8.5
          coverage: none
      - run: composer install --no-dev -q
      - run: cp .env.example .env
      - run: php artisan key:generate
      - run: php artisan inspire
2

替换为自己的命令

php artisan inspire 替换为你自己的命令。
- name: Run Command
  run: php artisan your:command
要执行多个命令:
- name: Run Commands
  run: |
    php artisan first:command
    php artisan second:command
3

调整调度频率

修改 cron 表达式来控制执行频率。
cron 表达式执行时机
0 0 * * *每天 UTC 0 点
0 */6 * * *每 6 小时
0 0 * * 1每周一 0 点
4

用 secrets 管理机密信息

API key、密码等敏感信息请使用 GitHub 仓库的 secrets 来管理。在 GitHub 仓库的 Settings > Secrets and variables > Actions 中添加 secrets,并在 workflow 中引用。
- name: Run Command with Secrets
  run: php artisan your:command
  env:
    API_KEY: ${{ secrets.API_KEY }}
    DB_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}

通知功能

你可以通过邮件、Slack 等通道通知命令的执行结果或错误。
1

创建通知类

php artisan make:notification TaskCompleted
会生成 app/Notifications/TaskCompleted.php
2

配置通知通道

按需发布配置文件。
php artisan config:publish mail
php artisan config:publish services
3

从命令中发送通知

<?php

namespace App\Console\Commands;

use App\Notifications\TaskCompleted;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Notification;

class ProcessDataCommand extends Command
{
    protected $signature = 'data:process';
    protected $description = '处理数据,完成时发送通知';

    public function handle()
    {
        $this->info('正在处理数据...');

        // 处理逻辑...

        Notification::route('mail', '[email protected]')
            ->notify(new TaskCompleted('数据处理已成功完成'));

        return Command::SUCCESS;
    }
}
详情请参考 Laravel 通知文档

实用应用示例

示例 1:网站健康监控与 Slack 报警

一个用来检查网站是否在线、在检测到问题时向 Slack 发送报警的命令。
1

安装 Slack 通知通道

composer require laravel/slack-notification-channel
2

创建命令和通知类

php artisan make:command MonitorWebsites --command=monitor:websites
php artisan make:notification WebsiteDown
3

配置 Slack

php artisan config:publish services
.env 中添加 Slack webhook URL。
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
更新 config/services.php
'slack' => [
    'webhook_url' => env('SLACK_WEBHOOK_URL'),
],
4

实现通知

编辑 app/Notifications/WebsiteDown.php
<?php

namespace App\Notifications;

use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;

class WebsiteDown extends Notification
{
    public function __construct(
        protected string $website,
        protected string $error,
    ) {}

    public function via($notifiable): array
    {
        return ['slack'];
    }

    public function toSlack($notifiable): SlackMessage
    {
        return (new SlackMessage)
            ->error()
            ->content('网站宕机报警!')
            ->attachment(function ($attachment) {
                $attachment->title($this->website)
                           ->content("错误: {$this->error}")
                           ->timestamp(now());
            });
    }
}
5

实现命令

编辑 app/Console/Commands/MonitorWebsites.php
<?php

namespace App\Console\Commands;

use App\Notifications\WebsiteDown;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;

class MonitorWebsites extends Command
{
    protected $signature = 'monitor:websites {--timeout=10}';
    protected $description = '检查网站是否可用,当宕机时通过 Slack 发送报警';

    protected array $websites = [
        'https://example.com',
        'https://yourwebsite.com',
    ];

    public function handle(): int
    {
        $timeout = (int) $this->option('timeout');
        $webhookUrl = config('services.slack.webhook_url');
        $hasErrors = false;

        foreach ($this->websites as $website) {
            try {
                $response = Http::timeout($timeout)->get($website);
                if ($response->successful()) {
                    $this->info("{$website}: 在线 ✓");
                } else {
                    $error = "HTTP 状态: " . $response->status();
                    $this->error("{$website}: 宕机 ({$error})");
                    Notification::route('slack', $webhookUrl)
                        ->notify(new WebsiteDown($website, $error));
                    $hasErrors = true;
                }
            } catch (\Exception $e) {
                $this->error("{$website}: 错误 ({$e->getMessage()})");
                Log::error("{$website} 检查失败", ['error' => $e->getMessage()]);
                Notification::route('slack', $webhookUrl)
                    ->notify(new WebsiteDown($website, $e->getMessage()));
                $hasErrors = true;
            }
        }

        return $hasErrors ? Command::FAILURE : Command::SUCCESS;
    }
}
6

在 GitHub Actions 中调度

更新 .github/workflows/cron.yml
name: Website Monitoring

on:
  schedule:
    - cron: '*/15 * * * *'  # 每 15 分钟执行

jobs:
  monitor:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: shivammathur/setup-php@v2
        with:
          php-version: 8.5
      - run: composer install --no-dev -q
      - run: cp .env.example .env
      - run: php artisan key:generate
      - name: Monitor Websites
        run: php artisan monitor:websites
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

示例 2:加密资产组合更新与 Discord 通知

一个用来从 CoinGecko API 获取加密资产价格、并通过 Discord webhook 推送更新的命令。
1

安装 Discord 通知通道

composer require revolution/laravel-notification-discord-webhook
2

创建命令与通知

php artisan make:command UpdateCryptoPortfolio --command=crypto:portfolio
php artisan make:notification CryptoPortfolioUpdate
3

配置 Discord

.env 中添加 Discord webhook URL。
DISCORD_WEBHOOK=https://discord.com/api/webhooks/YOUR/WEBHOOK
更新 config/services.php
'discord' => [
    'webhook' => env('DISCORD_WEBHOOK'),
],
4

实现命令并执行

详细实现请参考 GitHub 仓库中的教程
php artisan crypto:portfolio

示例 3:网站内容爬取与邮件通知

从网站爬取内容并通过邮件发送结果的命令。
1

创建命令与通知

php artisan make:command WebScraper --command=scrape:website
php artisan make:notification ScrapingCompleted
2

配置邮件

php artisan config:publish mail
.env 中添加邮件配置。
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_username
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=tls
[email protected]
MAIL_FROM_NAME="${APP_NAME}"
3

实现命令并执行

protected $signature = 'scrape:website {--url=https://example.com} {[email protected]}';

public function handle(): int
{
    $url = $this->option('url');
    $email = $this->option('email');

    $response = Http::timeout(30)->get($url);

    // 处理内容并发送通知...
    Notification::route('mail', $email)
        ->notify(new ScrapingCompleted(true, $url, $title, $content));

    return Command::SUCCESS;
}
php artisan scrape:website [email protected]

后续步骤

至此,你已经掌握了使用 revolution/laravel-console-starter 构建 Laravel 控制台应用的基础。 若想进一步深入:
  • 探索 Laravel 文档 —— 在 Laravel 官方文档 中查找能强化控制台应用的功能
  • 实现测试 —— 使用 Laravel 的测试框架为命令编写测试,提高可靠性
  • 考虑做成扩展包 —— 如果你要在多个项目中重用类似命令,可以考虑发布为可复用的 Laravel 包
  • 保持信息更新 —— 关注 Laravel News 和官方博客,及时掌握最佳实践
最后修改于 2026年7月13日