安裝

composer require prettus/l5-repository

設定 ServiceProvider

如果是 >= laravel5.5 ,ServiceProvider 會自動設定好

其他版本

要在 config/app.php 增加

'providers' => [
    ...
    Prettus\Repository\Providers\RepositoryServiceProvider::class,
],

發布設定

設定完成多出一個設定檔: /config/repository.php

php artisan vendor:publish --provider "Prettus\Repository\Providers\RepositoryServiceProvider"

檢查

可以透過查詢 artisan 了解目前已經新增了哪些功能,用法會陸續說明

php artisan

簡介

  • entity

即 Model, 為了避免與原有Laravel架構混淆,特別用 Entitys 來做區隔,在 Entity 只負責接資料表

  • repository

只針對單一 Entity,處理一個資料表的行為

Model --->  Repository --...-> Controller
  • Criteria

如果有些 Repository 查詢條件經常使用到,可以將查詢的方法放在 Criteria,重複使用

他使用的邏輯,偏向於,在原有Repository 再疊加 Criteria 規則

在 l5-repository 架構並沒有分出 Service 這層來處理商業邏輯,

因此,關於多個 Repository 組成的商業邏輯,也可放在 Criteria 裡面處理即可

MOdel ---> Repository |
MOdel ---> Repository | ---> Criteria --..-> Controller
MOdel ---> Repository |
  • Presenter 我們常常需要將資料庫紀錄的內容進行轉換,例如,性別會用 1, 0 的方式紀錄,在輸出給使用者之前,會轉換成 男, 女 針對 Controller 從 Repository或 Service 取得結果,返回給 View 之前,有些資料需要經過格式轉換,就會在 Presenter 處理

在 l5-repository 有兩種方式可以實作 Presenter

第一種,可以直接建立 Transformrer 並且設定他來操作 Presenter class 第二種,可以直接在 model 實作 Transform 的 interface,並且 use 預設的 Presenter ModelFractarPresenter,也能達到同樣的效果

有時,在同一個 Repository or Service ,我們只希望丟出少數的資料 例如,使用者只需要返回name 與 email ,這時就能透過 transform 處理

(使用 Presenter 必須使用 fractal 擴充,後面會提到這個)

Model --->  Repository --...-> Controller -> (Presenter) -> View

MOdel ---> Repository |
MOdel ---> Repository | ---> Service --..-> Controller -> (Presenter) -> View
MOdel ---> Repository |
  • transform
Model --->  Repository --...-> Controller -> (transform) -> Response

MOdel ---> Repository |
MOdel ---> Repository | ---> Service --..-> Controller -> (transform) -> Response
MOdel ---> Repository |
  • Validator 驗證皆放置在這裡面處理,預設會自動生成新增及更新的驗證

建立 Model

接下來會以 members 來進行範例說明,

在 l5-repository 為了避免混淆,會將 Model 以 Entitys 方式管理

php artisan make:entity 名稱

//例如

php artisan make:entity members

在建立過程中,會自動詢問是否要同步建立 Presenter, Transformer, Validator, Controller

php artisan make:entity Members

 Would you like to create a Presenter? [y|N] (yes/no) [no]:
 > yes

App\Transformers\MembersTransformerPresenter created successfully.

 Would you like to create a Transformer? [y|N] (yes/no) [no]:
 > yes

Transformer created successfully.

 Would you like to create a Validator? [y|N] (yes/no) [no]:
 > yes

Validator created successfully.

 Would you like to create a Controller? [y|N] (yes/no) [no]:
 > yes

透過自動新增的好處是,會自動在 app/Providers/RepositorySerivceProvider@registor 新增 binding

這裏 binging 的用意是,在某些地方會引用 repository,出問題時會容易混淆,因此這裡會透過一個 Interface 隔開

$this->app->bind(\App\Repositories\MembersRepository::class, \App\Repositories\MembersRepositoryEloquent::class);

