PHP

Laravel ポケモンでリポジトリパターン

Laravel

https://github.com/yuukanehiro/Laravel-Study/tree/master/Laravel-Pokemon-RepositoryPettern

 

よくある処理の流れのルーティンをポケモンのモデルを利用して作ってみました。

ソースコードを読めても、実際にどれだけ自分が使えるかわからなかったので。書いて確認しました(。- .•)

 

関連

Laravel リレーション 中間テーブルのカラム取得 withPivot()

 

 

 

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′]

 

 

 

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)