Laravel Sailで記事投稿フォームを作成してみた

こんにちは。22年度入社の中島です。

前回はLinuxについて書きましたが、今回は少し前に学習していたLaravel Sailについてまとめました。

●Laravel Sailとは
Dockerを使った開発環境であり、ターミナルでコマンド実行するだけで、DockerでLaravel環境を一発で作ってくれる便利なコマンドラインツールです。
Laravel Sailを使うメリットは次の通りです。

・開発に最低限必要なツールを一度にインストールでき、インストールの手間を省ける
・本番環境と同じ環境を手元に用意して、動作を確認できる
・PHPのバージョンが異なる複数のプロジェクトの管理が楽にできる
(Laravel 8.x からLaravel Sailは標準でインストールされています。)

●なぜLaravel Sailを使っているのか?
Laravelの学習するときに使用していた技術書がLaravel Sailでの環境構築、実装方法を紹介しているのを見て、docker環境との連携も取りやすいと思ったため、復習を兼ねてSailの紹介をやってみようと思いました。


使用したテキスト:『Laravelの教科書』

Laravel Sailのセットアップ及びLaravelトップ画面の表示
1.Docker Desktopをインストール
Dockerのインストールです。Dockerの公式サイトからDocker Desktop のインストーラーをダウンロードします。

2.Laravel Sailをインストールするディレクトリを予め作成しておきます。
今回はtest-projectフォルダを作成しました。

3.Laravel Sailのインストール
Ubuntuまたはターミナルで、次のコマンドを実行します。(今回はUbuntuを使用)

4.途中でパスワードが求められたらPCのパスワードを入力。

5.下記メッセージが出たらインストール完了です。

6.「test-project」ディレクトリへ移動します。

7.Dockerコンテナを起動します。

これによりLaravel Sailが使用する複数のDockerコンテナが起動します。
起動したコンテナについて、Docker Desktopのウィンドウで確認してみましょう。

※./vendor/bin/sail コマンドを毎回入力するのは大変です。
これを「sail」だけで使えるようにするため、エイリアス登録を行います。

以降は sail コマンドとして記載していきます。

8.マイグレーションコマンドを実行して、初期テーブルを作成する。

9.ブラウザで「http://localhost/」 へアクセスするとLaravelのスタート画面が表示されます。※Ubuntuまたはターミナルを開いたまま、ブラウザでアクセスしてください。

今回は、Laravelの復習として過去に作成したフォームの一部を紹介したいと思います。
『件名』『本文』にテキストエリアがあり、フォームの下に『送信する』ボタンがあるフォームを作成していきます。

1.モデルとマイグレーションファイルを作成
まずは、モデルとマイグレーションファイルを作ります。下記コマンドを実行して、Postモデルとマイグレーションファイルを作ります。

このコマンドを実行すると、database/migrations 配下と app/Modelsにファイルが作成されます。次にdatabase/migrations 配下に作成されたマイグレーションファイルに、postsテーブルを作成するためのtitleカラムとbodyカラムを設定します。

./database/migrations/(年)_()_(日)_(時刻)_create_posts_table.php

マイグレート実行し、データベースにpostsテーブルを作成

2.ビューファイルの作成
次にビューファイルを作ります。resources/viewsの中にpostフォルダを作り、その中に create.blade.phpファイルを作ります。create.blade.phpファイルの中には以下のコードを追加します。

create.blade.php

3.ビューファイル表示用コードを追加
次にコントローラです。下記コマンドを実行して、PostControllerを作成。

コマンド実行後、app/Http/Controllerの中のPostController.phpを開きます。先ほど作成した resources/views/post/create.blade.phpファイルを表示するためと投稿データ保存のために、下記のように記述します。

PostController.php

4.ルーティングの設定(ビューファイル表示用のルート設定)を追加
ルート設定の作成です。routes/web.phpの中に、下記のuse宣言とフォーム表示用のルート設定と投稿データ保存用のルート設定を加えます。これにより今回作成したフォームがブラウザに表示されます。

