[Laravel8.16.0] Mailgunを実装する

$ composer require guzzlehttp/guzzle

env

MAIL_DRIVER=mailgun
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@sandbox*.mailgun.org
MAIL_PASSWORD=*
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"

config/mail.php
-> 特に変更なし

config/service.php

'mailgun' => [
        'domain' => env('*'),
        'secret' => env('*'),
        'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
    ],

来た! なんか色々戸惑ったけど、OK

sandbox.mgsend.net経由でもそんなに目立って悪くないように思うが、mailgunのドメイン設定もやりたい。
というか、腹減ったー 飯食うの忘れてた。

[PHP 7.4.11] Mailgunでメール送信

前提: mailgunにアカウントを登録しておく

$ composer require mailgun/mailgun-php kriswallsmith/buzz nyholm/psr7

require 'vendor/autoload.php';
use Mailgun\Mailgun;

$mgClient = Mailgun::create('${api key}');
$domain = "sandbox*.mailgun.org";

$result = $mgClient->messages()->send($domain, array(
    'from'    => 'info@hpscript.com',
    'to'      => '*@gmail.com',
    'subject' => 'Hello',
    'text'    => 'Testing some Mailgun awesomness!'
));

まじかよ。Github通りにやると上手くいく。

mailgun/mailgun-php だけでなく、kriswallsmith/buzz、nyholm/psr7もインストールしないと動かないから注意が必要。

OK、これをLaravelに実装する。

[Laravel8.16.0] Mailtrapでメール送信

$ composer require guzzlehttp/guzzle

config/mail.php

    'from' => [
        'address' => env('MAIL_FROM_ADDRESS', 'test@hpscript.com'),
        'name' => env('MAIL_FROM_NAME', 'Hpscript'),
    ],

.env

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=${userName}
MAIL_PASSWORD=${password}
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"

$ php artisan make:mail TestMail
app/Mail/TestMail.php

    public function build()
    {
        return $this
                ->from("test@hpscript")
                ->subject("test send")
                ->view('email.test');
    }

resources/views/email/test.blade.php

test mail body

$ php artisan make:controller –resource MailController

Route

Route::get('/mail', [App\Http\Controllers\MailController::class, 'index']);

app/Http/Controllers/MailController.php

use Illuminate\Support\Facades\Mail;
use App\Mail\TestMail;

public function index()
    {
        //
        Mail::to('test@example.com')
        ->send(new TestMail());
    }

mailtrapはいいけど、実際に送れないと意味ない。
メール設計書を作成して、mailgunを使っていきます。

[Laravel8.16.0] CSVをimportする

1.fopenして、fputcsvで作成する手法。

public function csv(){
        $ary = [
            ["山田", "12", "O",""],
            ["田中", "20", "A",""],
            ["吉田", "18", "AB",""],
            ["伊藤", "19", "B", "エンジニア"]
        ]; 

        foreach($ary as $key => $value){
             array_splice($ary[$key], 1, 0, "");
        }

        $column = ["名前", "無記入", "年齢", "血液型", "備考"];
        array_unshift($ary, $column);

        $filename = "hogehoge.csv";
        $f = fopen($filename, "w");
        stream_filter_prepend($f,'convert.iconv.utf-8/cp932');
        if($f) {
            foreach($ary as $line){
                fputcsv($f, $line);
            }
        }
        header('Content-Type: application/octet-stream');
        header('Content-Length: '.filesize($filename));
        header('Content-Disposition: attachment; filename=hogehoge.csv');

        readfile($filename);
    }

2. StreamedResponseを使う書き方

public function csv(){
        $data = User::all(); 
        $response = new StreamedResponse(function() use($data){
                $stream = fopen('php://output', 'w');
                stream_filter_prepend($stream, 'convert.iconv.utf-8/cp932//TRANSLIT');
                fputcsv($stream, ['id','name','email',]);
                foreach($data as $user){
                     fputcsv($stream, [$user['id'],$user['name'],$user['email'],]);
                }
                fclose($stream);
        });
        $response->headers->set('Content-Type', 'application/octet-stream');
        $response->headers->set('Content-Disposition', 'attachment; filename="user.csv"');
        return $response;
    }

やってることは一緒なんだけど、StreamedResponseの方が良い。

[Laravel8.16.0] バッチ処理を実装する

$ php artisan make:command TestBatch

-> app/Console/Commands に TestBatch.phpが出来ます。

protected $signature = 'batch:test';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        echo "hello";
    }

$ php artisan list
batch
batch:test Command description

$ php artisan batch:test
hello

あとはsudo vi /etc/crontab で、$ php artisan batch:testを実行すればいいだけ。
そうだ、思い出した。

[Laravel8.16.0] S3へのアップロード・取得

// まずシンボリックリンクを貼ります
$ php artisan storage:link

// 続いてflysystem install
$ composer require league/flysystem-aws-s3-v3 ~1.0
$ composer require league/flysystem-cached-adapter ~1.0

.envの設定

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=

config/filesystem.php

'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

controller test
-> 取得できているか確認。filesystemでstorage_path(‘app’)となっているので、storage/app/にテストファイルを置く

public function file(){
        $file = Storage::get('test.jpg');
        dd($file);
    }

putFileAs

public function file(){
        $file1 = storage_path('app/test.jpg');
        $filename = 'hoge.jpg';
        Storage::disk('s3')->putFileAs('/', $file1, $filename, 'public');
        echo "done";
    }

ファイルパスの取得

