20191212のlaravelに関する記事は10件です。

フルスタックフレームワーク(Laravel)を触り始めて知ったことまとめ

はじめに

はじめまして!
専門学校3年の@tk_zawaです。

AdventCalendar初参加な為緊張&前日までの皆さんガチすぎこわ…ってなりながら書いてます。よろしくお願いします。

私は半年前までほぼ生でPHPを書いていました。それから学校の授業等でLaravelをほんの少し触り始め、それから自主制作の中でガッツリ使うようになりました。
今回の記事ではLaravelについて調べながら使っていた中で、個人的に感動した機能について書いていこうと思います。(機能の使い方のガッツリした解説は今回はしません)

紹介するのはフレームワーク全般に言えるような機能だったり、ただLaravelに付属されてるだけのライブラリだったりと色々ツッコミどころ満載ですがゆるして…

機能の紹介

1. ORM

それまでSQLクエリをPHPファイルにPDO(PHPDataObjects)を使って直書きしていた中で、
データベースのテーブル構造を1つのクラスとして定義してそれに対してメソッドを展開していくというところが、学び始めた当初はマジで理解が進まなくてめちゃくちゃ困惑してました。
しかし色々触っていった結果、テーブルのリレーション操作というものがめちゃくちゃ便利だと気づき、試行錯誤の中で一気に理解が進みました。
特に、1対多や多対多な関係を持つテーブル群から、SQLクエリ直書きでは(数行程度じゃ)到底取り出せないようなキレイな形のデータを取得できるところと、
多対多な関係にあるテーブルの間に挟むPivot(中間)テーブルの操作を簡潔に書けるところが自分の中でアツかったです。

2. Bladeディレクティブ(ページング)

生PHPで使っていた頃自作していたテンプレート継承機能やループカウンタ変数が最初から用意されていたことでかなり捗りました。
(Bladeディレクティブの特徴について振り返ってみると、ただコードの見た目が簡潔になっただけでは…みたいなことばっかりなんですが、
それでもなんとなくフレームワークを使いこなせてる感が自分の中で生まれてモチベーションを上げてくれたのが個人的に推したい点)

中でも、色々あるBladeディレクティブの中で感動どころか便利すぎて怖っ…てなったのが、ページング機能でした。
Laravelでページングを実装したければ、サーバ側で前述のORM(Eloquent)を叩いて取得したインスタンスで
paginate(ページ数)というメソッドを叩いて、フロント(Blade)側でインスタンス変数->links()と書くだけで
スクリーンショット 2019-12-12 17.28.06.png

↑が作れる。えっ簡単すぎこわ…(ボタンのデザインはもちろん弄れる)

3. 認証機能

Laravelが入ったディレクトリ上で$ php artisan make:auth(Laravel5.8以下の場合)と打つと
アカウント登録ページ、登録/ログイン処理機能、アカウント用テーブルのマイグレーションファイルその他諸々が一括で生成されます。
後はログイン後の遷移先、Sessionに関する設定をルーティングファイル(routes/web.php)や自作のController等に盛り込むだけ。

それまで自分で登録/ログイン機能を全部イチから作っていたので、今までやっていたのって無駄…?ってなるくらいビビりました。
(この機能については拡張方法等についてまだ調べている途中で使いこなせていないのは内緒)

4. Vue/React連携(+Laravel Mix)

LaravelはデフォルトのJSフレームワークとしてVueを採用しており、アプリのルートディレクトリ上でnpm install(Reactの場合事前に$ php artisan preset reactと叩いておく)と打って、
resources/js/app.jsをゴニョゴニョするだけでVue/Reactを使えるようになります。(ゴニョり方についての詳細はGoogle先生にて)

JSフレームワークを連携させる際はそれらのファイルをビルド(1つのJSファイルにする)して使うのですが、
そのビルド方法の設定ファイル(webpack)の記述が難しく、それを初心者でも書きやすく/分かりやすくしてくれるLaravel MixというツールがLaravelには初期搭載されてます。(Laravel Mixはその他のフレームワークでも使用可)
webpack等についてあまり詳しくは把握してないんですがとにかく、Laravelはフロントエンド技術導入へのサポート体制が整っているのがマジで初心者に優しいと感じた。
(VueでもReactでもCDN引っ張ってきて直接使うことももちろんできますが、そうなるとBladeディレクティブと書式が喧嘩したりとか色々支障を来すのでLaravel Mix経由で使うことを全力で推奨)

npmの話になるけど$ npm run watchが便利すぎ…保存する度にビルドしてちゃんと動くかどうか見てくれるのやべー…

おわり

この記事は、今年の8月にTechBowlさんのイベントに参加させてもらった際にスーさんに直近の自分の将来像設計をサポートしていただいて考えついた、
「今年中にはLaravelについて他人に教えられるくらい詳しくなる」という像に辿り着けました、という報告も兼ねて書きました。
本当にあのイベントを企画されたTechBowlさんには感謝しかない…

これからも"""自走力"""を大事にしていきながらエンジニア生活頑張ります!ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel×テスト駆動開発で初テストを書いてみた

この記事は G's ACADEMY Advent Calendarの14日目の記事です。

はじめに

初心者がTDDから始めることに対して賛否あるかと思いますが、テストに関してずっとモヤモヤしていた私にはこのアプローチがしっくりきたので、そのご紹介です。
ちなみに、そのモヤモヤについての考察はこちらの記事をご覧ください。

なぜテストが書けなかったのか?初心者がLaravelで初テストを書くまで

想定読者

この記事ではLaravelを使ったアプリケーション作成を前提として、テストに関わること以外の基礎的なところにあまり触れずに話を進めます。

  • Laravelの環境があり、基本的なCRUDが実装できる
  • DBのマイグレーションやシーダーなどが説明できる

