Laravel の Storage で disk として S3 を指定したときに deleteDirectory できない
LaravelにはStorageというファサードがあり、ローカルだろうがS3だろうが簡単ファイル操作を簡単に行うことができ、とても便利です。
例えば、localにファイルを設置したい場合は、
Storage::disk('local')->putFile('', $file);
などとすることができ、localの特定のディレクトリを丸々削除したい場合は、
Storage::disk('local')->deleteDirectory('target_dir');
と書けます。
上記はlocalでしたが、S3を対象にしたい場合には、
Storage::disk('s3')->putFile('', $file); Storage::disk('s3')->deleteDirectory('target_dir');
と local
→ s3
にするだけで良いです。
もちろん、S3上の操作なので、S3側で権限の設定は必要です。
putFile
のために s3:PutObject
の権限が必要だということは感覚的にわかりますが、 deleteDirectory
のためにはどんな権限が必要なのでしょう?
deleteに関することなので s3:DeleteObject
のような気がするのですが、これで実行すると、以下のようなエラーが発生します。
Error executing "ListObjects" on "https://〜〜"; AWS HTTP error: Client error: `GET https://〜` resulted in a `403 Forbidden` response: <?xml version="1.0" encoding="UTF-8"?> <Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>〜 (truncated...) AccessDenied (client): Access Denied - <?xml version="1.0" encoding="UTF-8"?> <Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>〜</RequestId><HostId>〜</HostId></Error>
正解は、対象バケットに対して s3:ListBucket
の権限が必要です。
deleteDirectory
の内部では、S3ClientInterface::deleteMatchingObjectsAsync() が呼ばれており、そこで ListObjects
らしきことが行われています。
S3には s3:ListObjects
といった権限がなく、結局どうすれば良いかわからなかったのですが、PHPもLaravelも関係ない、以下の記事に助けられました。
dev.classmethod.jp
まとめ。
Laravel の Storage(S3) で deleteDirectory する場合には、 S3 の対象バケットの s3:ListBucket
の権限が必要。
Laravel の Storage で S3 の特定のディレクトリをプレフィックスとして固定したい
LaravelにはStorage というファサードがあり、ローカルだろうがS3だろうが簡単ファイル操作を簡単に行うことができ、とても便利です。
config/filesystems.php
のdisksにはデフォルトで以下のような設定があります。
'disks' => [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), ], 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'url' => env('APP_URL') . '/storage', 'visibility' => 'public', ], 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), ], ],
.env
にAWSの設定をして、アクセス制限がしっかりしていれば、
Storage::disk('s3')->putFile('photos', new File('/path/to/photo'));
とするだけで、 S3上のAWS_BUCKET
バケットのphotos
ディレクトリに/path/to/photo
のファイルがアップロードされます。
簡単で便利ですが、そのプロジェクトで使うディレクトリがphotos
限定であったとき、毎回指定するのが面倒です。
public
の場合はroot
というオプションで設定できるようですが、s3
の場合にどうすれば良いのかわかりませんでした。
調べても情報はあんまり無かったのですが、山勘でやった結果うまく設定できました。
public
と同じようにroot
を設定するだけです。
'photos_s3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'root' => 'photos', 'url' => env('AWS_URL'), ],
これで、
Storage::disk('photos_s3')->putFile('', new File('/path/to/photo'));
とするだけで、先程と同じことが実現できます。
AtCoderで競技プログラミングというものに触れて半年が経った
はじめに
AtCoderで競技プログラミングを始めてから半年が経ったので久しぶりに中間報告としてアウトプットします。
開始3ヶ月時点でのブログはこちらです。 ohshige.hatenablog.com
開始1ヶ月時点でのブログはこちらです。 ohshige.hatenablog.com
現状
現時点(2019/08/12)での数字は以下の通りです。
項目 | 結果 | 3ヶ月時点との差分 |
---|---|---|
順位 | 6223rd | ↑87 |
Rating | 1076 (暫定) | ↑93 |
コンテスト参加回数 | 13 | +5 |
Accepted | 367 | +116 |
Rated Point Sum | 54600 | +4800 |
Longest Streak | 117 days | +26 |
Current Streak | 65 days | - |
しばらくかなり忙しくしていたため、AC数もコンテスト参加数もあまり増えていません。
参加したコンテストも成績が芳しくなく、レートの伸びも悪いです。
が、着実に少しずつレートは高くなっているので、とりあえず水色を目指していきます。
途中、Streakが途切れてしまったことがかなり悲しかったです。
どれだけ忙しくても絶対に1問は解いていたのに、ある日だけすっぽりと抜けてしまっていました。
忙しくてできなかったわけではなく、ポカで忘れてしまっていたことが何よりも残念です。
(そこで投げ出すことなくゼロから始められて自分偉いなと思っています)
明らかにACペースは落ちていますが、ABC-Aに関しては 129 / 137
まで来ていて、もうあと少しです。
引き続き頑張ります。
続けてみて
毎日本気で勉強しているわけではなく、解ける問題を解いているだけで、解説を見てもわからないような問題は基本的に放置しています。
絶対に良くない続け方ではありますが、続けられるだけマシかなと思います。
こういうとき、大学時代から始めていて周りに同じように勉強している友人がいればわからない問題や解説があったときに誰かに聞けたかもしれないですが、今ではそれができないので残念です。
わからない解説は放置してると言いつつも、理解できる解説はあるので少しづつ着実に成長はしています。
最近だと優先度付きキューを覚えました。
DPはまだ全然応用できないです。
こんな感じで少しずつつ覚えていければと思います。
前回のブログで、知人が2人始めてくれたと述べましたが、おそらくその2人はもうやっていないでしょう。
残念です。
おわりに
引き続きゆっくりと進めていきたいと思います。
半年以上の継続という目標は達成できたので、ABCの過去問を全て解く、水色を目指す、コンテストへの15回以上の参加という残りの3つの目標を達成できるように頑張ります。
PhpStorm で Laravel の Blade 内でもエンティティ等の補完をしてほしい
PhpStormにおけるLaravelの補完といえば laravel-ide-helper ですが、Blade内でエンティティ等の補完をする方法がわからなかったので調べました。
結論としては、これです。 blog.jetbrains.com
例えば、以下のようなエンティティがあったとします。
<?php declare(strict_types=1); namespace App\Entity; final class User { private $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } }
そして、ControllerでUser
エンティティの配列を渡してBladeを呼び出したとします。
Bladeは以下の通りです。
@extends('layouts.app') @section('content') <h1>ユーザ一覧</h1> <ul> @foreach($users as $user) <li>{{ $user->getName() }}</li> @endforeach </ul> @endsection
この場合、PhpStormが$users
がUser
エンティティの配列であることを認識していないので、{{ $user->getName() }}
で補完が効きません。
そこで、Bladeを以下の通りに修正します。
@extends('layouts.app') @php /** @var App\Entity\User[] $users */ @endphp @section('content') <h1>ユーザ一覧</h1> <ul> @foreach($users as $user) <li>{{ $user->getName() }}</li> @endforeach </ul> @endsection
@php
ディレクティブを使って(<?php ~ ?>
でも良いですが)、そこにPHPDocコメントを書くだけです。
微妙にダサい気もしますが、補完が効かずにtypoやアクセスできないメソッドを呼んでしまうよりは良いかと思います。
PHPでは未定義でも配列の代入ができる
PHPでは、変数をあらかじめ宣言していなくてもエラー無しでいきなり配列の代入ができるということを、恥ずかしながら初めて知りました。
$hoge[] = "hoge"; var_dump($hoge);
array(1) { [0]=> string(4) "hoge" }
同様に、こんなこともできます。
$hoge["hoge"][] = "hoge"; var_dump($hoge);
array(1) { ["hoge"]=> array(1) { [0]=> string(4) "hoge" } }
公式にもしっかりと記述されていました。
角括弧構文で作成/修正
$arr
がまだ存在しない場合は、新しく作成します。 つまり、これは配列を作成する方法のひとつでもあります。
そりゃそうかという感じですが、メモとして残しておきます。
PHPでGoogle Play Developer APIを使ってAndroidのレシートを検証した後にAcknowledgeしたい(超簡易メモ版)
調査してもほとんど情報を見つけることができない「Acknowledge」の挙動について超簡易的にまとめてみます。
以降、間違った情報もある可能性が高く、課金処理は慎重に実装されるべきなので、鵜呑みにはしないようお願いします。
基本的にはこちらの続きで、タイトルもほぼ一緒ですが、今回は Acknowledge についてです。 ohshige.hatenablog.com
Acknowledgeとは?
Acknowledgeとは公式によると以下です。 developer.android.com
端的に言うと、ユーザの購入行動の「確定処理」をアプリ側なりサーバ側なりでやれということでしょうか。
これをしないと払い戻しになってしまうため大変です。
そういうわけなので、言われた通りにAcknowledgeすればいいわけですが、いくつかの挙動の違いに悩んだのでまとめます。
Google APIs Client Library for PHP
google-api-php-clientでAcknowledgeを実装するには以下のように行います。
(ライブラリのインストールや認証の事前準備は前の記事の通りです)
// 定期購読の場合 $postBody = new \Google_Service_AndroidPublisher_SubscriptionPurchasesAcknowledgeRequest(); $result = $publisher->purchases_subscriptions->acknowledge([packageName], [productId], [purchaseToken], $postBody); // 都度課金の場合 $postBody = new \Google_Service_AndroidPublisher_ProductPurchasesAcknowledgeRequest(); $result = $publisher->purchases_products->acknowledge([packageName], [productId], [purchaseToken], $postBody);
必要なパラメータがあれば公式のリファレンスを参照してください。
Purchases.subscriptions: acknowledge | Google Play Developer API | Google Developers
Purchases.products: acknowledge | Google Play Developer API | Google Developers
挙動まとめ
全体的に挙動の把握に苦労したのでまとめます。
都度課金 | 定期購読 | ||
---|---|---|---|
新バージョンのレシート | そのまま | 204 No Content | 204 No Content |
払い戻し済み | 500 Internal Server Error ( null ) | 400 Bad Request (subscriptionNotOwnedByUser) | |
継続利用停止済み | - | 400 Bad Request (subscriptionNotOwnedByUser) | |
旧バージョンのレシート | 204 No Content | 400 Bad Request (subscriptionNotOwnedByUser) |
Acknowledgeに成功すれば204が返ってきます。
成功しなかった場合がよくわかりません。
旧バージョンのレシートに対して実行してしまった場合に都度課金と定期購読とで挙動が異なる上に、払い戻し済みのレシートに対して実行してしまった場合でも都度課金と定期購読で挙動が違って都度課金に至っては500が返ってきてしまいます。
これらに関しては今後変更になる可能性もあるため注意が必要がかもしれません。
ブログをはじめて半年が経った
このブログをはじめてから半年が経ちました。
毎週1記事はアップするようにして、よく続いているなと自分でも思っています。
もともとは2019年になったタイミングで何かやってみようと思い立ったのがキッカケでした。
ブログの目標はお小遣い稼ぎでも有名になりたいわけでもなく、ただの自己満のようなものなので、中には「小学生の感想文」のような内容と文章で公開したものもありますが、自分の紹介したいもの・勉強したこと・作ったものを書き続けられて良かったと思っています。
実は、前にもブログはやっていて、学生時代から社会人になってしばらく経つまで続けていました。
そのブログをやめてからしばらく経ち、学生のときのことも書かれていて恥ずかしいので、新しくブログを始めるなら心機一転ゼロから始めようと思いました。
その古いブログは数年間一切更新していませんが、今でも毎日のように一定数のアクセスはあるようで、このブログよりもPVは高いです。
とりあえずの目標としては、このブログを続けつつ、前の古いブログよりもアクセスを伸ばすことにしようと思います。
最後に、良かったこと大変なことを箇条書きで。
良かったこと
常日頃、ネタを探すようになりました。
何か作れるものはないか、初めて扱う技術はないか、面白い本はないか、日常にアンテナを張るようにはなりました。
間違いが無いようにしっかりと調べるようになりました。
これまでは軽く浅く調べて終わりということも多かったですが、アウトプットのためにもう一度間違いがないかしっかりと調べる癖がつきました。
休日の生活がまともになりました。
基本的に予定の無い休日に書くようにしているので、休日のダラダラと寝るだけの生活が是正され、カフェに出かけてブログを書く生活になりました。
大変なこと
ネタが無い...