> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 教程 - Laravel Console Starter

> 使用 Laravel Console Starter 构建控制台应用的完整流程。学习命令创建、GitHub Actions 调度、通知发送以及实际应用示例。

## 前言

在本教程中，我们将使用 `revolution/laravel-console-starter` 构建一个包含自定义 artisan 命令的 Laravel 应用。

使用这个 starter kit 的好处：

* **快速搭建** —— 无需复杂配置，就能立即开始开发控制台应用
* **Laravel 生态** —— 可以直接使用任务调度、数据库、通知等 Laravel 丰富的功能
* **Artisan 命令** —— 借助 Laravel 强大的 Artisan 系统，轻松创建和管理自定义命令
* **易于测试** —— 可以用 Laravel 的测试框架为命令编写测试

## 前置条件

请先安装以下软件。

* PHP `^8.3`
* Composer（[https://getcomposer.org/](https://getcomposer.org/)）
* Laravel Installer（`composer global require laravel/installer`）

## 项目搭建

<Steps>
  <Step title="创建新项目">
    在终端中执行以下命令。

    ```bash theme={null}
    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      # 命令注册
    ```
  </Step>

  <Step title="确认环境配置">
    默认情况下，邮件发送被配置为日志输出（`MAIL_MAILER=log`）。

    如果需要真正发送邮件，请在 `.env` 中配置 Mailgun、Postmark、SES 等服务。
  </Step>
</Steps>

## 创建控制台命令

<Steps>
  <Step title="生成命令">
    通过 `make:command` Artisan 命令创建新命令。

    ```bash theme={null}
    php artisan make:command YourCommandName --command=your:command
    ```

    * `YourCommandName` —— 要创建的命令类名（例如：`SendDailyReport`）
    * `your:command` —— 命令的调用名（例如：`report:send-daily`）

    会生成 `app/Console/Commands/YourCommandName.php`。
  </Step>

  <Step title="实现命令">
    编辑生成的文件来实现逻辑。下面是一个 Hello World 命令的示例。

    ```php theme={null}
    <?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()` 方法中编写逻辑。
  </Step>

  <Step title="执行命令">
    ```bash theme={null}
    php artisan hello:world
    ```

    执行后，`storage/logs/laravel.log` 和控制台都会输出对应消息。
  </Step>
</Steps>

## 用 GitHub Actions 做任务调度

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

<Steps>
  <Step title="查看 workflow 文件">
    Starter kit 中已预置了 `.github/workflows/cron.yml`。

    ```yaml theme={null}
    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
    ```
  </Step>

  <Step title="替换为自己的命令">
    将 `php artisan inspire` 替换为你自己的命令。

    ```yaml theme={null}
    - name: Run Command
      run: php artisan your:command
    ```

    要执行多个命令：

    ```yaml theme={null}
    - name: Run Commands
      run: |
        php artisan first:command
        php artisan second:command
    ```
  </Step>

  <Step title="调整调度频率">
    修改 `cron` 表达式来控制执行频率。

    | cron 表达式      | 执行时机       |
    | ------------- | ---------- |
    | `0 0 * * *`   | 每天 UTC 0 点 |
    | `0 */6 * * *` | 每 6 小时     |
    | `0 0 * * 1`   | 每周一 0 点    |
  </Step>

  <Step title="用 secrets 管理机密信息">
    API key、密码等敏感信息请使用 GitHub 仓库的 secrets 来管理。

    在 GitHub 仓库的 **Settings > Secrets and variables > Actions** 中添加 secrets，并在 workflow 中引用。

    ```yaml theme={null}
    - name: Run Command with Secrets
      run: php artisan your:command
      env:
        API_KEY: ${{ secrets.API_KEY }}
        DB_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
    ```
  </Step>
</Steps>

## 通知功能

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

<Steps>
  <Step title="创建通知类">
    ```bash theme={null}
    php artisan make:notification TaskCompleted
    ```

    会生成 `app/Notifications/TaskCompleted.php`。
  </Step>

  <Step title="配置通知通道">
    按需发布配置文件。

    ```bash theme={null}
    php artisan config:publish mail
    php artisan config:publish services
    ```
  </Step>

  <Step title="从命令中发送通知">
    ```php theme={null}
    <?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', 'admin@example.com')
                ->notify(new TaskCompleted('数据处理已成功完成'));

            return Command::SUCCESS;
        }
    }
    ```
  </Step>
</Steps>

<Info>
  详情请参考 [Laravel 通知文档](https://laravel.com/docs/notifications)。
</Info>

## 实用应用示例

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

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

<Steps>
  <Step title="安装 Slack 通知通道">
    ```bash theme={null}
    composer require laravel/slack-notification-channel
    ```
  </Step>

  <Step title="创建命令和通知类">
    ```bash theme={null}
    php artisan make:command MonitorWebsites --command=monitor:websites
    php artisan make:notification WebsiteDown
    ```
  </Step>

  <Step title="配置 Slack">
    ```bash theme={null}
    php artisan config:publish services
    ```

    在 `.env` 中添加 Slack webhook URL。

    ```
    SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
    ```

    更新 `config/services.php`。

    ```php theme={null}
    'slack' => [
        'webhook_url' => env('SLACK_WEBHOOK_URL'),
    ],
    ```
  </Step>

  <Step title="实现通知">
    编辑 `app/Notifications/WebsiteDown.php`。

    ```php theme={null}
    <?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());
                });
        }
    }
    ```
  </Step>

  <Step title="实现命令">
    编辑 `app/Console/Commands/MonitorWebsites.php`。

    ```php theme={null}
    <?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;
        }
    }
    ```
  </Step>

  <Step title="在 GitHub Actions 中调度">
    更新 `.github/workflows/cron.yml`。

    ```yaml theme={null}
    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 }}
    ```
  </Step>
</Steps>

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

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

<Steps>
  <Step title="安装 Discord 通知通道">
    ```bash theme={null}
    composer require revolution/laravel-notification-discord-webhook
    ```
  </Step>

  <Step title="创建命令与通知">
    ```bash theme={null}
    php artisan make:command UpdateCryptoPortfolio --command=crypto:portfolio
    php artisan make:notification CryptoPortfolioUpdate
    ```
  </Step>

  <Step title="配置 Discord">
    在 `.env` 中添加 Discord webhook URL。

    ```
    DISCORD_WEBHOOK=https://discord.com/api/webhooks/YOUR/WEBHOOK
    ```

    更新 `config/services.php`。

    ```php theme={null}
    'discord' => [
        'webhook' => env('DISCORD_WEBHOOK'),
    ],
    ```
  </Step>

  <Step title="实现命令并执行">
    详细实现请参考 [GitHub 仓库中的教程](https://github.com/invokable/laravel-console-starter/blob/main/docs/tutorial_ja.md)。

    ```bash theme={null}
    php artisan crypto:portfolio
    ```
  </Step>
</Steps>

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

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

<Steps>
  <Step title="创建命令与通知">
    ```bash theme={null}
    php artisan make:command WebScraper --command=scrape:website
    php artisan make:notification ScrapingCompleted
    ```
  </Step>

  <Step title="配置邮件">
    ```bash theme={null}
    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
    MAIL_FROM_ADDRESS=your-app@example.com
    MAIL_FROM_NAME="${APP_NAME}"
    ```
  </Step>

  <Step title="实现命令并执行">
    ```php theme={null}
    protected $signature = 'scrape:website {--url=https://example.com} {--email=admin@example.com}';

    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;
    }
    ```

    ```bash theme={null}
    php artisan scrape:website --email=admin@example.com
    ```
  </Step>
</Steps>

## 后续步骤

至此，你已经掌握了使用 `revolution/laravel-console-starter` 构建 Laravel 控制台应用的基础。

若想进一步深入：

* **探索 Laravel 文档** —— 在 [Laravel 官方文档](https://laravel.com/docs) 中查找能强化控制台应用的功能
* **实现测试** —— 使用 Laravel 的测试框架为命令编写测试，提高可靠性
* **考虑做成扩展包** —— 如果你要在多个项目中重用类似命令，可以考虑发布为可复用的 Laravel 包
* **保持信息更新** —— 关注 [Laravel News](https://laravel-news.com/) 和官方博客，及时掌握最佳实践


## Related topics

- [Laravel Console Starter](/zh/packages/laravel-console-starter/index.md)
- [Bot 教程 - Laravel Bluesky](/zh/packages/laravel-bluesky/bot-tutorial.md)
- [如何创建 Laravel Starter Kit](/zh/advanced/starter-kit-creation.md)
- [目录结构](/zh/directory-structure.md)
- [控制台测试](/zh/console-tests.md)