上記のような最低限のLaravelのお作法が理解できている方を想定します。
その上で、

  • テストを書くのに興味あるけど、何から始めればいいか分からない人
  • テスト駆動開発(TDD)を試してみたい人

に読んでいただけるといいのではないかと思います。特に何から始めればいいか分からなかったのは、まさにここ数ヶ月の私のことです!

用意するもの

PHPフレームワークLaravel Webアプリケーション開発という本を使いました。私は社内の有志でやってる勉強会での課題図書だったので購入しました。

この本は、有名な青い本と比べると少しレベルが高いようです。
(実は青い本、読んでないんですが、社内のPHPerの強い人たちがこの本は初心者向きじゃないよって言ってました)

でも、ぜひ11章だけは手を動かしながらやってみて欲しい!

「11章 テスト駆動開発の実践」という章は、ここだけで完結しているので前の章をやっていなくても大丈夫な構成になっています。
私が、この本の11章でTDDをやって見て、いいなと思ったところはこんな感じです。(本の特徴というよりTDDの特徴によるところも混ざっている気がします。。。)

  • テストありきで設計していくのでテストを書く過程が追える
  • 小さく小さくテストを書いていくのでテスト自体が単純で書きやすい
  • TDDの基本的な考え方を手を動かしながら知ることができる
  • テストに関するLaravelの機能をざっくり知ることができる
  • テンポよくテストが書けて気持ちいい
  • 通してやるとTDD良さそう感が体験できる
  • 何より、テストを書くための準備について触れてくれていた

TDDはそれだけで本一冊になるテーマなので、私が知らないだけで良書はたくさんあると思います。ただ手軽さという点でもたまたま読んだこの本は、私には取っ掛かりとしてとても良かったです。

この本の11章でできること

  • 単純なアプリケーションでTDDの実践
  • APIテストの実装
  • テスト用のDBの用意(シーダーの実行)
  • DBテストの実装(CRUDとバリデーション)
  • 最低限の実装からフレームワークのお作法に近い形の実装への変換(コントローラーへのせかえ)
  • コントローラーからさらにサービスクラスへの分離

詳細な説明は書籍に譲りますが、こんな感じで11章は結構やることは盛りだくさん!
なのですが、TDDのテンポに乗ってコードを書いていくと、なるほどなるほどー!!という感じでサクサクいけて、私はテスト駆動楽しいなーって思えました。
おかげで、テストに対して何から手をつけていいのやら、、、と途方に暮れる感覚、苦手意識はどっかに行ってしまいました!
これをやってみてテンションが上がったら、「9章 テスト」の章に戻って自分で勉強してみるといいかもしれません。テストについては9章の方がもう少し細かく、いろいろ書いてあります。

ということで以下では、

  • TDDの進め方の概要
  • テストで使った機能の一部を紹介

をまとめていきます。

基本的なTDDの進め方

本文の中では、TDDは次のような手順で進めていきます。

  1. アプリケーションを想定したテーブル構成とAPIの設計をする
  2. テストを書くまえにAPIの各エンドポイントで実行可能にするべきToDoリストを作る
  3. テストファイルの作成
  4. 先に検証のためのテストコードを書く(テストは失敗する)
  5. 次に実行部分のテストコードを書く(テストは失敗する)
  6. 最低限の実装を書く
  7. テストが通るか確認する(テストは成功する)

1〜3で下準備をして、4からやっとテストを書き始めます。
実装は6で書くので当然4と5の2つのテストは失敗します。でも失敗することでやるべきことが明確になり、リズムが生まれるそうです。そして次のToDoに対してまた4〜7を繰り返しながらテストと実装を書いていきます。これでToDoリストを潰していくのがTDDの開発の流れです。

実はこの、2つ目のToDoリストを作るというのがポイントです。
テストが書けなくて戸惑っていた私は、まさにここをしっかり言語化できていなかったのでした。
しかもテスト書ける人にとっては当たり前のことなのか、私のググり方が悪かったのか。いろいろ調べた中では、この点にしっかり触れてくれていなかったように思います。

自分で何かを実装する時は、まさに順を追ってToDoリストを頭の中に作っていたのに、テストを書こうとした時にはそれをしていなかったのが、今となっては自分でも不思議です。

また個人的には4番目と5番目の、「検証用のテスト」と「実行部分のテスト」の違いがいまいちピンとこなかったので、本書からの引用も載せておきます。これは言葉で説明するよりも実際にコードを書いた方が分かりやすいかもしれません。

テストとはそもそもなんでしょうか。ごくごく簡単にまとめると下記の通りです。つまり、テストメソッドには最低限、結果を取得する処理の「実行」と「検証」を記述する必要があります。
- 何らかの処理が「実行」されたとき、
- 結果が期待通りのものであるかを「検証」する

具体的に考えてみます。

  • あるAPIのエンドポイント(api/XXX)にGETメソッドでアクセスできる

というToDoに対してのテストを考えるとすると、

「api/XXXXにGETメソッドでアクセスする」(つまり実行された)とき、
「ステータスコード200のHTTPレスポンスが返ってくる」ことを検証する

のがテストの内容となります。「実行」と「検証」の2つのテストがありますね。

掲示板アプリを例に考える

例えば掲示板アプリを作るとして、投稿記事(Articleモデル)に対して下記のようなエンドポイントを設定するとします。

内容 エンドポイント メソッド
記事一覧 api/articles GET
記事投稿 api/articles POST
記事詳細 api/article/[article_id] GET
記事編集 api/article/[article_id] PUT
記事削除 api/article/[article_id] DELETE

