安裝
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