https://github.com/yuukanehiro/Laravel-Study/tree/master/Laravel-Pokemon-RepositoryPettern
よくある処理の流れのルーティンをポケモンのモデルを利用して作ってみました。
ソースコードを読めても、実際にどれだけ自分が使えるかわからなかったので。書いて確認しました(。- .•)
もくじ
関連
http://localhost/getMasters/{id}
http://localhost/getMasters/
http://localhost/fight/{id}
ルーティング
routes/web.php
Route::get('/getMasters', 'PokemonMasterController@getMasters'); Route::get('/getMasters/{id}', 'PokemonMasterController@getMaster'); Route::get('/fight/{id}', 'PokemonMasterController@fight');
モデル
- Eloqunetで標準的に取得できるような値を取得する
- 各フィールドの値
- リレーション定義
Models/Master.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; class Master extends Model { protected $table = 'masters'; protected $primaryKey = 'master_id'; protected $guarded = [ 'master_id' ]; public function pokemon() { return $this->belongsToMany('App\Models\Pokemon'); } public function master_pokemon() { return $this->belongsToMany('App\Models\Pokemon', 'master_pokemon', 'master_id', 'pokemon_id') ->withPivot('comment'); } public function getCntBattle(int $id = null): ?int { return $this->cnt_battle; } public function getCntWon(int $id = null): ?int { return $this->cnt_won; } public function getWinningPercentAttribute(): ?float { if ($this->getCntBattle() === 0) { return null; } return $this->getCntWon() / $this->getCntBattle(); } }
Models/Pokemon.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use phpDocumentor\Reflection\Types\Object_; class Pokemon extends Model { protected $table = 'pokemons'; protected $primaryKey = 'pokemon_id'; protected $guarded = [ 'pokemon_id' ]; public function master() { return $this->belongsToMany('App\Models\Master'); } public function master_pokemon() { return $this->belongsToMany('App\Models\Master', 'master_pokemon', 'pokemon_id', 'master_id') ->withPivot('comment'); } public static function getPokemonName(int $id) :Object { return Pokemon::select('name')->find($id); } }
マイグレーションファイル
テーブル定義ファイル
pokemonsテーブル
xxxx_create_pokemons_table.php
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePokemonsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('pokemons', function (Blueprint $table) { $table->bigIncrements('pokemon_id'); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('pokemons'); } }
mastersテーブル
xxxx_create_masters_table.php
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMasterPokemonTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('master_pokemon', function (Blueprint $table) { $table->bigIncrements('id'); $table->bigInteger('master_id')->unsigned(); $table->bigInteger('pokemon_id')->unsigned(); $table->string('comment'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('master_pokemon'); } }
中間テーブル
xxxx_create_master_pokemon_table.php
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMasterPokemonTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('master_pokemon', function (Blueprint $table) { $table->bigIncrements('id'); $table->bigInteger('master_id')->unsigned(); $table->bigInteger('pokemon_id')->unsigned(); $table->string('comment'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('master_pokemon'); } }
シーダファイル
テーブルデータのサンプルを流します。
MasterTableSeeder.php
<?php use Illuminate\Database\Seeder; class MastersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $master = new \App\Models\Master([ 'name' => 'さとし', 'cnt_battle' => 10, 'cnt_won' => 4, ]); $master->save(); $master = new \App\Models\Master([ 'name' => 'たけし', 'cnt_battle' => 100, 'cnt_won' => 20, ]); $master->save(); $master = new \App\Models\Master([ 'name' => 'かすみ' ]); $master->save(); $master = new \App\Models\Master([ 'name' => 'こじろう' ]); $master->save(); } }
PokemonTableSeeder.php
<?php use Illuminate\Database\Seeder; class PokemonsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $pokemon = new \App\Models\Pokemon([ 'name' => 'ピカチュウ' ]); $pokemon->save(); $pokemon = new \App\Models\Pokemon([ 'name' => 'イワーク' ]); $pokemon->save(); $pokemon = new \App\Models\Pokemon([ 'name' => 'スターミー' ]); $pokemon->save(); $pokemon = new \App\Models\Pokemon([ 'name' => 'ニャース' ]); $pokemon->save(); } }
Master_PokemonTableSeeder.php
<?php use Illuminate\Database\Seeder; class Master_PokemonTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('master_pokemon')->insert([ 'master_id' => 1, 'pokemon_id' => 1, 'comment' => "キミに決めた!", ]); DB::table('master_pokemon')->insert([ 'master_id' => 2, 'pokemon_id' => 2, 'comment' => "イワーク、ご苦労だった。" ]); DB::table('master_pokemon')->insert([ 'master_id' => 3, 'pokemon_id' => 3, 'comment' => "いけー!マイ ステディ!" ]); DB::table('master_pokemon')->insert([ 'master_id' => 4, 'pokemon_id' => 4, 'comment' => "ニャースでニャース! (せりふ) うるさい!" ]); DB::table('master_pokemon')->insert([ 'master_id' => 4, 'pokemon_id' => 1, 'comment' => "ピカチュウ!捕まえた〜!", ]); } }
AppServiceProvider.php
ワンポイント!
AppServiceProviderで『関連(new)』を行います。
App/Providers/AppServiceProvider.php
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { $this->app->bind( \App\Repositories\MasterRepository::class, function ($app) { return new \App\Repositories\MasterRepository(new \App\Models\Master); } ); $this->app->bind( \App\Service\PokemonMasterService::class, function ($app) { return new App\Service\MasterService( $app->make('\App\Repositories\MasterRepository') ); } ); } /** * Bootstrap any application services. * * @return void */ public function boot() { // } }
AppServiceProvider.phpで指定することで、
リポジトリ(app/Repositories/MasterRepository.php)とサービス(app/Http/Services/Service.php)のコンストラクタでnewしなくて良いようになっています。
例) app/Repositories/MasterRepository.php
class MasterRepository extends Repository { public function __construct(Model $model) { $this->model = $model; } ・・・
Modelクラスの指定となっている。
app/Http/Services/Service.php
class PokemonMasterService extends Service { private $MasterRepository; public function __construct( MasterRepository $MasterRepository ) { $this->MasterRepository = $MasterRepository; } ・・・
newしていない。
リポジトリ
- モデルの集合対
- モデルやEloquentを利用したDB処理を行う
app/Repositories/Repository.php
<?php namespace App\Http\Repositories; use Illuminate\Database\Eloquent\Model; abstract class Repository { protected $model; }
app/Repositories/MasterRepository.php
<?php namespace App\Repositories; use Illuminate\Database\Eloquent\Model; use App\Repositories\Repository; use Illuminate\Database\Eloquent\Collection; class MasterRepository extends Repository { public function __construct(Model $model) { $this->model = $model; } /** * バトルの勝利処理 * * @param int $id * @return void */ public function win(int $id): void { $master = $this->model->find($id); $master->increment('cnt_battle'); $master->increment('cnt_won'); } /** * バトルの負け処理 * * @param int $id * @return void */ public function lose(int $id): void { $master = $this->model->find($id); $master->increment('cnt_battle'); } /** * IDを指定してMasterユーザを表示 * * @param int $id * @return Collection */ public function getMasterById(int $id): Collection { $master = $this->model->where('master_id', $id)->get(); return $master; } /** * Masterユーザをリスト表示 * * @return Collection */ public function getAllMaster(): Collection { $master = $this->model->get(); return $master; } }
サービス
- リポジトリを利用してビジネスロジックを行う
- サービスと言えるような一連のまとまった処理を担当
app/Http/Services/Service.php
<?php namespace App\Services; abstract class Service { // }
app/Http/Services/PokemonMasterService.php
<?php namespace App\Http\Services; use App\Repositories\MasterRepository; use App\Models\Master; use App\Services\Service; class PokemonMasterService extends Service { private $MasterRepository; public function __construct( MasterRepository $MasterRepository ) { $this->MasterRepository = $MasterRepository; } public function battle(int $id): array { /** * サイコロバトル * 偶数:勝ち * 奇数:負け * * @param $id int * @return float */ $dice = (int) rand(1,6); $result = $dice % 2? 'even' : 'odd'; if ($result === 'even') { $this->MasterRepository->win($id); $battleResult['result'] = 'win'; } else { $this->MasterRepository->lose($id); $battleResult['result'] = 'lose'; } return $battleResult; } }
ビューモデル
- 仮想カラムを追加
- オブジェクトの付与
- JSONで返すデータの集合体
モデルだけだとテーブルの値そのままなので、ビューモデルを返すオブジェクトを定義して返却します。
app/Http/ViewModel.php
<?php namespace App\Http\ViewModels; use Illuminate\Support\Fluent; abstract class ViewModel extends Fluent { abstract public function render(); }
app/Http/MasterViewModel.php
<?php namespace App\Http\ViewModels; use App\Models\Master; use App\Http\Repositories\PokemonMasterRepository; class MasterViewModel extends ViewModel { public function render(): Master { return $this->master ->append('winning_percent') ->makeHidden(['master_id', 'created_at', 'updated_at' ]); } }
コントローラ
- リクエストの処理を担当
app/Http/Controller.php
<?php namespace App\Http\Controllers; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Routing\Controller as BaseController; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; }
app/Http/PokemonMasterController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Services\PokemonMasterService; use App\Repositories\MasterRepository; use App\Http\ViewModels\MasterViewModel; use Illuminate\Routing\ResponseFactory; class PokemonMasterController extends Controller { /** * Masterユーザをidを指定して取得 * * @param App\Repositories\MasterRepository $MasterRepository * @param Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function getMaster( MasterRepository $MasterRepository, Request $request ) { $userId = $request->id; $master = $MasterRepository->getMasterById($userId); $responseMaster = $master ->map(function ($item, $key){ return (new MasterViewModel(['master' => $item]))->render(); }); return response()->json($responseMaster); } /** * Masterユーザをリスト取得 * * @param App\Repositories\MasterRepository $MasterRepository * @return \Illuminate\Http\Response */ public function getMasters( MasterRepository $MasterRepository, Request $request ) { $master = $MasterRepository->getAllMaster(); $responseMaster = $master ->map(function ($item, $key){ return (new MasterViewModel(['master' => $item]))->render(); }); return response()->json($responseMaster); } /** * じゃんけんバトルを行って結果を返却 * * @param App\Http\Services\PokemonMasterService $pokemonMasterService * @return \Illuminate\Http\Response */ public function fight( PokemonMasterService $pokemonMasterService, Request $request ) { $userId = $request->id; $battleResult = $pokemonMasterService->battle($userId); return response()->json($battleResult); } }
モデルのフィールドをまとめる
public function getMasterSpecAttribute() { if ($this->master_spec_tairyoku === null) { return null; } return [ 'tairyoku' => $this->master_spec_tairyoku, 'kawaii' => $this->master_spec_kawaii, 'money' => $this->master_spec_money, ]; }
仮想カラム
『 masuter_spec』 のキーに配列として
- tairyoku
- kawaii
- money
が追加される
[amazon_link asins=’4798052582′ template=’ProductCarousel’ store=’izayoi55-22′ marketplace=’JP’ link_id=’f1a3a821-2747-47c4-90e9-8af2f8583fa3′]
[amazon_link asins=’4798059072′ template=’ProductCarousel’ store=’izayoi55-22′ marketplace=’JP’ link_id=’7af12ba9-cd06-4de0-88a7-a8c932c716c2′]
[amazon_link asins=’4802611846,B01FH3KVNU’ template=’ProductCarousel’ store=’izayoi55-22′ marketplace=’JP’ link_id=’56c7eaa0-a570-41fc-9fed-bf656b6f3043′]