上記のエンドポイントに対してのToDoリスト

  • api/articlesにGETメソッドでアクセスできる
  • api/articlesにPOSTメソッドでアクセスできる
  • api/article/[article_id]にGETメソッドでアクセスできる
  • api/article/[article_id]にPUTメソッドでアクセスできる
  • api/article/[article_id]にDELETEメソッドでアクセスできる

当たり前のことしか書いていませんが、これで良いんです。こんな感じでできるだけ小さい単位でどんどんテストを書いて作っていくのがTDDの特徴です。
当然、小さい実装を想定したテストは書きやすい!!テストに対する苦手克服にも良かったです。
ちなみに、このToDoリストは途中で変更があっても構わないようです。

作成するToDoリストは、最初から完璧なものである必要はありません。むしろ実装中に細分化したり、新たなToDoに気付きリストを追加したりすることは当然のことです。

掲示板アプリを例にした4〜7までの手順のサンプルコードは、最後にまとめて掲載しておきます。
興味のある方はTDDの流れを追ってみてください。

テストで使った機能の一部を紹介

ここからは、個人的に勉強になったなーと思うことなどを書き留めておきます。LaravelというよりはPHPの機能もありますが、まとめて書いています。

artisanコマンドでテストのファイルが作れる

みんな大好き!職人artisanです。コマンド一発でテストの雛形を作ってくれます。相変わらず職人強し。。。

$ php artisan make:test FooTest

このコマンド一発でtests/Feature/FooTest.phpというテスト用のファイルが作成されます!テストの実行は下記のコマンドで一気にやってくれます。

$ php vendor/bin/phpunit

アノテーションを使ってシンプルにテストのメソッド名が書ける

テストのサンプルコードを見ていて、私が疑問に思っていたことの一つは、なぜメソッドが急に日本語??ということでした。

    /**
     * @test
     */

こういうやつをメソッドの前にあらかじめ記述しておくと、その下のコードはテストメソッドと判断されて日本語で直接メソッドを書くことができるようになります。artisanコマンドでサクッとresource作った時とかに、よく見るやつですね。私はこれ邪魔だなーと思っていつもソッコー消してました

これをアノテーションと言います。@testと書くと、テスト用のアノテーションです。
個人的にはアノテーションと呼ぶことも知らず、これに意味があったことが驚きでしたが、知ると超便利です。テストのメソッド名もスッキリ書けていいです。

日本語を使う理由は、テストの結果を見る時に日本語の方が直感的に分かりやすい、とのことでした。また、社内の勉強会では「テスト用に英語でちゃんと名前を考えるリソースが無駄に感じる」という意見もありました。たしかに納得!!
一方でテストだけ日本語は気持ち悪い!という人もいたので、日本語を使うのはケースバイケースみたいです。

ちなみにアノテーションを使わない場合は、メソッド名の最初にtestをつける必要があります。

// アノテーションを使ったテストの記述
/**
 * @test
 */
public function api_articlesにGETメソッドでアクセスできる(){}

// アノテーションを使わない場合のメソッド名の例(testを頭につける)
public function test_api_articlesにGETメソッドでアクセスできる(){}

DBテストが簡単にできる

artisanコマンドでテスト用のファイルを作ると、下記のように自動的にRefreshDatabaseトレイトが利用できるようになっているので、use RefreshDatabase;と記述すればテストが実行される前にテスト用のDBのマイグレーションを自動で行ってくれます。かしこい。

さらに、あらかじめシーダーを用意しておいて、setUpメソッド内で呼び出してあげれば、テストで使用するためのダミーデータも入れてくれます。setUpメソッドはテストが始まる前に実行されるので毎回同じデータが用意できますね。便利!
Laravelで書くのテストのいいところは、こうやってDBがからむテストを簡単に書けることが一番なのではないでしょうか。

FooTest.php
<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;

class FooTest extends TestCase
{
    use RefreshDatabase;  // これだけで自動でDBを初期化してくれる!

    // setUpメソッドがテストの前処理をしてくれる.ここではシーダーを実行している
    public function setUp(){
        parent::setUp();
        $this->artisan('db:seed', ['--class' => 'TestDataSeeder']);
    }

豊富なassertメソッド

assertは翻訳すると主張する、とか言い張る、とか宣言する、というような意味です。assertメソッドは値が一致するかどうかや、値の存在の有無などをチェックする時に使うテストに便利なメソッドです。
これは結構種類が多いので、最初に作ったToDoリストに合わせて使えそうなメソッドを選べばOK。

Laravel5.5 HTTPテスト

ちなみに11章で出てきたメソッドはこんな感じでした。

assertStatus()        // クライアントのレスポンスが指定したコードでなければエラー
assertThat()          // 後に続く条件を満たさなければエラー
assertSame()          // 2つの引数が同じ型・同じ値でない場合にエラー
assertJsonCount()     // 引数で渡されたJSONの中のデータの数が指定した数でなければエラー
assertDatabaseHas()   // 引数で渡された抽出条件に一致するデータが指定のテーブルになかったらエラー
assertExactJson()     // 引数で渡されたJSONと指定したデータと完全に一致しなかったらエラー

掲示板アプリ用のサンプルコード

最後に、TDDの簡単なサンプルとして、例に挙げた掲示板アプリのToDoに対して、実際どう書いていくかを少しだけ紹介します。

まずはテストファイルを作成

コマンド打つだけです

$ php artisan make:test ArticleTest
Test created successfully.

ファイルを編集して最初のテストを書く

前半で紹介したToDoリストの1番目、

