- 投稿日:2020-12-08T23:49:29+09:00
Laravel Duskエラー 「Facebook\WebDriver\Exception\UnknownErrorException: unknown error: net::ERR_CONNECTION_REFUSED」
発生したタイミング
テストコード作成後、下記コマンド実行時にタイトル記載のエラーが表示
php artisan dusk原因
調べるとどうやら.envに記載したAPP_URLパラメータの値が間違っている模様
原因解決
.envのAPP_URLパラメータの値をブラウザからアクセスするアプリケーションで使用するURLと一致させる。
現在デプロイをしておらずローカル環境だった為下記のように変更する。APP_URL=http://localhost:8000/修正後コマンド実行
これで解決したと思い再度php artisan duskを実行すると......。
同じエラーが。
.......原因はサーバーを立ち上げていなかったから。
下記コマンドでサーバーを立ち上げ。。。。php artisan serve新規タブで
php artisan duskこれで無事テストが実行されました!!!
以上
- 投稿日:2020-12-08T23:31:28+09:00
Laravel Duskの導入
導入背景
現在Laravelで作成したポートフォリオのテストをデフォルトで搭載されているテストディレクトリを使用して実行していたが、利用が簡単なブラウザの自動操作/テストAPIであるDuckの存在を知ったので使用してみた
導入方法
プロジェクトに移動し、パッケージを取得
cd (プロジェクト名) composer require --dev laravel/dusk php artisan dusk:install上記後、プロジェクトのtestディレクトリ内にBrowserディレクトリが作成される
.envの変更
.envファイル内のAPP_URL環境変数を変更する。
値はブラウザからアクセスするアプリケーションで使用するURLと一致させます。
※ローカル環境の場合//デフォルト) APP_URL=http://localhost ↓ //変更後) APP_URL=http://localhost:8000参考文献
- 投稿日:2020-12-08T22:55:05+09:00
Laravel/Socialiteが何をしているか調べてみた ~①リダイレクト編~
目標
Laravelで「Googleでログイン」を実装する際に導入する"Socialite"がどんなことをやっているのか調査してみた。
①リダイレクト編の目標は、Controllerに記述することになる下記コードが最終的なアウトプットは何か、どのように実装されているのかを明確にする。以下Controllerに記述することになるコード
SocialOAuthController.php/** * Googleアカウントの認証ページへユーザーをリダイレクト * ①リダイレクト編で解説するコード */ public function redirectToGoogle() { return Socialite::driver('google')->redirect(); } /** * Googleアカウントからユーザー情報を取得 * ②コールバック編で解説予定 */ public function handleGoogleCallback() { $user = Socialite::driver('google')->stateless()->user(); }先に結論(最終的なアウトプット)
認可URLにリダイレクトするRedirectResponseインスタンスを生成します。
リダイレクト先のURLは下記の通りです。RequestURL = https://accounts.google.com/o/oauth2/auth? client_id=$client_id &redirect_url=$redirect_url &scope=$scope1+$scope2+$scope3 &response_type=code &state=$state<環境>
Laravel Framework 8.17.2 laravel/socialite v5.1.2<解説>
Socialite
SocialiteのファサードはSocialiteServiceProviderで下記のように定義されています。
SocialiteServiceProvider.phpnamespace Laravel\Socialite; class SocialiteServiceProvider extends ServiceProvider { /** * Register the service provider. * * @return void */ public function register() { $this->app->singleton(Factory::class, function ($app) { return new SocialiteManager($app); }); } }Socialite::driver('google')
driverメソッドに文字列でドライバを指定しています。
Socialiteクラスの基底クラスであるManagerクラスにdriverメソッドで引数に渡した文字列に該当するドライバが可変関数として呼び出されます。Managernamespace Illuminate\Support; abstract class Manager { /** * Get a driver instance. * * @param string|null $driver * @return mixed * * @throws \InvalidArgumentException */ public function driver($driver = null) { $driver = $driver ?: $this->getDefaultDriver(); if (is_null($driver)) { throw new InvalidArgumentException(sprintf( 'Unable to resolve NULL driver for [%s].', static::class )); } // ドライバのインスタンスはキャッシュして再利用される。 if (! isset($this->drivers[$driver])) { $this->drivers[$driver] = $this->createDriver($driver); } return $this->drivers[$driver]; } /** * Create a new driver instance. * * @param string $driver * @return mixed * * @throws \InvalidArgumentException */ protected function createDriver($driver) { if (isset($this->customCreators[$driver])) { return $this->callCustomCreator($driver); } else { $method = 'create'.Str::studly($driver).'Driver'; if (method_exists($this, $method)) { // ここでcreateGoogleDriver()が呼び出される return $this->$method(); } } throw new InvalidArgumentException("Driver [$driver] not supported."); } }createGoogleDriver()
createGoogleDriverメソッドではconfigファイル(services.google)に設定した情報を取得し、GoogleProviderクラスのインスタンスを生成しています。
SocialiteManager.phpnamespace Laravel\Socialite; class SocialiteManager extends Manager implements Contracts\Factory { /** * Create an instance of the specified driver. * * @return \Laravel\Socialite\Two\AbstractProvider */ protected function createGoogleDriver() { $config = $this->config->get('services.google'); return $this->buildProvider( GoogleProvider::class, $config ); } }AbstractProviderを継承するGoogleProviderインスタンスを生成するまでがSocialite::driver('google')の動きです。
Socialite::driver('google')->redirect()
redirectメソッドでは生成されたリダイレクトURLにリダイレクトするようにRedirectResponseクラスのインスタンスを生成している。
AbstractProvider.phpnamespace Laravel\Socialite\Two; abstract class AbstractProvider implements ProviderContract { /** * Redirect the user of the application to the provider's authentication screen. * * @return \Illuminate\Http\RedirectResponse */ public function redirect() { $state = null; if ($this->usesState()) { $this->request->session()->put('state', $state = $this->getState()); } // ここでRedirectResponseクラスのインスタンスを生成 return new RedirectResponse($this->getAuthUrl($state)); } }getAuthUrl()
getAuthUrlメソッドはGoogleProviderで定義されている
GoogleProvider.phpnamespace Laravel\Socialite\Two; use Illuminate\Support\Arr; class GoogleProvider extends AbstractProvider implements ProviderInterface { /** * {@inheritdoc} */ protected function getAuthUrl($state) { return $this->buildAuthUrlFromBase('https://accounts.google.com/o/oauth2/auth', $state); } }AbstractProviderに戻ってURLがどのように生成されるか見ていく。
GoogleのベースURLにクエリパラメータとして認可に必要な各項目をセットしている。AbstractProvider.php/** * Build the authentication URL for the provider from the given base URL. * * @param string $url * @param string $state * @return string */ protected function buildAuthUrlFromBase($url, $state) { // $this->encodingTypeはPHP_QUERY_RFC1738が定義されている return $url.'?'.http_build_query($this->getCodeFields($state), '', '&', $this->encodingType); } /** * Get the GET parameters for the code request. * * @param string|null $state * @return array */ protected function getCodeFields($state = null) { $fields = [ 'client_id' => $this->clientId, 'redirect_uri' => $this->redirectUrl, 'scope' => $this->formatScopes($this->getScopes(), $this->scopeSeparator), 'response_type' => 'code', ]; if ($this->usesState()) { $fields['state'] = $state; } return array_merge($fields, $this->parameters); } /** * Format the given scopes. * * @param array $scopes * @param string $scopeSeparator * @return string */ protected function formatScopes(array $scopes, $scopeSeparator) { return implode($scopeSeparator, $scopes); } }<おまけ>認可リクエストのカスタマイズ
Laravel公式にもあるようにパラメータや、スコープの追加をすることができる。
パラメータの追加
パラメータを追加するにはwithメソッドを使用します。
$parametersの規定値は空の配列[]です。AbstractProvider.php/** * Set the custom parameters of the request. * * @param array $parameters * @return $this */ public function with(array $parameters) { $this->parameters = $parameters; return $this; }scopeの追加
scopeに規定値は['openid','profile','email']が定義されている。
追加するには下記のようにscopesメソッドを呼び出すか、setScopeメソッドで上書きするとよい。SocialOAuthController.php/** * Googleアカウントの認証ページへユーザーをリダイレクト */ public function redirectToGoogle() { return Socialite::driver('google')->scopes(['read:user', 'public_repo'])->redirect(); }メソッドの中身は下記のように配列がマージされる。
array_uniqueメソッドでは、重複した要素が存在する場合、はじめの要素のキーと値が保持されます。
https://www.php.net/manual/ja/function.array-unique.phpAbstractProvider.php/** * Merge the scopes of the requested access. * * @param array|string $scopes * @return $this */ public function scopes($scopes) { // $this->scopes = array_unique(array_merge($this->scopes, (array) $scopes)); return $this; } /** * Set the scopes of the requested access. * * @param array|string $scopes * @return $this */ public function setScopes($scopes) { $this->scopes = array_unique((array) $scopes); return $this; }リダイレクトURLの変更
リダイレクトURLが複数存在し、configファイルではなく動的に値を変更したい場合はredirectURLメソッドを使用します。
SocialOAuthController.php/** * Googleアカウントの認証ページへユーザーをリダイレクト */ public function redirectToGoogle() { return Socialite::driver('google')->redirectURL($redirectUrl)->redirect(); }AbstractProvider.php/** * Set the redirect URL. * * @param string $url * @return $this */ public function redirectUrl($url) { $this->redirectUrl = $url; return $this; }
- 投稿日:2020-12-08T21:16:14+09:00
Laravelですでにあるマイグレーションファイルを書き換えてテーブル定義を変更する
「テーブル作成のマイグレーションファイルに外部キー制約書き忘れた!」みたいなときの対処法です。
マイグレーションファイルが増えるのが気にならない場合や、複数人で管理している場合は、新しくマイグレーションを追加してテーブル定義を変更したほうがいいと思います。1. テーブルを出力する
対象のデータベースのテーブルを全て出力します。
$ mysqldump --no-create-info --ignore-table=database_name.migrations -u user_name -ppassword > ~/dumps/database_name_20210101.dumpポイント
--no-create-info
オプション : マイグレーションでテーブルは作成するので、CREATE TABLE ~
を出力しないオプションです。INSERT INTO ~
のみが出力されます。--ignore-table=database_name.migrations
: マイグレーションデータが保存されているテーブルを出力しません。2. マイグレートする
ファイルを書き換えたあと、マイグレーションを実行します。
php artisan migrate:fresh注意
migrate:fresh
はデータベース内の全てのテーブルをDropするので、データベースをLaravelアプリケーション以外と共有している場合はmigrate:refresh
等を検討してください。3. 挿入する
出力したデータを再度データベースに追加します。変更したテーブル定義が、変更前と互換性がないと、挿入できないので注意してください。
mysql -u user_name -ppassword -D database_name < ~/dumps/database_name_20210101.dumpエラーがでなければ、大丈夫です。
- 投稿日:2020-12-08T20:38:38+09:00
php artisan migrate で autoload_real.php のシンタックスエラーが出た話
php artisan migrate でエラー
$ php artisan migrate PHP Parse error: syntax error, unexpected '' (T_ENCAPSED_AND_WHITESPACE), expecting ']' in /home/vagrant/***/vendor/composer/autoload_real.php on line 71 Parse error: syntax error, unexpected '' (T_ENCAPSED_AND_WHITESPACE), expecting ']' in /home/vagrant/***/vendor/composer/autoload_real.php on line 71composer update でもエラー
$ composer update PHP Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 134217736 bytes) in phar:///usr/local/bin/composer/src/Composer/DependencyResolver/RuleSet.php on line 83 Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 134217736 bytes) in phar:///usr/local/bin/composer/src/Composer/DependencyResolver/RuleSet.php on line 83 Check https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors for more info on how to handle out of memory errors.⏎composer i は成功するが、今度は autoload_static.php でエラー
$ composer i # 成功$ php artisan migrate PHP Parse error: syntax error, unexpected 'C' in /home/vagrant/***/vendor/composer/autoload_static.php on line 8765 Parse error: syntax error, unexpected 'C' in /home/vagrant/***/vendor/composer/autoload_static.php on line 8765/vendor/composer/autoload_static.php を削除
$ rm /vendor/composer/autoload_static.php$ php artisan migrate PHP Warning: require_once(/home/vagrant/***/vendor/composer/autoload_static.php): failed to open stream: No such file or directory in /home/vagrant/***/vendor/composer/autoload_real.php on line 28 Warning: require_once(/home/vagrant/***/vendor/composer/autoload_static.php): failed to open stream: No such file or directory in /home/vagrant/***/vendor/composer/autoload_real.php on line 28 PHP Fatal error: require_once(): Failed opening required '/home/vagrant/***/vendor/composer/autoload_static.php' (include_path='.:/usr/share/php') in /home/vagrant/***/vendor/composer/autoload_real.php on line 28 Fatal error: require_once(): Failed opening required '/home/vagrant/***/vendor/composer/autoload_static.php' (include_path='.:/usr/share/php') in /home/vagrant/***/vendor/composer/autoload_real.php on line 28/vendor/composer/autoload_real.php を削除
$ rm /vendor/composer/autoload_real.php$ php artisan migrate PHP Warning: require_once(/home/vagrant/***/vendor/composer/autoload_real.php): failed to open stream: No such file or directory in /home/vagrant/***/vendor/autoload.php on line 5 Warning: require_once(/home/vagrant/***/vendor/composer/autoload_real.php): failed to open stream: No such file or directory in /home/vagrant/***/vendor/autoload.php on line 5 PHP Fatal error: require_once(): Failed opening required '/home/vagrant/***/vendor/composer/autoload_real.php' (include_path='.:/usr/share/php') in /home/vagrant/***/vendor/autoload.php on line 5 Fatal error: require_once(): Failed opening required '/home/vagrant/***/vendor/composer/autoload_real.php' (include_path='.:/usr/share/php') in /home/vagrant/***/vendor/autoload.php on line 5composer i 再度実行で成功
$ composer i $ php artisan migrate # 成功結論
vender/composer/
配下は composer によって自動生成されるので、その配下のファイルでエラーが出たら怖がらずに削除して、再度 composer i すればよし。
- 投稿日:2020-12-08T19:07:41+09:00
Laravelのフォームリクエストで他のパラメータを参照する方法
やりたいこと
いろいろあって、バリデーション時に他の入力値を使ってバリデーションしたい。
(今回は、ユーザー情報を暗号化していたため、他の入力値を元にユーザーを探して、一致するかどうかを確認したかった)方法
$this->request->get()
を使用する。laravel/app/Http/Requests/HogeRequest.php<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use App\Rules\BirthdayMatched; class HogeRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { // getメソッドで入力値を取得する。 $email = $this->request->get('email'); return [ 'email' => ['required', 'string', 'email'], 'birthday' => ['required', 'string', new BirthdayMatched($email)], ]; }あとはカスタムバリデーションルールのプロパティに代入して使用する。
laravel/app/Rules/BirthdayMatched.php<?php namespace App\Rules; use Illuminate\Contracts\Validation\Rule; use App\User; class BirthdayMatched implements Rule { private $email; /** * Create a new rule instance. * * @return void */ public function __construct(string $email) { $this->email = $email; } /** * Determine if the validation rule passes. * * @param string $attribute * @param mixed $value * @return bool */ public function passes($attribute, $value) { //ここで$this->emailを使ったバリデーション処理をする。 } /** * Get the validation error message. * * @return string */ public function message() { return '誕生日が一致しません'; } }
- 投稿日:2020-12-08T13:36:40+09:00
Laravel ユーザー登録と認証機能
Laravelの認証機能について学習している中で気になったことがあったのでまとめ
今までlaravelの学習をしていてユーザーの認証などはlaravel uiに頼りっきりであまり理解していなかった。
今回、自分で認証機能を設定して作成する場合にどういった手順で進めていけばよいかを記載していく。〇ユーザー登録機能を自作する場合
web.phpを確認し、元々設定されているユーザー登録の廃止上記のコマンドを実行する。
そうすると、web.php内に、
このような、1文が追加される。この追加された文の引数を指定することにより、現在のユーザー登録機能のON/OFFが可能になる。
あとは、RegisterController.phpを編集することで任意の認証機能の実装が可能になる。
Controller内の設定などについてはまた勉強していく上で気が付いたことがあれば記述していく。
- 投稿日:2020-12-08T10:25:04+09:00
Laravel8で完成されたModelFactoryの使い方
Laravel Advent Calendar 2020 - Qiita の 8日目 の記事です。
昨日は @okdyy75 さんのLaravelを触って1年経ったのでTIPSの記事でした!
明日は @kaino5454 さんのポケモンAPIを叩いて使用率ランキングを取得してみたの記事です!概要
Laravel8からModelFactoryの機能が刷新されました。
新しい書き方についてまとめたいと思います。こちらの記事をリスペクトしてタイトル名に使わせていただきました???
予備知識
Laravelのデータベース関連の用語として、Migration、Seeder、ModelFactory、Fakerの4つあります。
Migration(マイグレーション)
https://readouble.com/laravel/8.x/ja/migrations.html
マイグレーションはデータベースのテーブルの定義を書きます。
英語で「移動・移行」という意味になります。テーブルの変更のソースコードで管理することができます。
そのため本番環境へリリースしたり、新しいメンバーが参画した際にも簡単に同じテーブルの状態を再現できます。artisan コマンド使用例
$ php artisan make:migration create_posts_table # マイグレーションのテンプレート生成 $ php artisan migrate # マイグレーション実行 $ php artisan migrate:fresh # 全テーブル削除、全マイグレーション再実行 $ php artisan migrate:fresh --seed # シーディングも行う同じ名前のマイグレーションファイルはエラーになるので、注意して命名していく必要があります。
1ファイルで複数テーブルのマイグレーション定義は書かないほうが良いです。Seeder(シーダー)
https://readouble.com/laravel/8.x/ja/seeding.html
シーダーはデータベースにダミーデータを一斉に挿入できる機能です。
英語で「種をまく人」という意味になります。(Seeding/シーディングとも表記されます)artisan コマンド使用例
$ php artisan make:seeder PostsSeeder # シーダーのテンプレート生成 $ php artisan db:seed # DatabaseSeeder を実行 $ php artisan db:seed --class=PostsSeeder # クラス指定して実行テーブル単位やデータのまとまり毎に書くと良いでしょう。
ModelFactory(モデルファクトリ)
https://readouble.com/laravel/8.x/ja/database-testing.html#creating-factories
モデルファクトリはEloquentモデルの各フィールドに入る値を定義します。
モデルの工場といったところです。主にシーダーやテストコードからモデルファクトリーは呼び出されます。
artisan コマンド使用例
$ php artisan make:factory PostFactory # モデルファクトリのテンプレート生成Facker(フェイカー)
フェイカーはダミーデータ生成用のライブラリです。
主にモデルファクトリを定義する際に使用します。名前や住所、メールアドレスといったよく使われるテストデータをランダムに生成してくれる便利な機能です。
補足: fzaninotto/Fakerのアーカイブ
最近の話(2020年10月21日)ですが、 fzaninotto/Faker の本家のリポジトリがアーカイブされています。
詳細については、下記のリンクをご参照ください。
- https://github.com/fzaninotto/Faker
- https://marmelab.com/blog/2020/10/21/sunsetting-faker.html
- https://twitter.com/taylorotwell/status/1321091021342650377
今後はフォーク先のリポジトリでメンテナンスが続けられるそうです。
また、こちらのプルリクエスト で既にライブラリの変更が行われてます。
- v8.12.0 (2020-10-29)
- v7.29.0 (2020-10-29)
- v6.20.0 (2020-10-28)
上記のバージョン以降のLaravelをご利用であればフォーク先のリポジトリが使用されます。
tinkerでお試し実行方法
ModelFactory, Fakerはtinker上で簡単にお試しできます。
どんなテストデータが生成されるのか気になる場合は、tinkerで試してみましょう!ModelFactoryのお試し実行方法
$ php artisan tinker # make() だとインスタンスのみ返す >>> App\Models\User::factory()->make() => App\Models\User {#3385 name: "Miss Dolores Mueller III", email: "marcus19@example.org", email_verified_at: "2020-12-06 14:58:34", } # create() だとデータベースに挿入してインスタンスを返す >>> App\Models\User::factory()->create() => App\Models\User {#3387 name: "Annabel Upton III", email: "kenton.klocko@example.org", email_verified_at: "2020-12-06 14:58:39", updated_at: "2020-12-06 14:58:39", created_at: "2020-12-06 14:58:39", id: 1, }Fakerのお試し実行方法
詳しい使い方はこちら https://fakerphp.github.io
$ php artisan tinker >>> $faker = app()->make(Faker\Generator::class) >>> $faker->name() => "中津川 和也" # () なしでメソッドではなくプロパティへのアクセスでも実行可能 >>> $faker->name => "佐々木 学" >>> $faker->safeEmail() => "kondo.yui@example.com" >>> $faker->emoji() => "?" >>> $faker->imageUrl() => "https://via.placeholder.com/640x480.png/0066ee?text=aut" >>> $faker->dateTimeBetween('-2weeks', '9 days')->format('Y-m-d H:i:s') => "2020-11-28 05:24:51"設定
Faker 日本語化
config/app.php
のfaker_locale
の値を変更してください。config/app.php/* |-------------------------------------------------------------------------- | Faker Locale |-------------------------------------------------------------------------- | | This locale will be used by the Faker PHP library when generating fake | data for your database seeds. For example, this will be used to get | localized telephone numbers, street address information and more. | */ // 'faker_locale' => 'en_US', 'faker_locale' => 'ja_JP',実行すると日本語名のデータが入るようになります。
>>> App\Models\User::factory()->make()->name => "田中 桃子"
本題
横道に逸れてしまいましたが、ここからが本題のModelFactoryです。
シーダーからテストデータを挿入する
シーダーからテストデータをデータベースに挿入したい場合、こんな感じで生成することができます。
database/seeders/DatabaseSeeder.php<?php namespace Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; class DatabaseSeeder extends Seeder { public function run(): void { DB::table('users')->insert([ 'name' => Str::random(10), 'email' => Str::random(10).'@gmail.com', 'password' => Hash::make('password'), ]); } }数件程度であれば良いですが、開発が進むにつれて複雑なテストデータを作ろうとしていくとメンテナンスがとても大変になってしまいます...。
デフォルトで用意されているモデル
app/Models/User.php<?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { use HasFactory, Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }
Illuminate\Database\Eloquent\Factories\HasFactory
trait を use する必要があります。デフォルトで用意されているモデルファクトリ
database/factories/UserFactory.php<?php namespace Database\Factories; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; class UserFactory extends Factory { /** * The name of the factory's corresponding model. * * @var string */ protected $model = User::class; /** * Define the model's default state. * * @return array */ public function definition() { return [ 'name' => $this->faker->name, 'email' => $this->faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; } }tinker からモデルファクトリーを呼び出して使う
$ php artisan tinker App\Models\User::factory()->create() => App\Models\User {#3380 name: "近藤 結衣", email: "uno.naoki@example.net", email_verified_at: "2020-12-06 15:43:28", updated_at: "2020-12-06 15:43:28", created_at: "2020-12-06 15:43:28", id: 3, } // name 属性を上書きする App\Models\User::factory()->create(['name' => 'ucan']) => App\Models\User {#4264 name: "ucan", email: "sato.yuki@example.com", email_verified_at: "2020-12-06 15:43:46", updated_at: "2020-12-06 15:43:46", created_at: "2020-12-06 15:43:46", id: 4, }
- モデルファクトリを使うと複数代入の保護を自動的に無効化します。
Laravel7との違い
database/factories/UserFactory.php<?php /** @var \Illuminate\Database\Eloquent\Factory $factory */ use App\User; use Faker\Generator as Faker; use Illuminate\Support\Str; /* |-------------------------------------------------------------------------- | Model Factories |-------------------------------------------------------------------------- | | This directory should contain each of the model factory definitions for | your application. Factories provide a convenient way to generate new | model instances for testing / seeding your application's database. | */ $factory->define(User::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; });呼び出し方
factory(App\User::class)->create();以前は
$factory->define()
関数を使って登録、factory()
関数を使って呼び出ししていく形でしたが、Laravel 8からクラスベースとなりました。基本の流れ
posts テーブル
テーブルがないとテストデータを入れれないので、下記のようにマイグレーションファイルを作っておきます。
$ php artisan make:migration create_posts_table
database/migrations/YYYY_MM_DD_XXXXXX_create_posts_table.php
といったマイグレーションファイルが生成されるので、下記のように追記しておきます。database/migrations/2020_12_08_000000_create_posts_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id'); $table->string('title'); $table->text('content'); $table->string('published_at')->nullable(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('posts'); } }$ php artisan migrateモデル、モデルファクトリークラスの生成
新しく自分でファイルを作成しても良いですが、テンプレートを生成する便利コマンドがあるので使いましょう。
# モデルとファクトリー個別に生成する $ php artisan make:model Post $ php artisan make:factory PostFactory --model=Post or # モデルとファクトリーを生成する $ php artisan make:model Post --factory $ php artisan make:model Post -f # 省略オプション or # モデルとファクトリー、マイグレーション、シーダー、コントローラを生成する $ php artisan make:model Post --all $ php artisan make:model Post -a # 省略オプション生成されるファイル
app/Models/Post.php
database/factories/PostFactory.php
app/Models/Post.php<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; }database/factories/PostFactory.php<?php namespace Database\Factories; use App\Models\Post; use Illuminate\Database\Eloquent\Factories\Factory; class PostFactory extends Factory { /** * The name of the factory's corresponding model. * * @var string */ protected $model = Post::class; /** * Define the model's default state. * * @return array */ public function definition() { return [ // ]; } }
database/factories/PostFactory.php
のdefinition()
を書き換えてみます。database/factories/PostFactory.phppublic function definition() { return [ 'user_id' => \App\Models\User::factory(), 'title' => $this->faker->title, 'content' => $this->faker->paragraph, ]; }シーダーへ追記します。
database/seeders/DatabaseSeeder.phppublic function run(): void { \App\Models\Post::factory()->count(3)->create(); }$ php artisan migrate:fresh --seed $ php artisan tinker >>> App\Models\User::count(); => 3 >>> App\Models\Post::count(); => 3 >>> App\Models\User::first(); => App\Models\User {#4044 id: 1, name: "斉藤 里佳", email: "yamamoto.asuka@example.org", email_verified_at: "2020-12-06 16:43:25", created_at: "2020-12-06 16:43:25", updated_at: "2020-12-06 16:43:25", } >>> App\Models\Post::first(); => App\Models\Post {#3639 id: 1, user_id: 1, title: "Dr.", content: "Aspernatur maiores qui neque iure cupiditate. Facilis voluptas consequatur mollitia placeat ea. Aperiam neque qui quae placeat debitis nobis minima.", published_at: null, created_at: "2020-12-06 16:43:25", updated_at: "2020-12-06 16:43:25", }Laravelではこんな流れでテストデータを作成していきます。
詳細な使い方
https://readouble.com/laravel/8.x/ja/database-testing.html
公式ドキュメントに詳しい使い方が記載されているので詳細はこちらを参考すると良いです。
補足
公式ドキュメントに書かれてないけど、知ってると便利そうなことをいくつかご紹介します。
Fakerに独自のメソッドを追加する
FakerServiceProvider
を作成します。$ php artisan make:provider FakerServiceProvider
FakerServiceProvider
を登録します。config/app.php'providers' => [ App\Providers\FakerServiceProvider::class, ],
FakerServiceProvider
を登録します。app/Providers/FakerServiceProvider.php<?php declare(strict_types=1); namespace App\Providers; use App\Faker\Planet; use Faker\Generator; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Support\ServiceProvider; class FakerServiceProvider extends ServiceProvider { /** * Bootstrap services. * * @return void * @throws BindingResolutionException */ public function boot(): void { $faker = $this->app->make(Generator::class); $faker->addProvider(new Planet($faker)); } }app/Faker/Planet.php<?php declare(strict_types=1); namespace App\Faker; use Faker\Provider\Base; /** * 惑星をランダムに返す */ class Planet extends Base { protected array $planet = [ '水星', '金星', '地球', '火星', '木星', '土星', '天王星', '海王星', ]; /** * @return string */ public function planet(): string { return $this->generator->randomElement($this->planet); } }$ php artisan tinker >>> $faker = app()->make(Faker\Generator::class) >>> $faker->planet() => "海王星"モデルファクトリーのリレーション定義
ファクトリークラスにリレーションの定義も行えます。
ただ私個人としては、ファクトリークラスはなるべくシンプルに保ち、シーダークラスに生成ルールを書いた方が読みやすいかなと思ってます。連番を生成する
連番のテストデータを作りたい時は、staticなプロパティを作ると良いです。
class UserFactory extends Factory { private static $sequence = 1; public function definition() { return [ 'name' => sprintf('No. %d %s', self::$sequence++, $this->faker->name), ]; }実行するとこんな感じです。
>>> App\Models\User::factory()->make()->name => "No. 1 青山 明美" >>> App\Models\User::factory()->make()->name => "No. 2 工藤 七夏" >>> App\Models\User::factory()->make()->name => "No. 3 渡辺 稔"$faker->email は使うな! $faker->safeEmail を使え!
$ php artisan tinker >>> $faker = app()->make(Faker\Generator::class) >>> $faker->email => "kyosuke.sasada@gmail.com" >>> $faker->safeEmail => "kondo.yui@example.com"実行結果を見るとわかりますが、
@gmail.com
など実在するドメインのテストデータが生成されます。
開発環境でメールの誤送信を防止するため$faker->safeEmail
を使うようにしましょう。Faker nullデータをランダムで入れたい
->optional()
を使うとランダムでnull値が入ります!public function definition() { return [ // ランダムでnullを入れたい時 'completed_at' => $this->faker->boolean ? $this->faker->dateTime : null, // Fakerのoptional()を使うとランダムでnullを入れてくれます(オススメ) 'completed_at' => $this->faker->optional()->dateTime, // 80%の確率で日付データが入り、20%の確率でnullが入ります 'completed_at' => $this->faker->optional(0.8)->dateTime, ]; }状態に応じたテストデータを作成する
公式サイトに詳しく記述されているので、ここで書く必要ないですがとても便利な機能なのでご紹介します。
※お試しする場合はマイグレーションファイルに下記を追記して、
php artisan migrate:fresh
してください。database/migrations/2014_10_12_000000_create_users_table.php$table->string('account_status');database/factories/UserFactory.php<?php namespace Database\Factories; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; class UserFactory extends Factory { /** * The name of the factory's corresponding model. * * @var string */ protected $model = User::class; /** * Define the model's default state. * * @return array */ public function definition() { return [ 'name' => $this->faker->name, 'account_status' => 'init', 'email' => $this->faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; } /** * そのユーザーが有効化(activated)されていることを表す * * @return Factory */ public function activated(): Factory { return $this->state(function () { return [ 'account_status' => 'activated', ]; }); } /** * そのユーザーが資格保留(suspended)されていることを表す * * @return Factory */ public function suspended(): Factory { return $this->state(function () { return [ 'account_status' => 'suspended', ]; }); } }$ php artisan tinker # 仮登録(init)状態のユーザーを作成 >>> App\Models\User::factory()->create() => App\Models\User {#3346 name: "中津川 あすか", account_status: "init", email: "tanaka.momoko@example.org", email_verified_at: "2020-12-06 18:30:21", updated_at: "2020-12-06 18:30:21", created_at: "2020-12-06 18:30:21", id: 7, } # 有効化(activated)状態のユーザーを作成 >>> App\Models\User::factory()->activated()->create() => App\Models\User {#3343 name: "石田 京助", account_status: "activated", email: "harada.yuta@example.com", email_verified_at: "2020-12-06 18:30:23", updated_at: "2020-12-06 18:30:23", created_at: "2020-12-06 18:30:23", id: 8, } # 資格保留(activated)状態のユーザーを作成 >>> App\Models\User::factory()->suspended()->create() => App\Models\User {#4090 name: "伊藤 稔", account_status: "suspended", email: "taichi.sugiyama@example.com", email_verified_at: "2020-12-06 18:30:24", updated_at: "2020-12-06 18:30:24", created_at: "2020-12-06 18:30:24", id: 9, }モデルの状態によって必要な埋めたいデータは異なるので、状態に応じてモデルファクトリを定義できるのはとても便利です!
db:seed でテストデータをクリアする
先週書いた記事ですが、こちらもやっておくと便利です。
Laravel 8 のリリース記事
Laravel 8の新しい機能を知りたい方はこちらの記事をご覧ください。
- 投稿日:2020-12-08T09:52:02+09:00
Composerパッケージを作ろう その② - Laravelパッケージ編
はじめに
今回はLaravelのComposr自作パッケージの公開手順を書いていきます。
基本的な手順は前回の記事と同様なので、まだご覧になっていない人はこちらの記事から先に読むことをお勧めします。下準備
ディレクトリやファイルの構成や準備などは前回の記事を参考にしてください。
Laravelでパッケージを自作する際はLaravelというFWのコアとなる
サービスプロバイダ
をパッケージ側にも実装します。Laravel5.5以降からは
Package Auto Discovery
という仕組みが導入されており、作成するパッケージのcomposer.json
に以下の様に追記しておくことでサービスプロバイダを自動的に読み込んでくれます。"extra": { "laravel": { "providers": [ "Vendor\\PackageName\\PackageServiceProvider" ] } }
サービスプロバイダ
内でルートを生成したり、作成したルートにミドルウェアを挟んだりすれば、デバッグツールの様なものを作成することも可能です。ちなみに自分は以下の様なパッケージを公開しています。
temori/artisan-browserブラウザ上でターミナル風の画面を表示してルートの確認や
artisan
コマンドを実行するための開発ツールになります。このパッケージでもサービスプロバイダでルートを生やして、ミドルウェア内でhtmlの要素やjs,cssを表示しているviewに追加する形でツールの画面を表示させています。
基本的な構成は前回の記事と同様ですが、Laravel用にディレクトリを若干追加しています。
以下が作成したパッケージのディレクトリになります。
├── artisan-browser │ ├── LICENSE │ ├── README.md │ ├── composer.json │ ├── config │ │ └── artisanbrowser.php │ └── src │ ├── ArtisanBrowser.php #このパッケージの基幹クラス │ ├── ArtisanBrowserServiceProvider.php #サービスプロバイダー │ ├── Controllers #ルートを生成したので、そこで呼び出すコントローラー │ │ ├── AjaxController.php │ │ └── AssetController.php │ ├── Facades │ │ └── ArtisanBrowser.php │ ├── Logs │ │ └── HistoryLogger.php │ ├── Middleware #パッケージのhtml要素を全ページにレンダリングするためのミドルウェア │ │ └── Inject.php │ ├── Renderer │ │ ├── AssetRenderer.php │ │ └── HtmlRenderer.php │ ├── Resources │ │ ├── artisanBrowser.css │ │ ├── artisanBrowser.js │ │ ├── image │ │ │ └── logo.png │ │ ├── vendor │ │ │ ├── draggabilly.pkgd.min.js │ │ │ ├── jquery-1.12.4.min.js │ │ │ └── suggest.js │ │ └── views │ │ └── index.blade.php │ └── helpers.php作成するパッケージによってディレクトリを適宜作成してください。
パッケージの処理を実装する
ここも前回の記事と一緒ですが、もし設定ファイル等で挙動を制御する様な場合、
パッケージ使用者に設定ファイルを編集してもらいたい場面が出てくるかと思います。その様な場合はLaravelにはアプリケーション側にassetを配置する仕組みが存在しているので、それを活用しましょう。
サービスプロバイダの
boot
メソッド内に$this->publishes([ __DIR__ . '/../config/artisanbrowser.php' => config_path('artisanbrowser.php', 'config'), ]);の様な記述をしておくと
# forceオプションはすでにファイルが存在していても上書きでファイルを配置する php artisan vendor:publish --tag=config [--force]で設定ファイルがアプリケーション側へ配置されます。
もしくはアプリケーション側とvendor下のパッケージの設定ファイルをマージすることもできます。
register
メソッド内に以下の様に定義すればパッケージ側の設定をデフォルトとして使用することができ、ユーザーは変更したい値のみオーバーライドすればパッケージ側とアプリケーション側の設定値をマージしてくれます。$this->mergeConfigFrom( __DIR__ . '/../config/artisanbrowser.php', 'artisanbrowser' );詳細は公式のパッケージ開発に詳しく書かれています。
パッケージの実装が完了したらその後の手順は前回の記事と同様です。
最後に
2回に分けて
Composer
自作パッケージの流れを書いていきましたがいかがでしたでしょうか?
ざっくりとしか流れしか書いていませんが、そこまでハードルの高いものではないと少しでも感じてもらえたら嬉しいです手始めに自分が使う用にパッケージを作ってみるのもありかも知れません。
- 投稿日:2020-12-08T09:32:21+09:00
AWS EC2上にDockerコンテナ(Laravel, MySQL, nginx)を起動しアクセスする
環境
- Mac
- Docker
- EC2 (Amazon Linux2, t2.micro)
- nginx
- Laravel
- MySQL
- Github
ゴール
前提
- 今回はお試しなのでEC2は無料利用枠を使えるAmazon Linux2, t2.micro選定
- EC2のインスタンスならびにSSHの接続で使用するキーペアは作成済みとする。
EC2にログインする
xxx.pemにEC2を作成する際に作成したキーペアのファイルパス、(接続先にユーザー名)@(EC2のパブリックDNS)を指定する。
ユーザー名はデフォルトで「ec2user」、EC2のパブリックDNSはEC2の詳細画面で確認する。mac$ sudo ssh -i xxx.pem ec2user@ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/dockerをインストールする
ec2$ sudo yum install docker 読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd 依存性の解決をしています --> トランザクションの確認を実行しています。 ---> パッケージ docker.x86_64 0:19.03.6ce-4.amzn2 を インストール --> 依存性の処理をしています: runc >= 1.0.0 のパッケージ: docker-19.03.6ce-4.amzn2.x86_64 --> 依存性の処理をしています: containerd >= 1.3.2 のパッケージ: docker-19.03.6ce-4.amzn2.x86_64 --> 依存性の処理をしています: pigz のパッケージ: docker-19.03.6ce-4.amzn2.x86_64 --> 依存性の処理をしています: libcgroup のパッケージ: docker-19.03.6ce-4.amzn2.x86_64 --> トランザクションの確認を実行しています。 ---> パッケージ containerd.x86_64 0:1.3.2-1.amzn2 を インストール : $ docker --version Docker version 19.03.12, build 48a66213fe <- インストールされたことを確認docker-composeをインストールする
最新バージョンを公式サイトで確認しインストールする
ec2$ sudo curl -L https://github.com/docker/compose/releases/download/1.27.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 651 100 651 0 0 2141 0 --:--:-- --:--:-- --:--:-- 2134 100 11.6M 100 11.6M 0 0 3734k 0 0:00:03 0:00:03 --:--:-- 5900k $ sudo chmod +x /usr/local/bin/docker-compose <- 実行権限を与える $ docker-compose --version docker-compose version 1.27.0, build 980ec85b <- インストールされたことを確認gitインストール
ec2$ sudo yum install git-all 読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd amzn2-core | 3.7 kB 00:00:00 依存性の解決をしています --> トランザクションの確認を実行していますローカル(Mac)で作成した環境を取り込む
今回はgithub上にあるリポジトリからクローンする
ec2$ git clone https://github.com/xxx.git Cloning into 'typing-app'... Username for 'https://github.com': xxx <- githubのユーザー名を入力 Password for 'https://narita-akk@github.com': <- githubのパスワードを入力 remote: Enumerating objects: 133, done. remote: Counting objects: 100% (133/133), done. [ec2-user@ip-172-31-47-56 ~]$ ls <- クローンされたことを確認 test-projectDockerのデーモンを起動する
ec2$ systemctl status docker.service <- デーモンの状態を確認 ● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled) Active: inactive (dead) Docs: https://docs.docker.com $ sudo systemctl enable docker.service <- デーモンを有効化する Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service. $ systemctl status docker.service <- デーモンが有効化されたことを確認 ● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled) Active: inactive (dead) Docs: https://docs.docker.com $ sudo systemctl start docker <- デーモンを起動する $ systemctl status docker <- デーモンが起動されたことを確認 ● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled) Active: active (running) since 金 2020-09-11 23:03:25 UTC; 5s ago Docs: https://docs.docker.com Process: 3651 ExecStartPre=/usr/libexec/docker/docker-setup-runtimes.sh (code=exited, status=0/SUCCESS) Process: 3638 ExecStartPre=/bin/mkdir -p /run/docker (code=exited, status=0/SUCCESS) Main PID: 3659 (dockerd) Tasks: 8 Memory: 118.4M CGroup: /system.slice/docker.service └─3659 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --default-ulimit nofile=1024:4096 :Dockerコンテナを構築し起動する
ec2$ docker-compose up -d --build Creating network "typing-app_default" with the default driver Creating volume "typing-app_db-store" with default driver Building app Step 1/7 : FROM php:7.4-fpm-buster 7.4-fpm-buster: Pulling from library/php d121f8d1c412: Pull complete : $ docker ps <- 起動されているコンテナを確認 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 734c90dda24a nginx:1.19.2-alpine "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:10080->80/tcp typing-app_web_1 0e545f085ec4 typing-app_app "docker-php-entrypoi…" 2 minutes ago Up 2 minutes 9000/tcp typing-app_app_1 f860eb86350e mysql:8.0 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 3306/tcp, 33060/tcp typing-app_db_1Laravelの初期設定をする
ec2$ docker-compose exec app bash <- Laravelがインストールされているコンテナに入るec2(app)$ composer install <- 必要なパッケージをインストール Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 106 installs, 0 updates, 0 removals - Installing doctrine/inflector (2.0.3): Downloading (100%) - Installing doctrine/lexer (1.2.1): Downloading (100%) - Installing dragonmantank/cron-expression (3.0.1): Downloading (100%) - Installing voku/portable-ascii (1.5.3): Downloading (100%) : $ ls -al <- .env.exampleがあることを確認 total 304 drwxrwxr-x 12 1000 1000 4096 Sep 12 05:33 . drwxr-xr-x 1 root root 33 Sep 12 05:33 .. -rw-rw-r-- 1 1000 1000 220 Sep 12 05:26 .editorconfig -rw-rw-r-- 1 1000 1000 784 Sep 12 05:26 .env.example -rw-rw-r-- 1 1000 1000 111 Sep 12 05:26 .gitattributes : $ cp .env.example .env <- .env.exampleを.envにコピー $ php artisan key:generate <- アプリケーションキーを作成 Application key set successfully. root@3e98c590943b:/work# ls -al <- storageの権限を確認 : total 304 drwxrwxr-x 5 1000 1000 46 Sep 12 05:26 storage : $ cd bootstrap/ $ ls app.php cache $ ls -al <- bootstrap/cacheの権限を確認 total 8 drwxrwxr-x 2 1000 1000 64 Sep 12 05:34 cache : $ cd .. $ chmod -R 777 storage <- storageフォルダに書き込み権限を付与 $ chmod -R 777 bootstrap/cache/ <-bootstrap/cache/フォルダに書き込み権限を付与 :ec2の80番ポートを開けてhttpアクセスを許可する
ec2のセキュリティグループを編集する
ec2にブラウザからアクセス
Laravelが表示できた!
参考サイト
- 投稿日:2020-12-08T09:31:44+09:00
Github Actionsを使って自動テスト(PHPunit)をパスしないとマージできないようにする
自分でもCI/CD環境を構築したい!と思いつつ、CircleCIはチョット敷居が高い・・・。
と思ったら、GithubでもGithub Actionsを使って自動テストができるらしいことを知ったので設定してみました!Githubアクションとは何なのか?
設定ファイル(yml)を元にイベント駆動で処理を実行してくれるCI/CDサービス。
Githubで用意した仮装サーバー(ランナーと呼ぶ)使ってジョブを実行する
個人利用の範囲なら無料で利用できる。手順
1.アカウント連携
2.ワークフローファイル(./github/workflows/test.yml)を作成(編集)
3.プルリクを立ててActions実行の確認
4.ブランチ保護設定でワークフローを指定
5.テストに失敗するプルリクを立ててブランチが保護されているのを確認アカウント連携をする
Github Actionsはリポジトリ管理の拡張機能なので、普通にgithubを使っている方でも利用のための設定をする必要があります。
といってもそれほど難しいことはなく、githubにログインした状態でリポジトリを閲覧できるページとかで表示されるGithub Actionsボタンを押すだけです。
*ここに画像挿入
これでアカウントとしてGithub Actionsを使えるようになりました。
ワークフローファイルの作成
アカウント連携をした後でリポジトリページを開くと、タブメニューに
Actions
が追加されているので、これをクリックします。
すると、↓のようにリポジトリの内容からワークフローの内容をサジェストしてくれているはずです!
Set up this workflow
ボタンを押すと、ymlのテンプレートが表示されます。ymlファイルの項目解説
name: Laravel on: # masterブランチへのpush時 push: branches: [ master ] # masterブランチへのプルリク作成時 pull_request: branches: [ master ] # jobs: laravel-tests: runs-on: ubuntu-latest steps: # 自動テスト用.envのを作成(.env.exampleをコピー)し、ライブラリインストール - uses: actions/checkout@v2 - name: Copy .env run: php -r "file_exists('.env') || copy('.env.example', '.env');" - name: Install Dependencies run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist # laravelの初期設定コマンド - name: Generate key run: php artisan key:generate - name: Directory Permissions run: chmod -R 777 storage bootstrap/cache # テスト用DB作成 - name: Create Database run: | mkdir -p database touch database/database.sqlite # .envにテスト用環境変数を書き込みphpunit実行 - name: Execute tests (Unit and Feature tests) via PHPUnit env: DB_CONNECTION: sqlite DB_DATABASE: database/database.sqlite run: vendor/bin/phpunitファイル内にコメントを書いてますが、
各項目の役割はこんな感じです↓
項目 役割 name ワークフローの名前 on どんな操作があった時にジョブを起動するか jobs ワークフローで行われるジョブのグループ runs-on ジョブを実行するランナー(CI/CD処理を行うサーバー)の設定 steps ジョブで段階的に実行される処理 uses コマンドを再利用可能な形に定義したもの。ワークフロー、パブリックリポジトリ、公開Dockerコンテナイメージで定義されているアクションを指定・使用できる name 各stepsの名前 runs stepsで実行するコマンド env ワークフローを実行する時の環境変数 CI/CDを自分の手で設定したので備忘録として書いておくと、
ワークフロー・ジョブ・ステップの違いは
・ジョブはファイルによって実行されるCI/CD処理(自動テスト)内容のこと
・ステップはジョブを実行するための段階的手順のこと
・起動イベントを含めた処理全体をワークフロー
と捉えておけばいいのかな、と思いました。ワークフロー実行の確認
ymlファイルを作るページからそのままプルリクエストを作りましょう。
※ テンプレートをそのまま使った場合、マージ先がmaster
に向いてないとワークフローが実行されないので注意また、Actionsタブからもワークフローの経過・結果を確認することができます。
ブランチ保護設定
最後に、ブランチ保護設定(ステータス)でワークフローと結びつける設定を行います。
Settings画面の左メニュー
Branches
を選択後、
Branch protection rules
>Add Rules
をクリック。・
Branch name pattern
に保護したいブランチの名前
・Require status checks to pass before merging
にチェックを入れ、先ほど追加したワークフローを選択
・管理者にも保護ルールを適用させたい場合はInclude administrators
にチェック
をして、画面下部のCreateボタンで設定を確定します。テスト失敗時にマージできないうことの確認
設定は全て完了したので、最後にテストコードが失敗した時にマージできなくなっていることを確認します。
class ExampleTest extends TestCase { /** * A basic test example. * * @return void */ public function testBasicTest() { $response = $this->get('/'); // 200でパスするケースを失敗するよう編集 $response->assertStatus(300); } }↑をpushした後にプルリクのマージボタンを確認すると、マージできなくなっているのが分かります。
また、自分の場合は
Include administrators
にチェックを入れてなかったため、Use your administrator...
にチェックを入れて制約を無視したマージができるようになっています。補足
・設定に使うymlファイルの名前はなんでもOK。Githubは
./github/workflows
にあるymlファイルを読み込んでGithub Actionsに使用する
・パブリックリポジトリである必要がある
・sqlite以外のDBを使用する場合はymlファイルの編集が必要参考資料
Github Actions公式リファレンス
Github Actions 8ヶ月使ってみてわかったことまとめ
【Github Actions】プッシュ時にgithubに自動でテストを実行してもらおう!
GitHub Actionsを使ってLaravelアプリケーションをCI/CDする