Laravel で 住所登録

今回はLaravel5.5にてユーザーの住所登録機能を作成します！
処理をControllerのみでの実装もできるのですがそれではコードが増えすぎてエラーの発見が遅れたり負荷が重かったりなどする恐れがあるので今回はContoroller と UseCase と Repository の３つに区分して説明していきます。

```php:AddressController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Requests\AddressRequest;
use App\UseCases\AddressUseCase;

class AddressController extends Controller
{

	public function __construct(
		AddressUseCase $addressUseCase
	) {
		$this->addressUseCase = $addressUseCase;
	}
	//住所一覧画面
	public function index()
	{
		$addresses = $this->addressUseCase->getAll();
		$address_id = Auth::user()->address_id;
		return view('address.index', compact('addresses', 'address_id'));
	}
	// 住所作成画面
	public function create()
	{
		$prefs = config('pref');
		return view('address.create', compact('prefs'));
	}
	// 住所作成処理
	public function store(AddressRequest $request)
	{
		$this->addressUseCase->store($request);
		return $this->index();
	}
	// 住所の編集画面
	public function edit($id)
	{
		$address = $this->addressUseCase->getByID($id);
		if (!$address || $address->user_id != Auth::id()) {
			set_message('不正なアクセスです。', false);
			return $this->index();
		}
		$prefs = config('pref');
		return view('address.edit', compact('address', 'prefs'));
	}
	// 住所の編集処理
	public function update(AddressRequest $request, $id)
	{
		$this->addressUseCase->update($request, $id);
		return $this->index();
	}
	// 選択した住所を削除
	public function destroy(Request $request)
	{
		$this->addressUseCase->destroy($request);
		return $this->index();
	}
	// 選択した住所に登録
	public function save(Request $request)
	{
		$this->addressUseCase->save($request);
		return $this->index();
	}

}

```
こちらはメソッドを呼び出してリダイレクト処理やViewを返すだけになっています。
続いてUseCase
これはlaravel/appの下にUseCasesディレクトリを作り、viでAddressUseCase.phpを起動

```
$ mkdir UseCases
$ vi AddressUseCase.php
```
UseCaseファイルでは条件分岐や代入処理を任せます。

```php:AddressUseCase.php
<?php

namespace App\UseCases;

use App\Address;
use App\Repositories\AddressRepository;
use App\Repositories\UserRepository;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class AddressUsecase
{

	public function __construct(
		Address $address,
		AddressRepository $addressRepository,
		UserRepository $userRepository
	) {
		$this->address = $address;
		$this->addressRepository = $addressRepository;
		$this->userRepository = $userRepository;
	}

	public function getAll()
	{
		return $this->addressRepository->getAll();
	}

	public function getByID($id)
	{
		return $this->addressRepository->getByID($id);
	}

	public function store($request)
	{
		$this->address->user_id = Auth::id();
		$this->address->name = $request->name;
		$this->address->first_code = $request->first_code;
		$this->address->last_code = $request->last_code;
		$this->address->state = $request->state;
		$this->address->city = $request->city;
		$this->address->street = $request->street;
		$this->address->tel = $request->tel;
        // 複数のテーブルを更新する際はトランザクションを使用
		DB::beginTransaction();
		try {
			$address_id = $this->addressRepository->save($this->address);
			$user = Auth::user();
			if ($user->address_id == null) {
				$user->address_id = $address_id;
				$this->userRepository->save($user);
			}
			DB::commit();
		} catch (Exception $exception) {
			DB::rollBack();
			throw $exception;
		}
		set_message('住所が追加されました。');
	}

	public function update($request, $id)
	{
		$address = $this->addressRepository->getByID($id);
		if (!$address) {
			set_message('住所が存在しません。', false);
		} elseif ($address->user_id != Auth::id()) {
			set_message('この住所は編集出来ません。', false);
		} else {
			$address->name = $request->name;
			$address->first_code = $request->first_code;
			$address->last_code = $request->last_code;
			$address->state = $request->state;
			$address->city = $request->city;
			$address->street = $request->street;
			$address->tel = $request->tel;
			$this->addressRepository->save($address);
			set_message('住所情報が変更されました。');
		}
	}

	public function destroy($request)
	{
		$address_id = $request->input('address_id');
		$address = $this->addressRepository->getByID($address_id);
		$user = $this->userRepository->getByID(Auth::id());
		if (!$address) {
			set_message('該当する項目が見つかりませんでした。', false);
		} elseif ($address->user_id != Auth::id()) {
			set_message('この住所は削除出来ません。', false);
		} else {
			DB::beginTransaction();
			try {
				$this->addressRepository->destory($address);
				if ($user->address_id == $address_id) {
					$user->address_id = null;
					$this->userRepository->save($user);
				}
				DB::commit();
			} catch (Exception $exception) {
				DB::rollBack();
				throw $exception;
			}
			set_message('住所が削除されました。');
		}
	}

	public function save($request)
	{
		$id = $request->input('address_id');
		$address = Address::find($id);
		if (!$address) {
			set_message('お届け先が存在しません。', false);
		} elseif ($address->user_id != Auth::id()) {
			set_message('この住所は登録出来ません。', false);
		} else {
			$user = Auth::user();
			$user->address_id = $id;
			$this->userRepository->save($user);
			set_message('お届け先住所が変更されました。');
		}
	}

}

```
データベース接続処理はRepositoriesディレクトリのAddressRepositoryとUserRepositoryに値を渡します。
デフォルトでRepositoriesディレクトリはlaravel/appの下に存在するはず（なければUseCases同様に作成）なのでそこでファイルを作成し、モデルを呼び出す。

