■P212

────────────────
sail artisan make:command SampleCommand
────────────────

●app/Console/Commands/SampleCommand.php
────────────────
<?php

namespace App\Console\Commands;

use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;

#[Signature('app:sample-command')]
#[Description('Command description')]
class SampleCommand extends Command
{
    /**
     * Execute the console command.
     */
    public function handle()
    {
        //
    }
}
────────────────


■P213

●app/Console/Commands/SampleCommand.php
────────────────
<?php

namespace App\Console\Commands;

use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;

#[Signature('sample-command')]
#[Description('Sample Command')]
class SampleCommand extends Command
{
    /**
     * Execute the console command.
     */
    public function handle()
    {
        echo 'このコマンドはサンプルです。';

        return Command::SUCCESS;
    }
}
────────────────

────────────────
sail artisan list
────────────────


■P214

────────────────
sail artisan sample-command
────────────────


■P215

●bootstrap/app.php
────────────────
<?php

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withSchedule(function (Schedule $schedule) {
        // 毎分
        $schedule->command('sample-command')->everyMinute();
        // 毎時
        $schedule->command('sample-command')->hourly();
        // 毎時8分
        $schedule->command('sample-command')->hourlyAt(8);
        // 毎日
        $schedule->command('sample-command')->daily();
        // 毎日13時
        $schedule->command('sample-command')->dailyAt('13:00');
        // 毎日3:15(cron表記)
        $schedule->command('sample-command')->cron('15 3 * * *');
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
────────────────


■P216

────────────────
sail artisan schedule:list
────────────────

●config/app.php
────────────────
   /*
    |--------------------------------------------------------------------------
    | Application Timezone
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default timezone for your application, which
    | will be used by the PHP date and date-time functions. The timezone
    | is set to "UTC" by default as it is suitable for most use cases.
    |
    */

    'timezone' => 'Asia/Tokyo',
────────────────


■P217

────────────────
sail artisan schedule:run
────────────────

────────────────
sail artisan make:mail DailyTweetCount
────────────────


■P218

●app/Mail/DailyTweetCount.php
────────────────
<?php

namespace App\Mail;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class DailyTweetCount extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public User $toUser;

    public int $count;

    /**
     * Create a new message instance.
     */
    public function __construct(User $toUser, int $count)
    {
        $this->toUser = $toUser;
        $this->count = $count;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: "昨日は{$this->count}件のつぶやきが追加されました！",
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            markdown: 'email.daily_tweet_count',
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}
────────────────


■P219

●resources/views/email/daily_tweet_count.blade.php
────────────────
@component('mail::message')

# 昨日は{{ $count }}件のつぶやきが追加されました！

{{ $toUser->name }}さんこんにちは！

昨日は{{ $count }}件のつぶやきが追加されましたよ！最新のつぶやきを見に行きましょう。

@component('mail::button', ['url' => route('tweet.index')])
    つぶやきを見に行く
@endcomponent

@endcomponent
────────────────


■P220

────────────────
sail artisan make:command SendDailyTweetCountMail
────────────────

●app/Console/Commands/SendDailyTweetCountMail.php
────────────────
<?php

namespace App\Console\Commands;

use App\Mail\DailyTweetCount;
use App\Models\User;
use App\Services\TweetService;
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;
use Illuminate\Contracts\Mail\Mailer;

#[Signature('mail:send-daily-tweet-count-mail')]
#[Description('前日のつぶやき数を集計してつぶやきを促すメールを送ります。')]
class SendDailyTweetCountMail extends Command
{
    private TweetService $tweetService;

    private Mailer $mailer;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(TweetService $tweetService, Mailer $mailer)
    {
        parent::__construct();
        $this->tweetService = $tweetService;
        $this->mailer = $mailer;
    }

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $tweetCount = $this->tweetService->countYesterdayTweets();

        $users = User::get();

        foreach ($users as $user) {
            $this->mailer->to($user->email)
                ->send(new DailyTweetCount($user, $tweetCount));
        }

        return 0;
    }
}
────────────────


■P221

●app/Services/TweetService.php
────────────────
<?php

namespace App\Services;

use App\Models\Tweet;
use Carbon\Carbon;

class TweetService
{
    public function getTweets()
    {
        return Tweet::query()->orderBy('created_at', 'DESC')->get();
    }

    public function checkOwnTweet(int $userId, int $tweetId): bool
    {
        $tweet = Tweet::where('id', $tweetId)->first();
        if (! $tweet) {
            return false;
        }

        return $tweet->user_id === $userId;
    }

    public function countYesterdayTweets(): int
    {
        return Tweet::whereDate('created_at', '>=', Carbon::yesterday()->toDateTimeString())
            ->whereDate('created_at', '<', Carbon::today()->toDateTimeString())
            ->count();
    }
}
────────────────


■P222

●database/factories/TweetFactory.php
────────────────
<?php

namespace Database\Factories;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Tweet>
 */
#[UseModel(Tweet::class)]
class TweetFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'user_id' => 1,
            'content' => $this->faker->realText(100),
            'created_at' => Carbon::now()->yesterday(),
        ];
    }
}
────────────────


■P223

────────────────
sail artisan migrate:fresh --seed
────────────────

────────────────
sail artisan mail:send-daily-tweet-count-mail
────────────────

────────────────
sail artisan queue:work --once
────────────────


■P224

●bootstrap/app.php
────────────────
<?php

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withSchedule(function (Schedule $schedule): void {
        $schedule->command('mail:send-daily-tweet-count-mail')
            ->dailyAt('11:00');
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
────────────────


■P225

────────────────
sail artisan schedule:list
────────────────