  • api/articlesにGETメソッドでアクセスできる

のところだけやっていきます

tests/Feature/ArticleTest.php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ArticleTest extends TestCase
{
    /**
     * @test
     */
    public function api_articlesにGETメソッドでアクセスできる()
    {
        // ①最初に検証部分(ステータス200が返る)を記述
        $response->assertStatus(200);
    }

①のテストを書いて、テストを実行すると、下記のように失敗します。

$ php vendor/bin/phpunit
PHPUnit 6.5.14 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 1.51 seconds, Memory: 12.00MB

There was 1 error:

1) Tests\Feature\ArticleTest::api_customersにGETメソッドでアクセスできる
ErrorException: Undefined variable: response

/home/tdd_sample/tests/Feature/ArticleTest.php:25

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

②のテストを書き加えます。

tests/Feature/ArticleTest.php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ArticleTest extends TestCase
{
    /**
     * @test
     */
    public function api_articlesにGETメソッドでアクセスできる()
    {
        // ②次に実行部分(GETでアクセスする)のテストを記述
        $response = $this->get('api/articles');
        // ①最初に書いたテスト
        $response->assertStatus(200);
    }

この後、再度テストするとちょっとエラーの内容が変わりますが、これも失敗します。

$ php vendor/bin/phpunit
PHPUnit 6.5.14 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 1.41 seconds, Memory: 12.00MB

There was 1 failure:

1) Tests\Feature\ArticleTest::api_customersにGETメソッドでアクセスできる
Expected status code 200 but received 404.
Failed asserting that false is true.

/home/tdd_sample/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:78
/home/tdd_sample/tests/Feature/ArticleTest.php:25

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

テストが失敗することが確認できたら、最低限の実装です。
今回はroutes/api.phpにルーティングを書くだけです。

<?php

use Illuminate\Http\Request;

Route::get('customers', function(){});

この状態で再度テストをすると、テストが成功します。(このOKが、気持ちいい!!)

vagrant@homestead:~/code/tdd_sample$ php vendor/bin/phpunit
PHPUnit 6.5.14 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 1.61 seconds, Memory: 12.00MB

OK (1 test, 1 assertion)

こんな感じで実行と検証が成功すると、ひとつ目のToDoリストは完了です。
非常に単純化して書きましたが、これの繰り返しです。個人的には、このサイクルをサクサク回していくのが結構楽しかったです。

書籍の中では、もう少し複雑なアプリケーションを想定してテスト用のデータの用意もしながらTDDをざっくり体感できます。ルーティングも今は非常に雑な感じですが、こちらも最後にLaravelらしくリファクタリングされていきます。

ということで、TDDってめちゃくちゃ難しそうって思ってた私も本を見ながらサクサクできたので、意外と初テストにTDDアリかもっていうお話でした。

参考、引用

PHPフレームワークLaravel Webアプリケーション開発

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravelでbootstrap-vueを使う

1.ターミナルにて下記コマンド実行

npm install vue bootstrap-vue bootstrap

2.src/resources/js/app.jsに下記コード追加

src/resources/js/app.js
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue)

3.もっと気軽に使いたい場合はCDNで
①head内に下記を記述

<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css">

②bodyの最後に先下記を記述

<script src="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelリレーション初心者向け!外部キーがデフォルトでないパターン!!!

foreign_key が <テーブル名>_id でない場合のリレーション

日本語ドキュメントはこちらです!
https://readouble.com/laravel/6.x/ja/eloquent-relationships.html

がッッッ!!!!
わたしは日本語ドキュメント見ても分からない、、ドキュメントのドキュメントがほぢいぃぢいいいい!と思っておりますので同じような方に向けて。
または自分のために忘れぬようメモとして、書きます~~~~。

やりたいこと

image.png

まずは結論

Status.php
class Status extends Model
{
    public function history()
    {
        return $this->belongsTo(History::class, 'post_number');
    }
}
History.php
class History extends Model
{
    public function status()
    {
        return $this->hasOne(Status::class, 'post_number');
    }
}
StatusController.php
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Status;

class StatusController extends Controller
{
    public function index()
    {
        $statuses = Status::orderBy('created_at', 'asc')->paginate(10);

        return view('status.index', compact('statuses'));
    }
}
index.blade.php
@foreach ($statuses as $status)
<tr>
 <td>{{ optional($status->history)->post_id }}</td>
</tr>
@endforeach

どっちが belongsTo? とわかりづらい場合は、
まっぴーさんのツイートを参考に!!!!!!!!!!!!!!!!!!!!!!!

https://twitter.com/mpyw/status/1197390766642847744

「常に第2引数が Foreign Key で,第3引数が Local Key です。基本的に Local Key はいじらない限りは "id" となるもので,何かしらテーブル名入ってる方は Foreign Key という覚え方でOK」
image.png

とのことなので、 第2引数が id などの Local Key になっていたら belongsTohasOne が逆 です〜〜!
あと公式ドキュメントにも

return $this->hasOne('App\Phone', 'foreign_key', 'local_key');

と載ってるのですがここ大事みたいです!!!!!
わたしはデータをとりにいくテーブル側が hasOne になる!と思い込んでたので最初下記のようにしていて実際にデータも取得はできていたのですが、これだと、 Local Key が第2引数になってしまい正しい形ではないと!

Status.php ❌
class Status extends Model
{
    public function history()
    {
        return $this->hasOne(History::class, 'id', 'post_number');
    }
}

ドキュメントに沿うと下記が正解なのではと思ったけど何故かデータ取得できず…。

Status.php ❓
class Status extends Model
{
    public function history()
    {
        return $this->hasOne(History::class, 'post_number', 'id');
    }
}

そこで belongsTohasOne を反対にするとちゃんとデータも取得でき正しい書き方になったという結論です。(※まっぴーさんに教えてもらった)
ちなみにもし外部キーがデフォルトの、<テーブル名>_id であれば引き数は必要ないけれど、
histories テーブルに status_id があり、それを外部キーとしてデータを取得したい場合)
今回は違うので引数を割り当てます。

基本的には第3引数が id の形になると思われるんじゃけど、

