- 投稿日:2020-01-21T20:29:26+09:00
【Laravel - AWS】composer install が mbstring のエラーで失敗するとき
結論だけ知りたい
$ sudo yum install php{$ver}-mbstring.x86_64
$ver
はインストールしたPHPのバージョンに合わせる。PHPバージョン7.2系をインストールしている場合は
$ sudo yum install php72-mbstring.x86_64とすればOK。心配な人はPHPのバージョンを確認しましょう。
前提
Amazon Linuxの公式ガイドに沿ってLAMP環境を構築。
基本は記事に忠実に。ただ途中、phpのバージョンを70→72に指定した。
バージョン情報
Laravel : 6.9
PHP : 7.2.24
AWS : EC2 - AmazonLinux▼上記の記事に書かれているコマンド▼
[ec2-user ~]$ sudo yum install -y httpd24 php70 mysql56-server php70-mysqlnd▼実際に打ち込んだコマンド▼
[ec2-user ~]$ sudo yum install -y httpd24 php72 mysql56-server php72-mysqlnd起きたこと
ローカルのLaravelを本番でgit経由でpullし、
composer install
しようとしたタイミングでエラー。エラー内容
[ec2-user@ip-XXX src]$ composer install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Your requirements could not be resolved to an installable set of packages. Problem 1 - Installation request for erusev/parsedown 1.7.3 -> satisfiable by erusev/parsedown[1.7.3]. - erusev/parsedown 1.7.3 requires ext-mbstring * -> the requested PHP extension mbstring is missing from your system. Problem 2 - Installation request for laravel/framework v6.9.0 -> satisfiable by laravel/framework[v6.9.0]. - laravel/framework v6.9.0 requires ext-mbstring * -> the requested PHP extension mbstring is missing from your system. Problem 3 - Installation request for facade/ignition 1.13.0 -> satisfiable by facade/ignition[1.13.0]. - facade/ignition 1.13.0 requires ext-mbstring * -> the requested PHP extension mbstring is missing from your system. Problem 4 - Installation request for phpunit/phpunit 8.5.1 -> satisfiable by phpunit/phpunit[8.5.1]. - phpunit/phpunit 8.5.1 requires ext-mbstring * -> the requested PHP extension mbstring is missing from your system. Problem 5 - Installation request for scrivo/highlight.php v9.17.1.0 -> satisfiable by scrivo/highlight.php[v9.17.1.0]. - scrivo/highlight.php v9.17.1.0 requires ext-mbstring * -> the requested PHP extension mbstring is missing from your system. Problem 6 - laravel/framework v6.9.0 requires ext-mbstring * -> the requested PHP extension mbstring is missing from your system. - facade/flare-client-php 1.3.1 requires illuminate/pipeline ~5.5|~5.6|~5.7|~5.8|^6.0 -> satisfiable by laravel/framework[v6.9.0]. - Installation request for facade/flare-client-php 1.3.1 -> satisfiable by facade/flare-client-php[1.3.1].何やら長々とエラーが出ているが、全部のエラーで php-extensionの
mbstring
が無いということなのでそれをインストールすれば良さそう。調べてみると
yum install --enablerepo=remi-php72 php-mbstring
でインストールできるとのこと。しかしコマンドを打ち込んだらまた怒られる。
[ec2-user@ip-XXX src]$ sudo yum install --enablerepo=remi-php72 php-mbstring 読み込んだプラグイン:priorities, update-motd, upgrade-helper Error getting repository data for remi-php72, repository not foundリポジトリが見つからないということなのでリポジトリのコマンドを外す。
[ec2-user@ip-XXX src]$ sudo yum install php-mbstring 読み込んだプラグイン:priorities, update-motd, upgrade-helper 依存性の解決をしています --> トランザクションの確認を実行しています。 ---> パッケージ php-mbstring.x86_64 0:5.3.29-1.8.amzn1 を インストール --> 依存性の処理をしています: php-common(x86-64) = 5.3.29-1.8.amzn1 のパッケージ: php-mbstring-5.3.29-1.8.amzn1.x86_64 --> トランザクションの確認を実行しています。 ---> パッケージ php-common.x86_64 0:5.3.29-1.8.amzn1 を インストール --> 衝突を処理しています: php72-common-7.2.24-1.18.amzn1.x86_64 は php-common < 5.5.22-1.98 と衝突しています --> 依存性解決を終了しました。 エラー: php72-common conflicts with php-common-5.3.29-1.8.amzn1.x86_64 問題を回避するために --skip-broken を用いることができます。 これらを試行できます: rpm -Va --nofiles --nodigestバージョンがコンフリクトしたらしい。どうやらインストールしているパッケージ自体がおかしい気がする。
よくよく調べると、AmazonLinuxはデフォルトで自前のリポジトリを最優先で探すことがわかった。
これを回避する方法は
- 対応するパッケージをAmazonLinux公式のリポジトリから落とす。
- リポジトリ優先度の設定をする。
の2つと考えられる。
今回は
mbstring
がインストールできれば良いので1の方法で行くことにした。2の方法についてはこちらの記事がわかりやすい。
Amazon LinuxでYUMを使う時に気をつけるポイント | Developers.IO対応パッケージを探す
Amazon公式のリポジトリは
php72-cli.x86_64
のように、バージョンとパッケージ名を同時に指定する方式になっている。今回は72に対応するパッケージが欲しいのでgrepを使って探す
[ec2-user@ip-XXX src]$ yum list | grep php72 php72.x86_64 7.2.24-1.18.amzn1 @amzn-updates php72-cli.x86_64 7.2.24-1.18.amzn1 @amzn-updates php72-common.x86_64 7.2.24-1.18.amzn1 @amzn-updates php72-json.x86_64 7.2.24-1.18.amzn1 @amzn-updates php72-mbstring.x86_64 7.2.24-1.18.amzn1 @amzn-updates php72-mysqlnd.x86_64 7.2.24-1.18.amzn1 @amzn-updates ...mbstringも発見したので、これをインストールしてみる。
[ec2-user@ip-XXX src]$ sudo yum install php72-mbstring.x86_64 読み込んだプラグイン:priorities, update-motd, upgrade-helper 依存性の解決をしています --> トランザクションの確認を実行しています。 ---> パッケージ php72-mbstring.x86_64 0:7.2.24-1.18.amzn1 を インストール --> 依存性解決を終了しました。 依存性を解決しました ================================================================================================================= Package アーキテクチャー バージョン リポジトリー 容量 ================================================================================================================= インストール中: php72-mbstring x86_64 7.2.24-1.18.amzn1 amzn-updates 1.4 M トランザクションの要約 ================================================================================================================= インストール 1 パッケージ 総ダウンロード容量: 1.4 M インストール容量: 3.2 M Is this ok [y/d/N]: y Downloading packages: php72-mbstring-7.2.24-1.18.amzn1.x86_64.rpm | 1.4 MB 00:00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction インストール中 : php72-mbstring-7.2.24-1.18.amzn1.x86_64 1/1 検証中 : php72-mbstring-7.2.24-1.18.amzn1.x86_64 1/1 インストール: php72-mbstring.x86_64 0:7.2.24-1.18.amzn1 完了しました!無事インストールできたので、改めて
composer install
してみる[ec2-user@ip-XXX src]$ composer install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 85 installs, 0 updates, 0 removals ... Package manifest generated successfully.無事通った。
- 投稿日:2020-01-21T18:35:14+09:00
Laravelではメール送信やSlack通知のテストが簡単だって知ってましたか?
メールやSlack通知のテストしてますか?
通知のテストはし難いのですが、Laravelではモックが充実しているので簡単にテストすることができます。参考: https://readouble.com/laravel/6.x/ja/mocking.html
環境
- PHP v7.2
- Laravel v6.x
- PHPUnit v7.5
テストする方法
以下のようにpostのテストを実行する前に Mail Facade で fake を差し込むようにしておく。
postテスト実行した後に、assertSent()
でMailableクラスの処理が走っているかテストできます。use Illuminate\Support\Facades\Mail; class IndexControllerTest extends TestCase { public function testIndex() { Mail::fake(); $this->post('/path/to/something/post', ['text' => 'ダミーテキスト'])->assertRedirect(); // MessageCreatedクラスは、Mailableをextendsしたものです。実行されているべきMailableをフルパスで指定します。 Mail::assertSent(MessageCreated::class); } }おまけ
notificationもテストできます。
Slack通知など。
ドキュメントのNotification Fake
のところを参照してください。
「テストをかけるよ」ということを知識として知っていないとなかなかかけないのでここで紹介だけしておきます。ハマりどころ
最初、以下のように書いて動かずにハマったので共有しておきます。
public function testIndex() { $this->post('/path/to/something/post', ['text' => 'ダミーテキスト'])->assertRedirect(); // postのテストが終わった後にfake差し込み Mail::fake(); Mail::assertSent(MessageCreated::class); }fakeを差し込むタイミングが終わった後なので意味がありません。
面倒なので毎回 fake を書きたく無い場合
setUp()のなかで処理を追加しておきましょう。
毎回クラスに書いてもいいのですが、これを差し込んだ共通親クラスを作っておくともっと楽チンです。protected function setUp(): void { parent::setUp(); Mail::fake(); }
- 投稿日:2020-01-21T17:16:49+09:00
Laravel データベースの論理削除
はじめに
- Laravel で データベースの論理削除を行いたい時は、ソフトデリート の機能が使用できる。
- 論理削除とは、本当にデータベースから削除するのではなく、フラグなどを立ててシステム上から見えなくすること
- Laravelでは Eloquent の機能でソフトデリートが使用できる。
前提
Laravel のバージョン
$ php artisan -V > Laravel Framework 5.8.16Mysql のバージョン
- mysql を使用する
mysql> select version(); +-----------+ | version() | +-----------+ | 5.7.28 | +-----------+使いかた
①テーブルに『deleted_at』カラムを追加する
- ここでは users テーブルにソフトデリートを実装する
mysql> describe users; +-------------------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | username | varchar(20) | NO | UNI | NULL | | | password | varchar(128) | NO | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | | deleted_at | timestamp | YES | | NULL | | +-------------------+---------------------+------+-----+---------+----------------+②ソフトデリートを使用できるようにモデルで宣言
- モデルで宣言するだけで、
delete()
処理を実行するとdeleted_at
カラムに現在の時刻がセットされる。deleted_at
カラムに現在の時刻がセットされていると、削除済み隣、クエリ結果に含まれない。app/Model/User.php<?php namespace App\Models; use Illuminate\Database\Eloquent\SoftDeletes; class User extends Model { use SoftDeletes; 〜省略まとめ
- とても簡単にソフトデリートを有効にできた。
- ソフトデリート済みのデータのみ取得や、物理削除の方法もあるがあとで、別途調べるよお。
- 投稿日:2020-01-21T12:46:32+09:00
Laravelで大規模プロジェクトと戦うにはRepository層を導入するといいかも
Laravelをなんとなく書いていると起こる問題点
- コントローラ膨らみすぎ問題
- Model膨らみすぎ問題
- MdoelにORMマッパーにベタベタに依存したビジネスロジックを書いてしまってテスト書くのがしんどくなる問題
設計方針
重要度が高いビジネスロジックはDBやLaravelの機能に依存させない
LaravelのModelにビジネスロジックを書いてしまうとEloquent依存のコードを簡単にかけてしまいます。
とりあえず動かすにはこれが一番早いので選択肢としてはありかと思いますが、後に機能が複雑になってくるとテスト結果がDBの状態になったりテストがしにくくなるのでやめましょう
とはいえEloquentは便利なのでRepository内で使いましょうできるところはLaravelに依存する
ログイン、ページネーション、バリデーションなどはLaravelに依存しましょう。できる限りテストを簡単に
テストを簡単にかけるような設計でないとテスト書くのが苦痛になり、テストが雑になりがちです。解決策
DDDや、クリーンアーキテクチャはそのまま導入しようとしましたが、現状に対してあまりに壮大過ぎる&チームでやるには学習コストが高すぎるので以下を導入することにしました。
Modelからビジネスロジックを分離させる
Entitiesディレクトリを新規に作成し、ここに重要度が高いビジネスロジックをModelに依存させない形で作成します。
LaravelのFacadeも使ってはいけません。CakePHPやWordPressにそのままコピペできるようにしましょう。<?php namespace App\Entities; class Video { protected int $id; protected string $title; protected string $description; protected string $publish_at; protected User $author; /** * @return int */ public function getId(): int { return $this->id; } /** * @return string */ public function getTitle(): string { return $this->title; } /** * @return string */ public function getDescription(): string { return $this->description; } /** * @return User */ public function getAuthor(): User { return $this->author; } /** * @return User|string */ public function getPublishAt() { return $this->publish_at; } /** * Video constructor. * @param int $id * @param string $title * @param string $description * @param string $publish_at * @param User $author */ public function __construct(int $id, string $title, string $description, string $publish_at, User $author) { $this->id = $id; $this->title = $title; $this->description = $description; $this->publish_at = $publish_at; $this->author = $author; } public function check_publish(){ // いろいろなロジックを書いていく } }Repository層の導入
Repositoriesディレクトリを作成し、ここでEloquentを使いながら上のEntityを作成します。
まずはapp/Repositoriesにインターフェイスを作成します。インターフェイスを作成することでデータベースへの依存を防げます。<?php namespace App\Repositories; use App\Entities\Video; interface VideoRepository { public function getById(int $id): ?Video; }app/Repositories/implに実際にEloquentを使った組み込みを書いていきます。
<?php namespace App\Repositories\impl; use App\Entities\Video; use App\Repositories\VideoRepository; class VideoDBRepository implements VideoRepository { function getById(int $id): ?Video { $video = \App\Model\Video::find($id); if(empty($video)){ return null; } return new Video($video->id, $video->title, $video->discription, $video->author} }サービスプロバイダーを追加し、VideoRepositoryとVideoDBRepositoryをバインドします。
これでVideoRepository参照するとVideoDBRepositoryが読み込まれます。
VideoRepositoryに依存することにより入れ替えで簡単にテストができます。<?php namespace App\Providers; use App\Repositories\impl\VideoDBRepository; use App\Repositories\VideoRepository; use Illuminate\Support\ServiceProvider; class RepositoryServiceProvider extends ServiceProvider { /** * Register services. */ public function register(): void { } /** * Bootstrap services. */ public function boot(): void { $this->app->bind(VideoRepository::class, VideoDBRepository::class); } }実際にリポジトリを使用する時はコンストラクタインジェクションを使います。
サービス・プロバイダーでbindしているのでInterfaceからアクセス可能です。<?php namespace App\Http\Controllers; use App\Entities\Video; use App\Repositories\VideoRepository; class VideoController extends Controller { protected VideoRepository $videoRepository; /** * VideoController constructor. * @param VideoRepository $videoRepository */ public function __construct(VideoRepository $videoRepository) { $this->videoRepository = $videoRepository; } public function video(VideoRequest $request){ /** @var Video $video */ $video = $this->videoRepository->getById($request->get('id')); return view('video', [ 'video' => $video ]); } }まとめ
ControllerはRepositoryのIntarfaceに依存しているので、ControllerはDBを意識しないでよくなりました。
テスト用のMockRepositoryでも、DBではなくAPIで永続化をするApiRepositoryでもimpl組み込みを追加しbindし直すだけで対応が可能になりました。
そしてEntityではRepositoryもControllerも意識する必要がありません。ロジックのテストにDBの依存が入らないのでテストがやりやすくなります。
- 投稿日:2020-01-21T01:47:24+09:00
ReflectionException Class does not exist 原因まとめ
ReflectionException Class does not existが出現するときの原因
違う原因で何度か遭遇したのでまとめます。
use
し忘れ -> ちゃんとuseする。- autoloaderが壊れる ->
composer dump-aoutoload
- sintax error -> 直す
3はPHPのバージョンに寄ったりするようで、ローカルでは問題ないけど、本番環境ではエラーが発生するという事象で少し厄介でした。
参考
https://qiita.com/yoshinyan/items/5116bd30f2d1f2b1d865
https://qiita.com/K-Shuuun/items/c48292cd4186a2c1c8fd
- 投稿日:2020-01-21T00:10:44+09:00