建立專案
建立專案。
1
| laravel new laravel-abstract-repository-example
|
資料庫
使用 SQLite 資料庫,在 database
資料夾新增 database.sqlite
檔,修改 .env
檔如下:
1 2 3 4 5 6
| DB_CONNECTION=sqlite DB_HOST=127.0.0.1 DB_PORT=3306 # DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD=
|
資料夾結構
資料夾結構如下:
1 2 3 4 5 6 7
| |- app/ |- Repositories/ |- Contracts/ |- RepositoryInterface.php |- UserRepositoryInterface.php |- Repository.php |- UserRepository.php
|
介面
在 app/Repositories/Contracts
資料夾新增一個 RepositoryInterface.php
介面,規定所有模型共用的資料庫存取方法。
1 2 3 4 5 6 7 8
| namespace App\Repositories\Contracts;
use Illuminate\Database\Eloquent\Model;
interface RepositoryInterface { public function getById(int $id): Model; }
|
在 app/Repositories/Contracts
資料夾新增一個 UserRepositoryInterface.php
介面,規定 User
模型專屬的資料庫存取方法。
1 2 3 4 5 6 7 8 9
| namespace App\Repositories\Contracts;
use Illuminate\Database\Eloquent\Model;
interface UserRepositoryInterface { public function getById(int $id): Model; public function getByEmail(string $email): Model; }
|
實作
在 app/Repositories
資料夾新增一個 Repository
抽象類別,實作 RepositoryInterface
介面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| namespace App\Repositories;
use App\Repositories\Contracts\RepositoryInterface; use Illuminate\Database\Eloquent\Model;
abstract class Repository implements RepositoryInterface {
protected Model $model;
abstract public function model(): string;
public function __construct() { $this->model = app($this->model()); }
public function getById(int $id): Model { return $this->model->findOrFail($id); } }
|
在 app/Repositories
資料夾新增一個 UserRepository
具象類別,擴展 Repository
抽象類別,並實作 UserRepositoryInterface
介面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| namespace App\Repositories;
use App\User; use App\Repositories\Contracts\UserRepositoryInterface; use Illuminate\Database\Eloquent\Model;
class UserRepository extends Repository implements UserRepositoryInterface {
public function model(): string { return User::class; }
public function getByEmail(string $email): Model { return $this->model->where('email', $email)->firstOrFail(); } }
|
服務提供者
新增一個 RepositoryServiceProvider
服務提供者。
1
| php artisan make:provider RepositoryServiceProvider
|
在服務提供者註冊容器綁定,並且實作 DeferrableProvider
介面延遲加載:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| namespace App\Providers;
use App\Repositories\Contracts\UserRepositoryInterface; use App\Repositories\UserRepository; use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider implements DeferrableProvider {
public function register() { $this->app->bind( UserRepositoryInterface::class, UserRepository::class ); }
public function boot() { }
public function provides() { return [ UserRepositoryInterface::class, ]; } }
|
- 由於此服務提供者只有做容器綁定的註冊,因此可以延遲載入,以提升系統效能。Laravel 會在綁定的服務被解析時,到
bootstrap/cache/services.php
檔中加載對應的服務提供者。
將服務提供者註冊到 config
資料夾的 app.php
中:
1 2 3 4 5 6 7 8 9 10
| return [
'providers' => [
App\Providers\RepositoryServiceProvider::class,
],
];
|
重新產生 Composer 自動載入檔案。
路由
修改 routes
資料夾的 web.php
檔:
1 2
| Route::get('/id/{id}', 'UserController@getById'); Route::get('/email/{email}', 'UserController@getByEmail');
|
控制器
新增一個 UserController
控制器。
1
| php artisan make:controller UserController
|
修改控制器,將 UserRepositoryInterface
介面注入到建構子中,並實作相關方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| namespace App\Http\Controllers;
use App\Repositories\Contracts\UserRepositoryInterface; use App\User;
class UserController extends Controller { private $repository;
public function __construct(UserRepositoryInterface $repository) { $this->repository = $repository; }
public function getById($id): User { return $this->repository->getById($id); }
public function getByEmail($email): User { return $this->repository->getByEmail($email); } }
|
測試
新增一個 UserControllerTest
測試案例。
1
| php artisan make:test UserControllerTest
|
修改測試案例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| namespace Tests\Feature;
use App\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; use Tests\TestCase;
class UserControllerTest extends TestCase { use RefreshDatabase;
public function testGetById() { $user = factory(User::class)->create();
$response = $this->get('/id/'.$user->id);
$response ->assertStatus(200) ->assertJson($user->toArray()); }
public function testGetByEmail() { $user = factory(User::class)->create();
$response = $this->get('/email/'.$user->email);
$response->assertStatus(200) ->assertJson($user->toArray()); } }
|
執行測試。
程式碼