return $this->belongsTo(History::class, 'post_number', 'id');

命名規則にのっていれば第3引数の id はデフォルトなので省略可能。

return $this->belongsTo(History::class, 'post_number');

無事データが取得できたか確認

\DB::enableQueryLog();

Status::find(1)->history;

dd(\DB::getQueryLog());

取得結果
image.png
bindings0 => 1 の意味は、「1つ目の 1 があてられる」
当てはめると下記になる↓

select * from `statuses` where `statuses`.`id` = 1 limit 1;
select * from `histories` where `histories`.`id` = 18 and `histories`.`deleted_at` is null limit 1;

上記を heidiSQL や A5SQL などにコピペして求めてるレコードが取れているか確認する

とれていればオオオオオオっっっkッッッッッッッッKえい!!!!!!!!!!!!!!!!1

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでリクエストとレスポンスを使用する

はじめに

プログラミング初心者の@lifeblogplusです。

プログラミングを学習して躓いた事をQiitaにまとめさせていただいています。

今回はリクエストとレスポンスについて説明をしたいと思います。

リクエストとレスポンスとは

リクエストとレスポンスとはクライアントとサーバーのやり取りを管理しているものです。

リクエストはクライアントからサーバーに送る情報を、レスポンスはサーバーからクライアントへ送り返す情報を管理しています。

リクエストとレスポンスはWebサイトへのアクセス処理としてとても重要な役割を果たしています。

リクエストとレスポンスの使用法(Laravel)

まずはアプリケーションの雛形を作りましょう。

今回はPracticeという名前で作成します。

コマンドプロント(ターミナル)で以下のコマンドを入力しましょう。

Laravel new Practice

これでアプリケーションの雛形は作成できたので、cdコマンドでPracticeフォルダの中に移動しましょう。

コントローラーの作成

次に使用するコントローラーを作成します。

今回はWorldという名前でコントローラを作成します。

以下のコマンドをコマンドプロント(ターミナル)で実行してください。

php artisan make:controller WorldController

これでWorldコントローラーが作成されました。

それでは作成したコントローラファイルを早速テキストエディタで開きましょう。

リクエストとレスポンスを表示する

今回はコントローラの中にviewコードも書いていくスタイルで行きたいと思います。

※本来はViewsの中にフォルダとファイルを作り、ルーティングでコントローラを呼び出し、Viewファイルを戻り値として表示するのが一般的です。

コントローラファイルを開くと以下のように表示されると思います。

WorldController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class WorldController extends Controller
{
    //
}

確認出来たらデフォルトで用意されているリクエストのuse文の下に以下のレスポンスのuse文を追加してください。

use Illuminate\Http\Response;

use文が用意できたら、コントローラに以下のメソッド分を追加してください。

WorldController.php
省略

class WorldController extends Controller
{
    public function index(Request $request, Response $response) {
        $html = <<<EOF
        <html>
            <head>
                <title>World/Index</title>
            </head>
            <body>
                <h1>Request</h1>
                <pre>{$request}</pre>
                <h1>Response</h1>
            </body>
        </html>
        EOF;

        $response->setContent($html);
        return $response;
    }
}

ルートの設定

次にルートの設定をします。

routesフォルダの中にあるweb.phpファイルを開いて以下のルーティングを追加しましょう。

web.php
Route::get('world', 'WorldController@index');

追加が出来たらコマンドプロント(ターミナル)で以下のコマンドでサーバーを立ち上げます。

php artisan serve

最後にトップページのURLの末尾に/worldを追加してアクセスすると表示されると思います。

おわりに

今回はリクエストとレスポンスについて書かせていただきました。

私はレスポンスのuse文を追加し忘れてエラーが起き、多くの時間を割いてしまいました。

もしエラーが起きた場合はレスポンスのuse文が追加されているか確認してみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel MJMLをレスポンシブなメールをサクッと送る

コミュニケーションの主役は、電子メールからチャットアプリへと変わりつつありますが、マスプロモーションや保存性のある通知手段として、今でも電子メールは一定の役割を果たしています。

現在主流となっているほとんどのメーラはHTMLメールをサポートしており、HTMLタグとスタイルシートを組み合わせることでレスポンシブデザインをはじめとする高度な表現が可能となっていますが、ブラウザのHTMLとは違った独特の癖や制約があり、メール用のHTMLを直接コーディングすることは大きな苦痛を伴います。

今回紹介するLaravel MJMLというライブラリを利用することで、コンポーネント化されたタグを組み合わせるだけで、レスポンシブかつ洗練されたデザインのHTMLメールを簡単に送信することができます。

MJMLそのものの説明はこちら
https://mjml.io/

前提条件

PHP7.1以上
Laravel 5.6以上
npmがインストールされていること

Laravel MJMLライブラリのインポート

composer create-project --prefer-dist laravel/laravel nicemail "5.8.*"
cd nicemail
composer require asahasrabuddhe/laravel-mjml
php artisan vendor:publish
( Tag: laravel-mailを選択)
npm install --save mjml

テスト用コマンドの生成

php artisan make:command MailTestCommand

空のテンプレートを作成

touch resources/views/mail.blade.php

設定ファイルを作成

touch config/mjml.php

下記の内容をコピー
(この手順は公式ページには記載がありませんが、私の環境ではこれを実行しないと動作しませんでした)

<?php
/**
 * Configuration for Laravel MJML Package.
 */
return [
    'auto_detect_path' => true,
    /*
     * The path to the MJML binary
     */
    // 'path_to_binary' => '',
];

ここまで用意ができましたら、

https://mjml.io/try-it-live

にアクセスして、送りたいメールのレイアウトを作成します。
(特になければ、一旦デフォルトで表示されている内容そのままでもいいです)

作成したMJMLをコピーして、上で作成したmail.blade.phpに貼り付けます。