web.php

これまで記述したコードによって、次の流れが実現します。

  1. ユーザーがログイン後にhttp://localhost/postにアクセスする
  2. ルート設定により、PostControllerのcreateメソッドに処理が割り振られる
  3. 処理が実行され、resources/views/post/create.blade.phpの内容がブラウザに表示される

    実際に作成したもの

4.件名(title)と本文(body)を入力し『送信する』ボタンを押すことで、PostControllerにフォーム(http://localhost/post)を通じて値が送信される
5.ルート設定により、PostControllerのstoreメソッドに処理が割り振られる
6.処理が実行され、件名が$request->titleに、本文が$request->body にそれぞれ入っているため、Post::create() に渡してやることでpostsレコードの対応するフィールドに値がセットされる
7.returu back()によっての元ページに戻る

いかがでしょうか。

今回は振り返る形でLaravel Sailのことを書きました。
今後も学んだことを忘れないようにLaravelやLinux、業務で行った作業なども復習する形でブログ更新していきたいと思います。

今回はここまで
最後までご覧いただきありがとうございました!

ヘルパー関数のあれこれ!紙屋03

おつかれさまです!
‘23年度入社の紙屋です!
9月に入って朝晩の暑さがすこーーしずつ和らいでいたような気がしますね。早く日中の暑さも和らいでいくといいですね。

8月で課題の会員登録のアプリケーションを作り終えました。
外部研修でもLaravelでwebアプリケーションをつくりましたが、そのときはとりあえずの実装を優先して、「この関数がどんな仕事をしているのか、どんな機能があるのか」ということを考えていませんでした。
そこで、今回はLaravelにおけるヘルパ関数に焦点をあてて振り返ろうと思います!

ヘルパ関数とは?
Laravelに搭載されている独自の関数のことで、いろいろなところで呼び出せる便利な関数です。

Laravelの公式ドキュメントをみえると数えただけで213個ありました(今回Laravel ver9で実装)。
まだまだ分からないことが多いので、今回は使ったものに触れながら振り返ります!
今回の会員登録フォームで使用したヘルパ関数は、
・view ・redirect ・route ・request ・session
・old ・dd ・dump ・config
でした。
Contollerや入力フォーム、処理の確認で使うものが多く、外部研修でも使っていたので、コードレビューいただく前から使っていました。しかし、config関数は教えていただいてからの導入でした。

よくアドバイスいただく内容として、同じコードで内容量が多く、その処理を複数個所で使用する場合は、可読性や再利用性を考えてコーディングした方が良いと教えていただきます。
今回config関数をどこに用いたかというと、formタグで会員情報の登録や編集を行う箇所でバリデーションを実装するときに、バリデーションの中身は同じものなので、バリデーション時にコードが冗長にならないように利用しました。

まずは、config関数の公式ドキュメントを確認します。
config関数は設定変数の値を取得します。設定値はファイル名とアクセスしたいオプションを「ドット」記法で指定します。
とあります。

となっており、「/config/(.phpファイル)」内に記述されている配列を呼び出します。
では、私はどのように実装したかというと、
Controller内には以下のように記述し、

「/config/mySetting.php」ファイル内に

と記述しました(教えてもらいながらですが…)。

これでバリデーションチェック時の項目をController内に何度も記述する必要が無く、バリデーション内容の変更・修正時も配列の中身を記述することでスッキリします。

ちなみに、ヘルパ関数がどんな処理をしているのかは、「/vender/laravel/framwork/src/illuminate/Foudation/helpers.php」ファイル内に記述してありました。

確かに配列があればキーを返してくれているみたいです。

ちなみに、今回使用した「ドット記法」についてちょっと触れると、配列からの値の取得時にキーを「.」でつなげることで、多次元配列の場合、より深い階層へネストし値を取得してくれます。
例えば、Arr:get()関数の公式ドキュメントを参照すると、

となっています。$arrayのような多次元配列から値を取得したい場合は、第2引数のキーの部分をドット記法で、欲しい値までネストしていきます。
ちなみにArr::get()関数に第3引数をセットすると、第2引数に指定したキーが存在しない場合の値を返してくれます。

改めて振り返ってみると、「なるほど!」と思いました!
今後も、いろいろと忘れないようにまとめていきたいと思います。
では、また次回!!


パスワードはハッシュ化する!!紙屋02

こんにちは!
’23年度入社の紙屋です!
日に日に暑さが増すなか、台風もやってきて大変ですね。
私は出身が鹿児島県なので台風には慣れっこです。
学生のうちは台風が来ると「学校が休みになるんじゃないか」とそわそわしていましたが、社会人になると「台風きたら出勤どうしよう…」と不安でそわそわしてしまいます(笑)

現在は、課題として会員登録フォームをPHPでの実装、その後フレームワークを使った実装がひと段落終えたところです。調べながら、分からないところが教えていただきながらの実装だったので、ちょっとずつまとめをしていこうと思います。

まずはとりあえず機能の実装を!と考え、新規登録フォーム、検索機能、編集・削除機能を実装しました。その中でセキュリティ対策への考えはまだまだ足りず、データベースへ登録する際に、エスケープ処理を行わなければならないことは覚えていましたが、パスワードに対しては何も処理をしていませんでした。
レビューいただいた際に、「え・・ハッシュ化って?」「まだ処理がいるの?」と混乱しました…。なので、今回はハッシュ化について調べてみました。

今回、PHPプログラミングで実装した内容は、

です。
最初は直観的に

をそのままDBに登録していました。

レビューいただいた際に、このままでは、DBを攻撃された場合、個人情報が盗まれてしまう危険性があると教えていただきました。

確かに!ログイン情報を盗まれてしまったらなりすましなんて簡単にできますね。

password_hash関数をマニュアルで調べてみると、


◦$passwordには登録するパスワードの値が入って、
◦$algoにはハッシュ化に使用するパスワードアルゴリズム定数が入って、
◦$optionsには各アルゴリズムがサポートするオプションが入る
とのこと!

アルゴリズム定数のPASSWORD_BCRYPTを使うと
◦”$2y$” crypt フォーマットを使ったハッシュになる
◦長さは常に 60 文字
◦オプションのcostは省略時はデフォルトの10が入り、ハードウェアの性能が許容できるなら高くも設定可

とのことでした。
上記のpassword_hash()関数で登録しなおすことで、

ちゃんとハッシュ化されていました。
これで一安心です!

知らないことが多くて調べて、コーディングして、エラーと闘う日々です。
今後も、いろいろと忘れないようにまとめていきたいと思います。
では、また次回!!

VeeValidateでデフォルト値検査を外す話

福岡開発センターの野田です。
年度末ということで忙しい日々を過ごしています。

弊社では、Vuejs/Nuxtjsを使った開発を行っています。フロントでの値検査を行う場面も増えてきたと思います。ある案件でフロントでの値検査にVeeValidateを利用していたのですが、カスタマイズした検査が有効にならず何でだろうと調べてみました。有効にならなかったのは、Inferred Rules というものが有効になっていてためであることが分かりました。

Inferred Rules

https://vee-validate.logaretm.com/v2/guide/inferred-rules.html#example

この Inferred Rules ですが、例えば type=”email” に対して VeeValidate をかけると何もしなくても email のバリデーションルールが適用されるというものになります。

対象となるinput属性と値は以下になります(上記サイトより抜粋)。

AttributevalueRule
type“email”
type“number”
type“date”
type“datetime-local”
type“time”  or   depending on the step value
type“week”
type“month
minval
maxval
patternrgx
requirednone
maxlength“val”
minlength“val”

これを無効化する方法は以下のコードとなります。useConstraintAttrs: false のオプションを指定することでInferred Rulesの設定を無効化することができます。Inferred Rulesはカスタムルールより優先度が高いため、結構扱いが難しいな、と感じています。どこでも使われるようなもののためそういう実装になっているのでしょうけど、メッセージをカスタマイズしたいときは足かせになりました。

[/crayon]

こういう設定調整ができるのは、しっかりポイントが抑えられていて人気のライブラリであることを感じます。また何かTipsが出てきたときは紹介したいと思います。

[bug]マルチプロセスでlog4netのファイルローテーション時に一部ログが欠損して困った

福岡の香月です。

log4netのRollingFileAppenderを使って日付で出力ファイルが切り替わる設定をしていたところ、日付が変わって新しいファイルへの出力が始まると先頭に出力されるであろうログがなぜか出力されない現象に遭遇しました。
設定はこうです。
複数のプロセスで同じ設定を用いて、同じファイルに対してログ出力します。

[/crayon]

AppenderはRollingFileAppenderを指定しています。複数プロセスで使用するため、lockingModelはInterProcessLockを指定します。これによりファイルストリームを開きっぱなしにして、mutexで排他制御しながら末端にシークし書き込みを行うことで、複数プロセスから効率的にログ出力を行ってくれます。
複数プロセスから使う場合はこれ以外にもMinimalLockを指定できますが、書き込むたびにファイルのオープンクローズを実施するため速度が求められる場合は不向きでしょう。さらに複数プロセスからの出力が同時に走った場合、片方はオープンできないためログが出力されません。

この設定でビルドし、業務終わりにアプリを起動して一晩中実行させ、翌朝出社して確認しても日付が変わった後のログの内容が足りませんでした。
最初はlog4netの使い方が悪いのか、自分のプログラムにバグがあるのかさんざん悩んだんですがさっぱりわからない。トレース用に埋め込んだログも当然のように出ていないのでどうしたものかと思いました。

しかし、プロセスが1つの場合はちゃんと期待通りのログが出ていました。問題があるのは複数プロセスの場合だけだったのです。

そこでlog4netのロジックを確認することにしました。 幸いなことにソースはこちらから取得できます。
https://github.com/apache/logging-log4net
すると、ファイルが切り替わったあとは最後に書き込んだプロセスのログが先頭に来て、それ以前のログが破棄される作りになっていたのです。

少しずつソースを見てみましょう。今回使っているのはRollingFileAppenderです。日付が変わるときにファイルを切り替える部分は、RollOverTime()という関数であることがわかりました。こうなっています。
https://github.com/apache/logging-log4net/blob/master/src/Appender/RollingFileAppender.cs

[/crayon]

ふむふむ、SafeOpenFile()で新しいファイルをオープンしに行くわけね。そして第二引数がfalseと。そしてSaveOpenFile()の実態はベースクラスFileAppenderにありました。
https://github.com/apache/logging-log4net/blob/master/src/Appender/FileAppender.cs
ここから次の順番で呼び出されていきます。

  • RollingFileAppender.OpenFile()
  • FileAppender.OpenFile()
  • FileAppender.InterProcessLock.OpenFile()
  • FileAppender.LockingModelBase.CreateStream()
[/crayon]

ここでSafeOpenFile()の第二引数で指定されたfalseは、CreateStreamのappendに代入されていることがわかりました。この場合FileMode.Createが指定され、FileStreamインスタンスが作成されます。
ではこのFileMode.Createはどのような動きをするかというと、

オペレーティング システムが新しいファイルを作成することを指定します。ファイルが既に存在する場合は上書きされます。これには Write 許可が必要です。
FileMode.Create は、ファイルが存在しない場合は CreateNew を使用した要求、ファイルが存在する場合は Truncate を使用した要求と等価です。

https://docs.microsoft.com/ja-jp/dotnet/api/system.io.filemode?view=netcore-3.1

複数のプロセスでログ出力を行う場合、FileStreamを最後に作成するプロセスによりそれまでのプロセスが作成したログファイルの中身がTruncateされて、つまり削除されてしまうわけです。
発生している現象とも合致するのでこれが問題であることを確信しました。

対応するためにはRollingFileAppender.RollOverTime()から呼んでるSafeOpenFile()の第二引数をtrueに知ればよいわけです。実際に修正して確認してみると、見事欠損することなく必要なログが出力されていました。
めでたしめでたし。

なお、SafeOpenFile()はRollingFileAppender.RollOverSize()内からも呼び出しているのでここも忘れずにtrueに変更しましょう。

C#でWOL(Wake-on-LAN)

福岡の香月です。

コロナ対応で世の中はリモートワークが進んでいるようです。中程度スペックの中古PCもたくさん売れているようで、在庫が少なくなっているようですね。

自宅作業する場合、会社のPCにリモートデスクトップでつないで作業することも多いと思いますが、気付いたらPCがシャットダウンしていることがあります。間違って電源切ったとか、夜中にWindows Updateが動いたとか。

そんな時はWOL(Wake-on-LAN)ですが、私はこれについて知識が無かったので調べがてらC#プログラムのWOLツールを作ってみました。プラットフォームは.Net Core 3.1です。
以下全文です。どん!

[/crayon]

特徴

  • マジックパケットを飛ばします。マジックパケットとは…
    • UDPで送信するパケット
    • 先頭6バイトに{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
    • 続けて対象PCのmacアドレスを16回
    • 合わせて102バイトのパケットとなる。
  • exeと同じ名前のiniファイルを設定ファイルとして読み込む
  • 設定ファイルにて以下を設定
    • host=対象PCのIPアドレス
    • post=UDPポート(ルータが許可したポートであれば何でもOK)
    • macアドレス=対象PCのmacアドレス
  • エラーが発生した場合はコンソール上にエラーを表示

プログラムは案外簡単でびっくりしました。
それよりもWOLが使える環境の設定が大変そうですね。BIOS、ネットワークカード、Windowsの設定など、情報はぐぐればいろいろ出てきますので一度お試しあれ。

Swagger-PHPでOA\PathItem()エラー

福岡拠点の野田です。
OpenAPI(Swagger)の仕様書を使っていますか?

PHPでは、アノテーションでOpenAPIを定義できるSwagger-PHPというものがあります。使うためには以下を実行します。

[/crayon]

対象のコントローラーを指定するとアノテーションで記載したコメントがAPI仕様書になるという素晴らしいライブラリです。swagger.ymlの書き出し方は、以下。

[/crayon]

ここで少しはまりどころがあります。
OA\Infoを定義したクラスには必ずOA\GetやOA\PutといったAPIをセットで定義しなければなりません。それをしないと以下のエラーで怒られます。

[/crayon]

ちょー分かりにくいエラーなので、私もはまりました。そんなときは汎用エラーレスポンスでも定義してしのぎましょう。

[/crayon]

これはライブラリ側でなんとかしてほしかったところではありますが、めげずにswaggerを使っていきましょう。

それでは、また!

nuxt.js環境を構築する

福岡拠点の野田です。

サーバーサイドでレンダリングするSSR(Server Side Rendering)を次の案件で使うことにしました。API側は慣れ親しんだLaravel。

最初に悩むのが環境構築。
どうしようか、というところで参考にしたのが、次の記事。

LaravelとNuxt.jsを同一レポジトリで管理するときの構成https://www.wantedly.com/companies/roxx/post_articles/84615

client フォルダ以下に環境を作る例ですが、とても参考になりました。以下おっかけになりますが手順になります。

[/crayon]

nuxtは起動フォルダ配下に.nuxtという一時ファイルを作成します。このため設定ファイルをclient以下に配置して、このフォルダを起動フォルダとします。

設定ファイル (nuxt.config.js)

[/crayon]

起動シェル(再起動シェル)は以下のような感じで作ります。

[/crayon]

ちなみにecho “” | はjenkinsから呼び出したとき、標準入力を要求されたので、それ対策で入れています。

このシェルを実行することでnuxt.jsサーバーが起動します。

nuxtは素のvueでやるよりも以下の点で優れていると思います。

  • 自動でルーティングしてくれる。
  • レイアウト機構を持っている。
  • API連携の仕組みも実装しやすい形で持っている。

VueやReactやAngularは、マイクロサービスをつなげる糊の役割をしているフレームワークだと思います。これからもっと深く使っていきたいと思います。

JQコマンドをおぼえた

福岡拠点の野田です。久しぶりの投稿です。

ある案件でAWSを扱っており、ELBからインスタンスが切り離されたか確認してほしいと連絡がありました。生憎、AWS Consoleへの接続アカウントをもらっていない状態で調べられない…と思ったのですが、aws-cliがインストールされてあるサーバーがあることに気づき、そちらで確認してみました。

ELBの一覧を表示するコマンドは以下のようなものになります。

[/crayon]

けれども、awsのコマンドで出てくる内容はjsonで縦に長い!必要な情報だけに整理したいときに役立つのがjq(恐らくjson queryの略)コマンドになります。jsonの中からDNSNameと紐づくインスタンスを見たい場合は、以下のようなクエリをjqに投げます。

[/crayon]

jqコマンドの基本は、配列のフィルターと生成。.から始まる要素がフィルターされる内容になります。{}や[]で配列を再構築します。詳しいやり方は以下をご参考ください。

すぐれものなのは、jsonからcsvを作れること。以下のような感じでフィルターと配列の再構築を利用すれば、@csvでcsvの作成も楽々です。

[/crayon]

zabbixと連携させたりとか、 レポート出力を簡易化できないかとか夢が広がりますよね?是非、jqを使ってより良いjsonライフを!

[Python]拡張モジュールをWin Debug版DLLで試すとエラー

福岡拠点の香月です。

Windows環境で動作するPython3.6.6の拡張モジュールをC++で作りました。
開発ツールはVisual Studio 2015です。
早速作成した拡張モジュールのDebug版を使ってみようとPythonインタプリタを起動してモジュールロードしたんですが゙………

とこのようにエラーが発生してしまいました。インタプリタも強制終了しているようです。

これは拡張モジュールがDebug版なので、Pythonバイナリもデバッグ版を使う必要があります。python_d.exeがpython.exeと同じ場所にあるのでそれを使いましょう。
また、拡張モジュール名にも「_d」が必要です。Release版が「spam.pyd」ならDebug版では「spam_d.pyd」です。リネームで十分です。

このようにFatal Python Errorは発生せずに続行できます。
Debug版でのデバッグをあきらめて、Release版に無理やりデバッグ情報をつけてデバッグしていた方、是非これをお試しあれ。

これに気付く前にVisual Studio 2017のドキュメントで次のようなものを見つけていました。
https://docs.microsoft.com/ja-jp/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2017
以下抜粋

警告

デバッグ構成の場合でも [C/C++] > [コード生成] > [ランタイム ライブラリ] のオプションを常にマルチスレッド DLL (/MD) に設定します。これは、この設定がデバッグ以外の Python バイナリのビルドに使用されるためです。
マルチスレッド デバッグ DLL (/MDd) オプションを設定すると、デバッグ構成をビルドするときに、”C1189: Py_LIMITED_API は Py_DEBUG、Py_TRACE_REFS、Py_REF_DEBUG と互換性がありません” というエラーが表示されます。 さらに、ビルド エラーを避けるために Py_LIMITED_API を
削除すると、モジュールをインポートしようとしたときに Python がクラッシュします (後で説明しますが、クラッシュは DLL のPyModule_Create の呼び出し内で発生し、出力メッセージは “Fatal Python error: PyThreadState_Get: no current thread (Python 致命的エラー: PyThreadState_Get: 現在のスレッドがありません)” です)。
/MDd オプションは Python デバッグ バイナリ (python_d.exe など) のビルドに使われますが、拡張 DLL に対して選ぶと、やはり Py_LIMITED_API のビルド エラーになります。

でもこんなことする必要なく、python_d.exeを使うだけでOKですよー。