手動註冊 RepositoryServiceProvider

透過 make entity 的流程建立好任務後,會自動產生新的 service provider,這個 service provider 會需要綁定 Eloquent Repository 相關的 Repository Interface

要載入這項綁定,需要在 AppServiceProvider 的 register method 註冊:

app/providers/AppServiceProvider.php

<?php
...
    public function register()
    {
    	  //新增註冊
        $this->app->register(RepositoryServiceProvider::class);
    }

設定 Entities (Models)

無論是自行建立的 Model或者是 Entity,請記得定義 fillable 欄位,設定哪些欄位可以被變更

例如: app/Entities/Members.php

protected $fillable = ['title','email'];

還可以設定 softdelete 或者需要隱藏的欄位:

<?php

namespace App\Entities;

use Illuminate\Database\Eloquent\Model;
use Prettus\Repository\Contracts\Transformable;
use Prettus\Repository\Traits\TransformableTrait;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Class Members.
 *
 * @package namespace App\Entities;
 */
class Members extends Model implements Transformable
{
    use TransformableTrait;
    use SoftDeletes;

    /**
     * SoftDeletes as datetime
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name','email'];
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = ['deleted_at', 'updated_at', 'created_at'];

}

建立好基本 migration

這裏, 設定 members schema 內容

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateMembersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    // protected $dates = ['deleted_at'];
    public function up()
    {
        Schema::create('members', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->softDeletes();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('members');
    }
}

執行 migrate

php artisan migrate

Controller

透過 make:entity 可以自行決定要產生哪些功能,其中包括了 Controller

在預設生成的 Controller 裡面包含了 index, store, show, edit , update, destroy

  • index 返回所有資料
  • store 儲存一筆資料
  • show 顯示指定id資料
  • update 更新資料
  • destroy 刪除指定id資料
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use Prettus\Validator\Contracts\ValidatorInterface;
use Prettus\Validator\Exceptions\ValidatorException;
use App\Http\Requests\MembersCreateRequest;
use App\Http\Requests\MembersUpdateRequest;
use App\Repositories\MembersRepository;
use App\Validators\MembersValidator;

/**
 * Class MembersController.
 *
 * @package namespace App\Http\Controllers;
 */
class MembersController extends Controller
{
    /**
     * @var MembersRepository
     */
    protected $repository;

    /**
     * @var MembersValidator
     */
    protected $validator;