mail.blade.php
<mjml>
  <mj-body>
    <mj-section>
      <mj-column>

        <mj-image width="100px" src="/assets/img/logo-small.png"></mj-image>

        <mj-divider border-color="#F45E43"></mj-divider>

        <mj-text font-size="20px" color="#F45E43" font-family="helvetica">Hello World</mj-text>

      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

テストメールを作成

php artisan make:mail TestMail

上で作成したファイルに下記の内容をコピー

<?php

namespace App\Mail;

use Asahasrabuddhe\LaravelMJML\Mail\Mailable;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;

class TestMail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->mjml("mail");
    }
}

実行用コマンドを作成

php artisan make:command MailTestCommand

<?php

namespace App\Console\Commands;

use App\Mail\PurchaseReportMail;
use App\Mail\TestMail;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;

class MailTestCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'test:mail';

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

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

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        Mail::to("changeme@example.com")->send(new TestMail());
    }
}

("changeme@example.com"の部分は、ご自身のメールアドレスに変更してください)

上記設定後
php artisan test:mail
でコマンドを実行することで、"Test Mail"という件名のメールが送られるはずです。

変数埋め込み等Bladeの機能を利用することができるため、非常に強力かつ使いやすいライブラリだと思います。
HTMLメールを送る必要があるときは、ぜひ利用をご検討ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerを使用してLaravelプロジェクトを作成および実行する

Using Docker to Create & Run a Laravel Project

このチュートリアルで使用したコンピューター:

The computer used for this tutorial:

Ubuntu 19.04 clean install

Docker version 19.03.3, build a872fc2f86

docker-compose version 1.25.0, build 0a186604

一時的な php composer コンテナを使用してプロジェクトを作成します

Use temporary php composer container to create project

sudo docker run --rm -v $(pwd):/app composer create-project --prefer-dist laravel/laravel <mylaravelproject>

終了したら、プロジェクトフォルダーの所有権を変更します

Once finished, change the ownership of the project folder

sudo chown -R myuser:user <mylaravelproject>

プロジェクトフォルダー内に、 .cloud /という新しいフォルダーを作成します。このフォルダーには、2つのフォルダー docker /nginxが含まれます。

Inside the project folder, create a new folder called .cloud/, which in turn, will have 2 folders docker/ & nginx

mylaravelproject/
    .cloud/
        docker/
        nginx/

docker /フォルダー内で、php container Dockerfileを作成します

inside the docker/ folder, create the php container Dockerfile

この記事の時点では、PHP 7.4がリリースされていますが、私はそれを試していないため、すべてのプラグインが互換性を持っているかどうかわかりません。したがって、このチュートリアルはPHP 7.3で行います。

As of the time of this article, PHP 7.4 has been released, but I haven´t experimented with it, and don´t know if all plugins have been made compatible, hence, we will be doing this tutorial with PHP 7.3

Dockerfile
FROM php:7.3-fpm

# Installing dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    default-mysql-client \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    libzip-dev \
    locales \
    zip \
    jpegoptim optipng pngquant gifsicle

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Installing extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl bcmath opcache
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

# Changing Workdir
WORKDIR /application

nginxフォルダー内に、設定ファイルを作成します

inside the nginx folder, we will create a config file

ngingx.conf
server {
  listen 80;
  index index.php index.html index.htm;
  root /application/public; # default Laravel's entry point for all requests

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  location / {
    # try to serve file directly, fallback to index.php
    try_files $uri /index.php?$args;
  }

  location ~ \.php$ {
    fastcgi_index index.php;
    fastcgi_pass api-server:9000; # address of a fastCGI server, must be the same name used for the container in the docker-compose.yml
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    include fastcgi_params;
  }
}

プロジェクトのルートフォルダに戻って、 docker-compose.ymlファイルを作成できます

Going back to the project root folder, we can now create the docker-compose.ymlfile

docker-compose.yml
version: '3'

services:
  app-server: #name of the container, must be the same used on the nginx.conf
    build: .cloud/docker
    image: larave-app #name of the image
    depends_on:
      - mysql
      - mysql-test
      - redis
    volumes:
      - ./:/application:cached

  horizon:
    build: .cloud/docker
    image: laravel-app #name of the image
    command: php artisan horizon
    depends_on:
      - mysql
    volumes:
      - ./:/application:cached

  mysql:
    image: mysql:5.7
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=laraveldb
    volumes:
      - db-data:/var/lib/mysql:cached

  mysql-test:
    image: mysql:5.7
    ports:
      - "3307:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=testing

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - .cloud/nginx/nginx.conf:/etc/nginx/conf.d/default.conf:cached
      - ./:/application:cached
    depends_on:
      - app-server #must be the same name of the laravel container used above

  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

volumes:
  db-data:

コンテナーを実行する前に、mysqlのプロジェクトの .envファイルを変更します

Before running the container, modify the projects .env file, for mysql

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laraveldb
DB_USERNAME=root
DB_PASSWORD=secret

私はdevにいるので、 storage /パーミッションを変更することを忘れないでください、私は通常777パーミッションで行きます

Remember to change the storage/ permissions, since I´m on dev, I usually go with 777 permissions

sudo chmod -R 777 storage/

最後に、docker-composeを実行します

Finally, run the docker-compose

sudo docker-compose up --build

http:// localhostlaravel ウェルカムページを見ることができます。

You can see the laravel welcome page, on http://localhost

移行または職人のコマンドを実行する場合は、*app-server * コンテナを参照することで実行できます

If you want to run migrations, or any artisan command, you can do so, by referencing the app-server container

sudo docker-compose exec app-server php artisan migrate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】herokuにデプロイする方法

