> ## 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 建置 console 應用程式的步驟。學習指令建立、GitHub Actions 排程、通知發送與實務應用範例。

## 前言

本教學將透過 `revolution/laravel-console-starter` 建置具備自訂 artisan 指令的 Laravel 應用程式。

使用此 starter kit 的好處：

* **快速上手** — 無需複雜設定即可立即開始 console 應用開發
* **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` 目錄，並設定 console 應用的基本結構。

    安裝時會自動執行：

    * 從 `.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="確認環境設定">
    預設情況下，郵件寄送設定為輸出至 log（`MAIL_MAILER=log`）。

    若需要實際寄送郵件，請在 `.env` 檔中設定 Mailgun、Postmark、SES 等服務。
  </Step>
</Steps>

## 建立 Console 指令

<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` 與 console 輸出訊息。
  </Step>
</Steps>

## 透過 GitHub Actions 進行任務排程

使用 GitHub Actions 可以無需伺服器 cron job 就定期執行指令。

<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="以 secret 管理機密資訊">
    API 金鑰或密碼等機密資訊請使用 GitHub 儲存庫 secret。

    在 GitHub 儲存庫的 **Settings > Secrets and variables > Actions** 新增 secret，並於 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>

## 通知功能

可透過 Email、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：網站內容抓取與郵件通知

從網站抓取內容並以 Email 傳送結果的指令。

<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 console 應用程式的基礎。

若要進一步深入學習：

* **探索 Laravel 文件** — 於 [Laravel 官方文件](https://laravel.com/docs) 了解可強化 console 應用的功能
* **實作測試** — 以 Laravel 測試框架建立指令的測試，確保可靠度
* **考慮套件開發** — 若在多個專案中會使用類似指令，可考慮發佈為可重複使用的 Laravel 套件
* **掌握最新資訊** — 追蹤 [Laravel News](https://laravel-news.com/) 或官方部落格，隨時掌握最佳實踐


## Related topics

- [Laravel Console Starter](/zh-TW/packages/laravel-console-starter/index.md)
- [起始套件（Starter Kit）](/zh-TW/starter-kits.md)
- [Bot 教學 - Laravel Bluesky](/zh-TW/packages/laravel-bluesky/bot-tutorial.md)
- [主控台測試](/zh-TW/console-tests.md)
- [目錄結構](/zh-TW/directory-structure.md)