    /**
     * MembersController constructor.
     *
     * @param MembersRepository $repository
     * @param MembersValidator $validator
     */
    public function __construct(MembersRepository $repository, MembersValidator $validator)
    {
        $this->repository = $repository;
        $this->validator  = $validator;
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $this->repository->pushCriteria(app('Prettus\Repository\Criteria\RequestCriteria'));
        $members = $this->repository->all();

        $myTest = $this->repository->myCustomRepo();

        if (request()->wantsJson()) {
            return response()->json([
                'data' => $members,
                'test' => $myTest
            ]);
        }

        return view('members.index', compact('members'));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  MembersCreateRequest $request
     *
     * @return \Illuminate\Http\Response
     *
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function store(MembersCreateRequest $request)
    {
        try {
            $this->validator->with($request->all())->passesOrFail(ValidatorInterface::RULE_CREATE);

            $member = $this->repository->create($request->all());

            $response = [
                'message' => 'Members created.',
                'data'    => $member->toArray(),
            ];

            return response()->json($response);
        } catch (ValidatorException $e) {
            return response()->json([
                'error'   => true,
                'message' => $e->getMessageBag()
            ]);
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $member = $this->repository->find($id);

        if (request()->wantsJson()) {
            return response()->json([
                'data' => $member,
            ]);
        }

        return view('members.show', compact('member'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $member = $this->repository->find($id);

        return view('members.edit', compact('member'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  MembersUpdateRequest $request
     * @param  string            $id
     *
     * @return Response
     *
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function update(MembersUpdateRequest $request, $id)
    {
        try {
            $this->validator->with($request->all())->passesOrFail(ValidatorInterface::RULE_UPDATE);

            $member = $this->repository->update($request->all(), $id);

            $response = [
                'message' => 'Members updated.',
                'data'    => $member->toArray(),
            ];

            return response()->json($response);
        } catch (ValidatorException $e) {
            return response()->json([
                'error'   => true,
                'message' => $e->getMessageBag()
            ]);
        }
    }


    /**
     * Remove the specified resource from storage.
     *
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $deleted = $this->repository->delete($id);
        return response()->json([
            'message' => 'Members deleted.',
            'deleted' => $deleted,
        ]);
    }
}

權限檢查

store 與 update 在使用時,預設要經過 auth() 流程 可以在 app/Http/Requests/ 看到這兩個對應的 Request 處理,也可以寫入 Request 的檢查規則 MembersCreateRequest.php MembersUpdateeRequest.php

如果在 store, update 不想使用 auth,只需要在這兩隻檔案中 authorize() return true 即可

Route

可以在 api.php 設定對應的 route

Verb URI Action Route Name
GET /members index members.index
GET /members/create create members.create
POST /members store members.store
GET /members/{member} show members.show
GET /members/{member}/edit edit members.edit
PUT/PATCH /members/{member} update members.update
DELETE /members/{member} destroy members.destroy
<?php  ...

Route::resource('members', 'MembersController');

Criteria

建立 Criteria

php artisan make:criteria Mytest

會自動在 app/Criteria/ 建立 MytestCriterias.php

可以直接在 apply 裡面新增你想要的查詢規則

<?php ..
    public function apply($model, RepositoryInterface $repository)
    {
        return $model->where('id', 2);
    }

接著就能在 Controller 使用,原則上他是以累加方式來套上這些規則

        $this->repository->pushCriteria(new MytestCriteria());
        $members = $this->repository->all();

RequestCriteria

l5-repository 提供一個預設的 RequestCriteria

裡面提供了一整套查詢規則,相當方便

首先要先在 MembersRepositoryEloquent 新增可以被搜尋的欄位

並且可以在 boot 直接引用 RequestCriteria (這個在自動生成時,就會直接幫你加上)

<?php
class MembersRepositoryEloquent extends BaseRepository implements MembersRepository
{
    protected $fieldSearchable = [
    'name',
    'email'
    ];

		...
    /**
     * Boot up the repository, pushing criteria
     */
    public function boot()
    {
        $this->pushCriteria(app(RequestCriteria::class));
    }

也可以在 controller 裡面手動導入

<?php
...
        $this->repository->pushCriteria(app('Prettus\Repository\Criteria\RequestCriteria'));
        $members = $this->repository->all();

接著就可以參考下方規則,進行搜尋

http://prettus.local/users?search=John%20Doe

or

http://prettus.local/users?search=John&searchFields=name:like

or

http://prettus.local/users?search=john@gmail.com&searchFields=email:=

or

http://prettus.local/users?search=name:John Doe;email:john@gmail.com

or

http://prettus.local/users?search=name:John;email:john@gmail.com&searchFields=name:like;email:=

詳細內容可以參考官方文件

錯誤排解

Target [App\Repositories\MembersRepository] is not instantiable while building [App\Http\Controllers\MembersController].

需要在 AppServiceProvider 的 register method 註冊:

app/providers/AppServiceProvider.php

<?php
...
    public function register()
    {
    	//新增註冊
        $this->app->register(RepositoryServiceProvider::class);
    }

laravel request()->wantsJson() not work in postman

如果從 POSTMAN 以 Get 請求 members 卻仍會訪問 view ,得到以下錯誤:

View [xxx.index] not found.

只需要在 postman 的 Header 設定 Accept:application/json 即可

POSTMAN Resources controllers put update not working

請檢查以下項目:

postman使用 x-www-form-urlencoded 傳送參數

model 設定 fillable