Laravel 入門してみた

PHPフレームワーク、いままでまともに付き合ったことがないのですが、いまなら Laravel だろうということで、とりあえずルーティングまわり少しさわってみた話です。

## まずは環境構築
https://laravel.com/docs/8.x#getting-started-on-macos
> If you're developing on a Mac and Docker Desktop is already installed, you can use a simple terminal command to create a new Laravel project.

Macで開発していて、Dockerデスクトップがすでにインストールされている場合は、簡単なターミナルコマンドを使用して新しいLaravelプロジェクトを作成できます。（Google翻訳まま）

環境構築（？）終了！簡単！

↑のリンクにもありますが、一応コマンドだけ

```zsh
% cd ~/workspace
% curl -s https://laravel.build/example-app | bash
% cd example-app
% ./vendor/bin/sail up
```

example-app のところは何でもOK （指定した名称のディレクトリがカレントディレクトリに作成される）
本日時点だとバージョンは以下

* PHP : 8.0.0
* Laravel : 8.18.1

数分待つとアクセス可能に
http://localhost/
![example-app-localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/874425/fd630d90-2a49-248b-1fac-5f8251717275.png)
コンテナは何が立ち上がってる？
ほうほう、mysql と redis もいるんすね（今回使ってないけど。。）

```zsh
% docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Ports}}"
CONTAINER ID        IMAGE                    NAMES                        PORTS
f0b77d88072b        sail-8.0/app             example-app_laravel.test_1   0.0.0.0:80->80/tcp, 8000/tcp
740d3fc055e9        mysql:8.0                example-app_mysql_1          0.0.0.0:3306->3306/tcp, 33060/tcp
85ca0521fd6b        mailhog/mailhog:latest   example-app_mailhog_1        0.0.0.0:1025->1025/tcp, 0.0.0.0:8025->8025/tcp
734ce5fbbf76        redis:alpine             example-app_redis_1          0.0.0.0:6379->6379/tcp
```

作成されたプロジェクトディレクトリ（`example-app`）の中を見てみる

```zsh
% tree example-app -L 1
example-app
├── .editorconfig
├── .env <-- '動作環境に関する設定情報'
├── .env.example
├── .gitattributes
├── .gitignore
├── .styleci.yml
├── README.md
├── app <-- 'アプリの主なプログラム置き場 （Controllersなど）'
├── artisan <-- 'Laravelの職人'
├── bootstrap <-- 'アプリケーション実行時に最初に行われる処理群'
├── composer.json
├── composer.lock
├── config <-- '設定関連ファイル'
├── database <-- 'DB関連ファイル'
├── docker-compose.yml
├── package.json
├── phpunit.xml
├── public <-- '外部公開用ディレクトリ'
├── resources <-- 'リソース関連、css, js, view など）'
├── routes <-- 'ルーティング（リクエストマッピングなど）の設定'
├── server.php
├── storage <-- 'ファイル保存場所、ログなど'
├── tests
├── vendor <-- 'フレームワーク本体'
└── webpack.mix.js
```

## ルーティング（リクエストマッピング）
なにはともあれリクエストマッピング

web用のルーティングは routes/web.php に記述されている

```php:routes/web.php
Route::get('/', function () {
    return view('welcome');
});
```

↑の場合、GETで / にアクセスされた場合、view は resources/views/welcome.blade.php を返す
（Blade は Laravel のテンプレートエンジン）

Route でHTTPメソッドが各種対応されていて、リダイレクトなどなど他にもいろいろ使い方ある

```php
Route::get($path, $callback);
Route::post($path, $callback);
Route::put($path, $callback);
Route::patch($path, $callback);
Route::delete($path, $callback);
Route::options($path, $callback);
```

### エンドポイントを追加してみる

```php:routes/web.php
Route::get('/sample', function () {
    return view('sample');
});
```

アクセスしてみる
http://localhost/sample
![not-found-sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/874425/ea5eaba2-e2f9-33b5-053e-117c9bdcc5ce.png)
うむ、まだ view を作っていないので作る

```html:resources/views/sample.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>sample</title>
</head>
<body>
<main>
    <p>This is sample page.</p>
</main>
</body>
</html>
```

これでもう一度アクセスしてみる
![スクリーンショット 2020-12-10 22.24.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/874425/b1b29b5e-c199-d846-a9b8-efbb4b67587d.png)
飾り気なし男前なページだけど😂

### コントローラを使うように変更
routes/web.php が view 返すのもアレなのでコントローラから返すようにする
コントローラは先程出てきた職人（artisan）が作ってくれる

```zsh
% php artisan make:controller SampleController
```

これで SampleController が以下の状態で生成される

```php:app/Http/Controllers/SampleController.php
class SampleController extends Controller
{
    //
}
```

SampleController に function `sampleAction` を追加