```php:AddressRepository.php
<?php

namespace App\Repositories;

use App\Address;
use Illuminate\Support\Facades\Auth;

class AddressRepository
{

	public function __construct(
		Address $address
	) {
		$this->address = $address;
	}

	public function getAll()
	{
		return $this->address->where('user_id', Auth::id())->get();
	}

	public function getByID($id)
	{
		return $this->address->find($id);
	}

	public function save($address)
	{
		$address->save();
        // 作成した際にUserのaddress_idがnullの時にこのidを保存したいため、idを返す
		return $address->id;
	}

	public function destory($address)
	{
		return $address->delete();
	}

}

```
```php:UserRepository.php
<?php

namespace App\Repositories;

use App\User;

class UserRepository
{

	public function __construct(
		User $user
	) {
		$this->user = $user;
	}

	public function getByID($id)
	{
		return $this->user->find($id);
	}

	public function save($user)
	{
		return $user->save();
	}

}

```

※尚、今回のControllerとUseCaseでのsave関数は登録住所保存、Repositoryのsave関数は追加や更新によるもの

これでそれぞれの役割を分担出来ました。こうすることによってエラーの原因が発見しやすかったり、関数の使い回しが出来て便利になります。
index、create、editのviewはそれぞれ以下のようにしてます。

```php:index.blade.php
@extends('layouts.app')
@section('content')
<h2>お届け先一覧</h2>
@if (0 < $addresses->count())
<table border="1">
<tr style="background-color:yellow">
<th>◉</th>
<th>お届け先</th>
<th>名前</th>
<th>郵便番号</th>
<th>住所</th>
<th>電話番号</th>
<th>編集</th>
<th>削除</th>
</tr>
@foreach ($addresses as $address)
<tr>
<td>
@if ($address->id == $address_id)
<input type="radio" checked>
@endif
</td>
<td align="center">
<form method="post" action="{{ route('address.save') }}">
{{ csrf_field() }}
<input type="hidden" name="address_id" value="{{ $address->id }}">
<button type="submit">選択</button>
</form>
</td>
<td>{{ $address->name }}</td>
<td>{{ $address->first_code }}-{{ $address->last_code }}</td>
<td>{{ $address->state . $address->city . $address->street }}</td>
<td>{{ $address->tel }}</td>
<td><button><a href="{{ route('address.edit', $address->id) }}">編集</a></button></td>
<td>
<form method="post" action="{{ route('address.destroy') }}">
{{ csrf_field() }}
<input type="hidden" name="address_id" value="{{ $address->id }}">
<button type="submit" class="btn btn-danger btn-sm btn-dell">削除</button>
</form>
</td>
</tr>
@endforeach
</table>
@else
<p>お届け先は登録されていません</p>
@endif
<br>
<button><a href="{{ route('address.create') }}">お届け先追加</a></button><br>
@endsection
```

```php:create.blade.php
@extends('layouts.app')
@section('content')
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<body>
<form method="post" action="{{ route('address.store') }}">
{{ csrf_field() }}
<p>名前</p>
<input type="text" name="name" value="{{ old('name') }}">
<p>郵便番号</p>
<input type="text" name="first_code" value="{{ old('first_code') }}">-
<input type="text" name="last_code" value="{{ old('last_code') }}">
<p>都道府県</p>
<select name="state">
<option value="{{ old('state', '') }}" selected="{{ old('state', '') }}">{{ old('state', '都道府県') }}</option>
@foreach($prefs as $pref)
<option value="{{ $pref }}">{{ $pref }}</option>
@endforeach
</select>
<p>市町村</p>
<input type="text" name="city" value="{{ old('city') }}">
<p>以下住所</p>
<input type="text" name="street" value="{{ old('street') }}">
<p>電話番号</p>
<input type="text" name="tel" value="{{ old('tel') }}"><br>
<input type="submit" value="追加">
</form>
</body>
@endsection
```

```php:edit.blade.php
@extends('layouts.app')
@section('content')
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<body>
<form method="post" action="{{ route('address.update', $address->id) }}">
{{ csrf_field() }}
<p>名前</p>
<input type="text" name="name" value="{{ old('name', $address->name) }}">
<p>郵便番号</p>
<input type="text" name="first_code" value="{{ old('first_code', $address->first_code) }}">-
<input type="text" name="last_code" value="{{ old('last_code', $address->last_code) }}">
<p>都道府県</p>
<select name="state">
<option value="{{ old('state', $address->state) }}" selected="{{ old('state', $address->state) }}">{{ old('state', $address->state) }}</option>
@foreach($prefs as $pref)
<option value="{{ $pref }}">{{ $pref }}</option>
@endforeach
</select>
<p>市町村</p>
<input type="text" name="city" value="{{ old('city', $address->city) }}">
<p>以下住所</p>
<input type="text" name="street" value="{{ old('street', $address->street) }}">
<p>電話番号</p>
<input type="text" name="tel" value="{{ old('tel', $address->tel) }}"><br>
<input type="submit" value="編集">
</form>
</body>
@endsection
```

第一引数にold、第二引数にデフォルトを入れておくことでバリデーションにかかってもoldを保持してくれます。
よりユーザー目線でサービスを作れるようにしていきたいものです笑

ありがとうございました！！