とりあえずXAMPPでLaravel入門してみた

Laravel入門しようと思い立ったので、とりあえずXAMPPでやってみました。

Laravel5.2のチュートリアルをなんとなく参考に、タスク管理を作って基本的なCRUDをやってみます。

> https://readouble.com/laravel/5.2/ja/quickstart.html

今回はログインとか認証は一切ありません。
使ってるLaravelは5.3です。



## Composerインストール

XAMPPのシェルでComposerをインストールします。

```
php -r "readfile('https://getcomposer.org/installer');" | php
```

## Laravelインストール
exampleプロジェクトを作成します。

```
php composer.phar create-project laravel/laravel example --prefer-dist
```

とりあえずLaravel
`http://localhost/example/public/`
![hellolaravel.png](https://qiita-image-store.s3.amazonaws.com/0/47389/72fa54e1-0cb5-bd43-9b04-0b9c08769e52.png)

Laravelのバージョンは5.3です。



## データベース

XAMPP使ってるのでMySQL(Maria DB)を使います。

事前にMySQLにユーザーとデータベースを作っておきます。

データベースの接続情報を.envに設定します。

```:.env
DB_DATABASE=example
DB_USERNAME=example
DB_PASSWORD=example
```

tasksテーブルを作成するためのマイグレーションを作ります。
XAMPPシェルでartisanを使います。

```
php artisan make:migration create_tasks_table --create=tasks
```

database/migrationsにマイグレーションファイルが作成されます。

マイグレーションファイルを修正してテーブルに作成するカラムを指定します。

```php:database/migrations/yyyy_mm_dd_hhmmss_create_tasks_table.php
public function up()
{
    Schema::create('tasks', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name')->nullable();
        $table->boolean('done')->default(false);
        $table->timestamps();
        $table->softDeletes();
    });
}
```

nameカラムとdoneカラムを追加しました。
あと、論理削除に対応するため`$table->softDeletes()`を追加しています。

マイグレーションを実行してテーブルを作成します。

```
php artisan migrate
```

## Modelの作成

artisanでTaskモデルを作ります。

```
php artisan make:model Task
```

app/Task.phpができるので、編集します。

```php:app/Task.php
namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Task extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'name',
        'done',
    ];
}
```

`use SoftDeletes`で、このモデルをdestoryしたときに論理削除されるようになります。
`$fillable`はこのモデルで使うリクエストパラメータを指定しておきます。



## Controllerの作成

artisanでTaskコントローラを作成します。

```
php artisan make:controller TasksController
```

app/Http/ContollersにTasksControllerができます。



## ルーティング

LaravelではResourcefulなルートに対応してるので、それでいきます。

routes/web.phpを編集します。

```php:routes/web.php
Route::resource('tasks', 'TasksController');
```

この1行で次の7つのアクションが定義されます。
参考に公式ドキュメントの表を転記しておきます。

|Verb|URI|Action|Route Name|
|---|---|---|---|
|GET|/tasks|index|tasks.index|
|GET|/tasks/create|create|tasks.create|
|POST|/tasks|store|tasks.store|
|GET|/tasks/{task}|show|tasks.show|
|GET|/tasks/{task}/edit|edit|tasks.edit|
|PUT/PATCH|/tasks/{task}|update|tasks.update|
|DELETE|/tasks/{task}|destroy|tasks.destroy|

> https://laravel.com/docs/5.3/controllers#resource-controllers

Railsと比較するとcreate→store, new→createとなっているのでRailsに慣れている人はご注意。



## Viewレイアウトの作成

Viewで共通して使うレイアウトを作ります。

```php:resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>@yield('title')</title>
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    <body>
        <div class="container">
            <nav class="navbar navbar-default">
                <a class="navbar-brand" href="{{ url('/tasks') }}">
                    タスク一覧
                </a>
                <a class="navbar-brand" href="{{ url('/tasks/create') }}">
                    新規作成
                </a>
            </nav>
        </div>

        @yield('content')

        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>
```



## Formヘルパーを使う

Laravel5からFormヘルパーは別プロジェクトになったようなので、インストールします。

> https://github.com/LaravelCollective/html

```
php ../composer.phar require laravelcollective/html
```

config/app.phpを編集してprovidersとaliasesに設定を追加します。

```php:config/app.php
'providers' => [
    Collective\Html\HtmlServiceProvider::class,
'aliases' => [
    'Form' => Collective\Html\FormFacade::class,
    'Html' => Collective\Html\HtmlFacade::class,
```



## 新規登録画面の作成

TasksControllerにcreateアクションとstoreアクションを作ります。

```php:app/Http/Controllers/TasksController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Task;
use App\Http\Requests;

class TasksController extends Controller
{
    public function create()
    {
        return view('tasks/create')->with('task', new Task());
    }

    public function store(Request $request)
    {
        $task = new Task();
        $task->fill($request->all());
        $task->save();
        return redirect()->route('tasks.index');
    }
}
```

storeではDBに保存して一覧にリダイレクトしているだけです。
Validationなどはしていません。
Modelの`$fillable`にフィールド名を定義しておくと、`$task->fill`でリクエストパラメータから値を取得してくれます。（Railsのstrong parametersみたいな感じ）



## Viewの作成

新規作成画面のViewを作ります。

```php:resources/views/tasks/create.blade.php
@extends('layouts.app')

@section('title', '新規作成')

@section('content')

    <div class="panel panel-default">
        <div class="panel-heading">
            新規作成
        </div>
        <div class="panel-body">
            {!! Form::model($task, ['route' => 'tasks.store', 'method' => 'post', 'class' => 'form-horizontal']) !!}
                <div class="form-group">
                    {!! Form::label('name', 'タスク名', ['class' => 'col-sm-3 control-label']) !!}
                    <div class="col-sm-6">
                        {!! Form::text('name', $task->name, ['id' => 'task-name', 'class' => 'form-control']) !!}
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-3 col-sm-6">
                        {!! Form::submit('タスク追加', ['class' => 'btn btn-default']) !!}
                    </div>
                </div>
            {!! Form::close() !!}
        </div>
        <div class="panel-footer">
            {{ link_to_route('tasks.index', '戻る') }}
        </div>
    </div>

@endsection
```

`http://localhost/example/public/tasks/create`
新規作成画面ができました。

![tasks_create.png](https://qiita-image-store.s3.amazonaws.com/0/47389/96846ce1-28e2-dfbf-4164-b99b6ff6c565.png)



## 一覧画面を作成

TasksControllerにindexアクションを作ります。

```php:app/Http/Controllers/TasksController.php

    public function index()
    {
        $tasks = Task::orderBy('updated_at', 'desc')->get();
        return view('tasks/index')->with('tasks', $tasks);
    }
```

Viewを作ります。

```php:resources/views/tasks/index.blade.php
@extends('layouts.app')

@section('title', 'タスク一覧')

@section('content')

    <div class="panel panel-default">
        <div class="panel-heading">
            タスク一覧
        </div>
        <div class="panel-body">
            <table class="table table-striped task-table">
                <thead>
                    <th>タスク名</th>
                    <th>完了</th>
                    <th>編集</th>
                    <th>削除</th>
                </thead>
                <tbody>
                    @foreach ($tasks as $task)
                        <tr>
                            <td class="table-text">
                                {{ link_to_route('tasks.show', $task->name, $task->id) }}
                            </td>
                            <td class="table-text">
                                {{ $task->done ? '完了' : '未' }}
                            </td>
                            <td class="table-text">
                                {{ link_to_route('tasks.edit', '編集', $task->id, ['class' => 'btn btn-sm btn-default']) }}
                            </td>
                            <td class="table-text">
                                {{ Form::open(['route' => ['tasks.destroy', $task->id], 'method' => 'delete']) }}
                                    {{ Form::hidden('id', $task->id) }}
                                    {{ Form::submit('削除', ['class' => 'btn btn-sm btn-default']) }}
                                {{ Form::close() }}
                            </td>
                        </tr>
                    @endforeach
                </tbody>
            </table>
        </div>
    </div>

@endsection
```

`http://localhost/example/public/tasks`
新規登録すると一覧に表示されます。

![tasks_index.png](https://qiita-image-store.s3.amazonaws.com/0/47389/b86717ab-b442-c7cc-df6f-699070e2a801.png)



## 詳細画面の作成

コントローラにshowアクションを作ります。

```php:app/Http/Controllers/TasksController.php

    public function show($id)
    {
        $task = Task::find($id);
        return view('tasks/show')->with('task', $task);
    }
```

Viewを作ります。

```php:resources/views/tasks/show.blade.php
@extends('layouts.app')

@section('title', $task->name)

@section('content')

    <div class="panel panel-default">
        <div class="panel-heading">
            {{ $task->name }}
        </div>
        <div class="panel-body">
            <div>
                タスク名: {{ $task->name }}
            </div>
            <div>
                完了: {{ $task->done ? '完了' : '未' }}
            </div>
        </div>
        <div class="panel-footer">
            {{ link_to_route('tasks.index', '戻る') }}
        </div>
    </div>

@endsection
```

一覧のリンクから詳細が表示されます。



## 更新、削除の作成

コントローラにedit, update, destroyアクションを作ります。

```php:app/Http/Controllers/TasksController.php

    public function edit($id)
    {
        $task = Task::find($id);
        return view('tasks/edit')->with('task', $task);
    }

    public function update(Request $request, $id)
    {
        $task = Task::find($id);
        $task->fill($request->all());
        $task->save();
        return redirect()->route('tasks.index');
    }

    public function destroy($id)
    {
        $task = Task::find($id);
        $task->delete();
        return redirect()->route('tasks.index');
    }
```

ちなみに、前述のとおりModelで定義しているため、deleteは論理削除になります。

editのViewを作ります。

```php:resources/views/tasks/show.blade.php
@extends('layouts.app')

@section('title', "$task->nameの編集")

@section('content')

    <div class="panel panel-default">
        <div class="panel-heading">
            {{ $task->name }}の編集
        </div>
        <div class="panel-body">
            {!! Form::model($task, ['route' => ['tasks.update', $task->id], 'method' => 'patch', 'class' => 'form-horizontal']) !!}
                <div class="form-group">
                    {!! Form::label('name', 'タスク名', ['class' => 'col-sm-3 control-label']) !!}
                    <div class="col-sm-6">
                        {!! Form::text('name', $task->name, ['id' => 'task-name', 'class' => 'form-control']) !!}
                    </div>
                </div>
                <div class="form-group">
                    {!! Form::label('done', '完了', ['class' => 'col-sm-3 control-label']) !!}
                    <div class="col-sm-6">
                        {!! Form::select('done', [false => '未', true => '完了'], $task->done, ['id' => 'task-done', 'class' => 'form-control']) !!}
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-3 col-sm-6">
                        {!! Form::button('<i class="fa fa-save"></i> 保存', ['type' => 'submit', 'class' => 'btn btn-default']) !!}
                    </div>
                </div>
            {!! Form::close() !!}
        </div>
        <div class="panel-footer">
            {{ link_to_route('tasks.index', '戻る') }}
        </div>
    </div>

@endsection
```

Formの部分は工夫すればcreateと共通化できそうですね。

ひとまず今回はここまで。

ソースコードはGitHubにアップしてあります。
[HisakiEndo/learning_of_laravel](https://github.com/HisakiEndo/learning_of_laravel/tree/a9ce979bf4aea4c7de9765919b83584c05129846)