この記事では、ローカル開発環境で作成したlaravelアプリをherokuというプラットフォームを使い、webアプリをデプロイする方法を紹介します。

この記事を作成する上以下の記事を参考にさせていただきました。
Laravelをherokuにデプロイする(データベースはPostgreSQL)

前提条件

  • 開発環境:vagrant&virtualbox
  • 開発OS:centos7
  • laravelをherokuでデプロイ

開発環境の構築がまだの方はコチラ
【開発環境】仮想サーバーの構築〜laravel導入まで(vagrant/centos7/apache/PHP/composer/laravel)

基本的な流れ

  • herokuの設定
  • laravelの設定
  • デプロイ

herokuの設定

1.herokuのアカウント作成

herokuのアカウントの作成はコチラ

2.heroku CLIのインストール

heroku CLIはコマンドラインでherokuの操作をするために必要なソフトです。

vagrantに以下のディレクトリを作成します。
$ sudo mkdir -p /usr/local/lib /usr/local/bin

heroku-linux-amd64をダウンロードします。
$ cd /vagrant
$ sudo wget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz

解凍して展開します。
$ sudo tar -xvzf heroku-linux-amd64.tar.gz -C /usr/local/lib

シンボリックリンクを貼ります。
$ sudo ln -s /usr/local/lib/heroku/bin/heroku /usr/local/bin/heroku

バージョンを確認します。表示されていればインストール完了です。
$ heroku --version
heroku-cli/6.99.0-ec9edad (linux-x64) node-v9.11.1

3.herokuにログイン

$ heroku login

heroku: Press any key to open up the browser to login or q to exit: 
(q以外のボタンを押す)

Opening browser to https://cli-auth.heroku.com/auth/browser/efa5711d-4987-4f4f-9c23-a269fec44724
 ›   Warning: Cannot open browser.
heroku: Waiting for login... ⢿

(ブラウザで指定のアドレスを開き、ログインする)

Logging in... done
Logged in as (herokuに登録しているメールアドレス)

4.herokuアプリの作成とPHPを使用可能にする

$ heroku create laravel-fileup-jum --buildpack heroku/php
Creating ⬢ laravel-fileup-jum... done
Setting buildpack to heroku/php... done
https://laravel-fileup-jum.herokuapp.com/ | https://git.heroku.com/laravel-fileup-jum.git

laravel-fileup-jumはheroku上で作成するアプリ名、heroku上で誰かと重複していると作れません

Laravel側での作業

1.Procfileを作る

ProcfileはHerokuアプリの起動時に実行するプロセスを定義するためのファイルです。
今回は、サーバーにApacheを使うこと、public/をドキュメントルートとすることを宣言するために使います。
laravelアプリのフォルダ直下にProcfileを作ります。

$ vi Procfile
Procfile
web: vendor/bin/heroku-php-apache2 public/

2.PHPの国際化用拡張モジュール(intl)を使う

$ composer require ext-intl:*

./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.
Writing lock file
Generating optimized autoload files
Carbon 1 is deprecated, see how to migrate to Carbon 2.
https://carbon.nesbot.com/docs/#api-carbon-2
    You can run './vendor/bin/upgrade-carbon' to get help in updating carbon and other frameworks and libraries that depend on it.
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Package manifest generated successfully.

3.herokuのアドオンにPostgreSQLを作成する

[vagrant@localhost fileup_heroku]$ heroku addons:create heroku-postgresql:hobby-dev
Creating heroku-postgresql:hobby-dev on ⬢ laravel-fileup-jum... free
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pg:copy
Created postgresql-cubed-52314 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation

4.データベースの情報を取得する。

$ heroku config:get DATABASE_URL

# メモしよう
postgres://<ユーザ名>:<パスワード>@<ホスト>:5432/<DB名>

5.herokuのconfigファイルに設定

$ heroku config:set DB_CONNECTION=pgsql
$ heroku config:set DB_HOST=<ホスト>
$ heroku config:set DB_DATABASE=<DB名>
$ heroku config:set DB_USERNAME=<ユーザ名>
$ heroku config:set DB_PASSWORD=<パスワード>
$ heroku config:set APP_KEY=$(php artisan key:generate --show)
$ heroku config:set APP_ENV=heroku
$ heroku config:set LANG=ja_JP.UTF-8
$ heroku config:set TZ=Asia/Tokyo

デプロイ

1.gitでherokuにデータをプッシュ

$ git init
Initialized empty Git repository in /home/vagrant/laravel/fileup_heroku/.git/
$ git add .
$ git commit -m "heroku first commit"
$ git remote add origin https://git.heroku.com/laravel-fileup-jum.git
$ git push origin master

2.データベースをマイグレーション

$ heroku run php artisan migrate

 Do you really wish to run this command? (yes/no) [no]:
 > y 

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table
マイグレーションをやり直したい場合

マイグレーションをやり直したい場合

$ heroku run php artisan migrate:refresh --seed

3.webアプリの起動

$ heroku open

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チーム開発日誌1-20191212-