public function file(){
        $path = Storage::disk('s3')->url('test.jpg');
        echo $path;
    }

あ、思い出した。

jpg, webp, tiff, bmpの違い

jpg: Chrome, Edge, Firefox, IE, Opera, Safari
L .jpg, .jpeg, .jfif, .pjpeg, .pjp
・フルカラー1670万色を表現できる
・非可逆圧縮の画像形式
・背景の透過は行えない

png: Chrome, Edge, Firefox, IE, Opera, Safari
L .png
・フルカラー1670万色を表現できる
・可逆圧縮の画像形式
・背景透過も可能

GIF: Chrome, Edge, Firefox, IE, Opera, Safari
L .gif
・単純な画像やアニメーションに適している

webp: Chrome, Edge, Firefox, Opera, Safari
L .webp
・画像とアニメーションの両方に最適
・WebP は PNG や JPEG よりもはるかに優れた圧縮を提供し、より高い色深度、アニメーションフレーム、透明度などに対応

TIFF: Safari ※webでは非推奨
L .tif, .tiff
・高い解像度が必要な場合に用いられる

BMP: Chrome, Edge, Firefox, IE, Opera, Safari ※webでは非推奨
L .bmp
・Microsoft Windowsのために作られた画像形式
・圧縮処理がされていない大きい画像

SVG: Chrome, Edge, Firefox, IE, Opera, Safari
L .svg
・ベクター画像形式
・異なる大きさで正確に描画する必要があるユーザーインターフェース要素、アイコン、図などに最適

jpg, webp, tiff, bmpで表示

	<img src="img/test.jpg" width="120px" height="80px">
	<img src="img/test.webp" width="120px" height="80px">
	<img src="img/test.tiff" width="120px" height="80px">
	<img src="img/test.bmp" width="120px" height="80px">

tiffはsafariでないと表示されないです。

[AWS S3] バケットの画像パス一覧を取得する

S3に画像が置いてあり、view側では画像のファイル名のみわかっており、拡張子が不明だった場合に、view側からS3に画像があるか判定して、画像があれば拡張子を取得して表示する、という仕組みを作りたかった。

globやfile_get_contentsでワイルドカードを使って拡張子を取得しようとしたが上手くいかない。
よって、画像パス一覧を取得してDBに格納する方法に切り替える。

### バケットの画像パス一覧を取得

require_once "./vendor/autoload.php";

$prefix = "test/";
$s3client = new Aws\S3\S3Client([
    'credentials' => [
        'key' => $key,
        'secret' => $secret,
    ],
    'region' => 'ap-northeast-1',
    'version' => 'latest',
]);

$objects = $s3client->listObjects([
	'Bucket' => $bucket,
	'Prefix' => $prefix,
	'Delimiter' => "/"
]);

$list = array();
foreach($objects["Contents"] as $object) {
    if(substr($object['Key'],-1,1) != "/"){
        array_push($list, substr($object['Key'], 5));
    }
}

print("<pre>");
var_dump($list);
print("</pre>");

ここからDBにpdoで入れる方法を考えます。

[AWS AIM&S3] 特定のバケットのみ操作を許可するポリシー

1. ポリシー作成
まずrootユーザでpolicyを作成します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
          "arn:aws:s3:::${BucketName}",
          "arn:aws:s3:::${BucketName}/*"
      ]
    }
  ]
}

2. ユーザ作成
続いて、ユーザに作成したpolicyをattachします。

3. ログイン
https://aws.amazon.com/jp/console/
アカウントID:
ユーザ名:
パスワード:

!!Danger!!
ログインして、S3に行こうとすると、「You don’t have permissions to list buckets」と表示される。
※jsonが間違っている訳ではないので注意が必要

以下のURLに直接アクセスしないとbucketは表示されない。
https://s3.console.aws.amazon.com/s3/buckets/${BucketName}?region=ap-northeast-1&tab=objects

あせったーーーーーーー
さー手順書作るかー

[aws/aws-sdk-php 3.166.2] 複数画像をphpでS3にアップロード

$ composer require aws/aws-sdk-php

require_once "vendor/autoload.php";

$bucket = '';
$key = '';
$secret = '';

$s3client = new Aws\S3\S3Client([
    'credentials' => [
        'key' => $key,
        'secret' => $secret,
    ],
    'region' => 'ap-northeast-1',
    'version' => 'latest',
]);

$file = "img/flower.jpg";

$result = $s3client->putObject(array(
	'Bucket' => $bucket,
	'Key' => 'test/flower.jpg',
	'ACL' => 'public-read',
	'SourceFile' => $file,
	'ContentType' => mime_content_type($file),
));

echo "<pre>";
var_dump($result);
echo "</pre>";

これをファイル複数一括で行いたい。
複数ファイルの取得はglobだったかな。

foreach(glob("img/*.*") as $file) {
    $files[] = $file;
}

$s3client = new Aws\S3\S3Client([
    'credentials' => [
        'key' => $key,
        'secret' => $secret,
    ],
    'region' => 'ap-northeast-1',
    'version' => 'latest',
]);

foreach($files as $file){
		$result[] = $s3client->putObject(array(
		'Bucket' => $bucket,
		'Key' => $file,
		'ACL' => 'public-read',
		'SourceFile' => $file,
		'ContentType' => mime_content_type($file),
	));
}

echo "<pre>";
var_dump($result);
echo "</pre>";

ちょっと時間かかるな。手動でやった方が良さげ。