```php:app/Http/Controllers/SampleController.php
class SampleController extends Controller
{
    public function sampleAction()
    {
        return view('sample');
    }
}
```

先ほど routes/web.php に定義した `'/sample'` のルートを以下に変更


```php:routes/web.php
Route::get('/sample', 'SampleController@sampleAction')->name('sample.sampleAction');
```

このままだと SampleController を見つけられないとエラーになるので、RouteServiceProvider.php を変更する （コメントアウトをはずす）

```php:app/Providers/RouteServiceProvider.php
// protected $namespace = 'App\\Http\\Controllers';
// ↓
protected $namespace = 'App\\Http\\Controllers';
```

これで先ほどと同じ動きになる （routes/web.php が役割に専念できる）

### パスパラメータの受け方
http://localhost/user/55 のようなユーザIDを受け取る場合を試してみる

先ほどと同じように、UserController を作成して show メソッドを作っておく

```zsh
% php artisan make:controller UserController
```

```php:app/Http/Controllers/UserController.php
class UserController extends Controller
{
    public function show(int $id)
    {
        return view('user.show', ['id' => $id]);
    }
}
```

ルート追加

```php:routes/web.php
Route::get('/user/{id}', 'UserController@show')->name('user.show');
```

view を作成

```html:resources/views/user/show.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>user.show</title>
</head>
<body>
<main>
    <p>The user ID specified is {{ $id }}.</p>
</main>
</body>
</html>
```

http://localhost/user/55 
![スクリーンショット 2020-12-10 22.24.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/874425/605aec7a-6080-aedc-4cf0-e265d7b041b5.png)
送ったパラメータを view まで引き渡せていることがわかる

### ルートの一覧
artisan でルート一覧を確認できる

```zsh
% php artisan route:list
+--------+----------+-----------+---------------------+----------------------------------------------------+------------+
| Domain | Method   | URI       | Name                | Action                                             | Middleware |
+--------+----------+-----------+---------------------+----------------------------------------------------+------------+
|        | GET|HEAD | /         | welcome             | Closure                                            | web        |
|        | GET|HEAD | api/user  |                     | Closure                                            | api        |
|        |          |           |                     |                                                    | auth:api   |
|        | GET|HEAD | sample    | sample.sampleAction | App\Http\Controllers\SampleController@sampleAction | web        |
|        | GET|HEAD | user/{id} | user.show           | App\Http\Controllers\UserController@show           | web        |
+--------+----------+-----------+---------------------+----------------------------------------------------+------------+
```

## RESTコントローラ
RESTコントローラの場合はコントローラ作成時に --resource オプションをつけると、CRUD関連のアクセスを扱えるように以下のクラスが生成される

```zsh
% php artisan make:controller SampleRestController --resource
```

```php:app/Http/Controllers/SampleRestController.php
class SampleRestController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}
```

この場合も Route の設定を行うが、以下のように `Route::resource` を使う

```php:routes/web.php
Route::resource('/sample-rest', 'SampleRestController');
```

この状態でルート一覧を確認すると sample-rest がHTTPメソッドごとにルートできるようになっているのがわかる

```zsh
% php artisan route:list --name=sample-rest
+--------+-----------+--------------------------------+---------------------+---------------------------------------------------+------------+
| Domain | Method    | URI                            | Name                | Action                                            | Middleware |
+--------+-----------+--------------------------------+---------------------+---------------------------------------------------+------------+
|        | GET|HEAD  | sample-rest                    | sample-rest.index   | App\Http\Controllers\SampleRestController@index   | web        |
|        | POST      | sample-rest                    | sample-rest.store   | App\Http\Controllers\SampleRestController@store   | web        |
|        | GET|HEAD  | sample-rest/create             | sample-rest.create  | App\Http\Controllers\SampleRestController@create  | web        |
|        | GET|HEAD  | sample-rest/{sample_rest}      | sample-rest.show    | App\Http\Controllers\SampleRestController@show    | web        |
|        | PUT|PATCH | sample-rest/{sample_rest}      | sample-rest.update  | App\Http\Controllers\SampleRestController@update  | web        |
|        | DELETE    | sample-rest/{sample_rest}      | sample-rest.destroy | App\Http\Controllers\SampleRestController@destroy | web        |
|        | GET|HEAD  | sample-rest/{sample_rest}/edit | sample-rest.edit    | App\Http\Controllers\SampleRestController@edit    | web        |
+--------+-----------+--------------------------------+---------------------+---------------------------------------------------+------------+

```

## 次回アクション
Spring（Java）の `@RequestMapping` 的なアノテーションも使えるっぽいので、そっちの方が便利だし今度はそっちを試してみようかなと思います。（途中で気になって調べたら出来そうだったので最初からやれば良かった。。）
それから、DBまわりは Laravel に Eloquent ORM というのが入ってるのでそのへんもどのくらい便利なのか、見ていこうかなーという感じです。