プログラミングの勉強をはじめて9ヶ月目に入るのだけど
幸運なことにシステム開発の案件をいただくことができた(本当奇跡

チームでおこなうので記録に残していこうと思う
思いつくままツラツラと書いているので話が飛ぶこともある(許して

開発環境どうする?

JavaScriptでいける内容だからFirebase使ってつくろうか
と最初サクッと決めて進めていっていた
Vue.jsが良さそうだねーと

Firebaseでデータベース設計、バックアップ、セキュリティ、機能追加と考えたところ
これでいいのかなぁ...となって相談

うまく言葉にはできなかったのだけど
何を選ぶにしてもデータベース設計はよく考えなければなのは置いといて
1からつくるときにFirebaseをデータベースに使うのに何か抵抗がでた
(語彙力。。。)

チーム開発する&いずれ誰かが保守する+諸々考慮で
ある程度書き方にもルールがあって保守&機能追加しやすいものがよくないか
とも思って、LaravelでVue.jsを用いてMySQL使おうとなった

ただ、、開発コストと時間が不安点。。

つづく

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜテストが書けなかったのか?初心者がLaravelで初テストを書くまでのモヤモヤを考察

この記事の続きはG's ACADEMY Advent Calendarの14日目に公開します。
この記事用にそもそもの話を掘り下げようと思ったら、長くなったので前置きを分けました。

はじめに

私が初めてLaravelを触ったのはおよそ1年前です。

ちょうど去年の今頃は、フレームワークとかMVCとかちんぷんかんぷんです。とにかくサンプルコードをコピペしてきてなんとかいじって動かす、ということを延々とやっていました。
その後だんだんLaravelのお作法にも慣れてきて、全てを画面で動かしながらデバッグすることに辟易してきたのが今から半年前。
それからつい先日まで、テスト書きたいけど何からやればいいのやら…と途方にくれていた私が開眼した(と思う)ので、そのことについて書きます。

私のようなケースはマイノリティかも知れませんが、テストがなんとなく書けなかったこのモヤモヤの正体を言語化できればと思います。他にもテストでモヤっている人の何かのきっかけになれば。。。

結論

結論から言うとPHPフレームワークLaravel Webアプリケーション開発という本で「11章 TDDの実践」の中身を写経してみたら、結構スッと入ってきた、というだけです。
体感として本当に抵抗なく、テストってこう言うことか!と分かったので、じゃあ逆になぜ分からなかったのかが気になったので考察してみた次第です。

対象になりそうな方

  • テストを書くのに興味あるけど、何から始めればいいか分からない人
  • Laravelの基本的なお作法が分かっている人

なぜテストが書けなかったのか

テストコードを書く、という感覚が掴めなかった私にとって、課題は下記の3つでした。

  • そもそもテストのお作法(構文、使う関数)がよく分かっていなかった
  • すでにある実装に対して、テストを書くために何をすればいいか分からなかった
  • どういうテストが必要か分かっていなかった(テストの種類に対する理解がない)

でも今考えると、両方とも私の中では問題の根っこはひとつでした。すなわち テストの書き方は実装の内容によって変わる ということです。テストの構文や使う関数は調べればいくらでも出てきます。しかし、どこでどうやって使えばいいのかイメージができない。
すでに自分で実装したコードに対してテストを書こうとしても、ググって出てきたサンプルコードは当然使えませんし、一般的な書き方を調べてもピンと来ませんでした。

これは私の勉強スタイルとの相性にも問題があったかもしれません。私のよくやる勉強方法はこんな感じです。

  • 使いたい部分をとりあえずコピペしたり写経したりして、それっぽいサンプルを動かす
  • それをいろいろいじって自分のアプリケーション上で動くようにする
  • 上記の過程で何となく仕組みを理解していく
  • 自分で最初から書いてみる(繰り返す)

これだと、まず自分の実装に使えそうなテストコードを探すことになります。でも自分の実装に対してどういうテストを書けばいいか全くイメージできていないので、使えそうなテストを探し出せない。

そうかと言って簡単なサンプルアプリケーションをテストも含めて写経してみたところで、このサンプルのケース以外ではどうやって書くの?という状態でした。こちらに関しては覚えれば済む話で、テストで使う構文や関数に対する知識が圧倒的に足りなかったのだと思います。

上記のような状況の中さらに悪いのは、大抵の場合自分で書いた実装は テストが書きやすい実装になっていない ことです。これにより結局何をすればいいのか分からず混乱します。

こうして私は、必要と知りつつテストを書くことから、なんとなく逃げていました。。。

なぜLaravelでやるのか

  • Laravelの機能でDBのテストも含めたテストが簡単に書ける
  • Laravelのお作法に慣れ親しんでいれば直感的に使える(それの良し悪しはおいといて)

私にとっては慣れているフレームワーク上で書けたので戸惑いが少なかったように思います。
また、書籍の11章の中で、特に下記のようなところがテスト初心者の私にとって易しいポイントでした。

  • テストありきで設計していくのでテストを書く過程が追える
  • 小さく小さくテストを書いていくのでテスト自体が単純で書きやすい
  • サクサクとテストが通って気持ちいい感覚が得られる
  • 何より、テストを書くための準備について触れてくれていた

気がつくと、テスト書くの楽しいかも〜!!となってました。

さらに9章でもう少し細かく実践できる

11章を終えた後9章に戻ると、Laravelでのテストの仕組みについて、より詳細な説明を読むことができます。
9章は11章と違ってハンズオンでテンポよく進む感じではなかったし、初心者には少し難しい部分があると感じました。脱初心者くらいの方に良い本なのかもしれません。

ということで、TDDを通してテストを書くことで「テスト書けない問題」の克服のための第一歩にしてみてはいかがでしょうか。次の記事ではもう少し具体的に書いていきます。

ちなみにTDDについては賛否両論だった

社内勉強会でこの本を使ったので、TDDに関してはいろんな意見が出ていました。

  • テストが動く安心感がある
  • エンジニアにとって精神衛生上良い!
  • 現場ではTDD難しいよね、、(既存サービスの保守改善とかだとなかなか実践できない)
  • 新規開発の時はテンポ良く開発できて楽しそう

などなど。
個人的にはテスト書いて実装して、を繰り返しながら開発していくTDDは、螺旋階段をどんどん登っていくようなイメージで楽しかったのでした。自分でミニアプリ作るときにTDDでやってみたいと思いました!

この記事の続きはG's ACADEMY Advent Calendarの14日目に公開します。

参考

PHPフレームワークLaravel Webアプリケーション開発

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む