mirror of
https://gitee.com/ShopeX/ECShopX
synced 2026-08-05 11:45:51 +08:00
删除文件 docs/docs/framework_architecture-repository.md
This commit is contained in:
@@ -1,339 +0,0 @@
|
||||
---
|
||||
url: framework_architecture-repository
|
||||
---
|
||||
|
||||
# Repository
|
||||
|
||||
> **使用 Repository 辅助 Model**<br />
|
||||
|
||||
|
||||
若将数据库逻辑都写在 model,会造成 model 的肥大而难以维护,基于 SOLID 原则,我们应该使用 Repository 模式辅助 model,将相关的数据库逻辑封装在不同的 repository,方便中大型项目的维护。
|
||||
|
||||
<a name="c101406b"></a>
|
||||
## 数据库逻辑
|
||||
|
||||
在 CRUD 中,CUD 比较稳定,但 R 的部分则千变万化,大部分的数据库逻辑都在描述R 的部分,若将数据库逻辑写在 controller 或 model 都不适当,会造成 controller 与 model 肥大,造成日后难以维护。
|
||||
|
||||
<a name="Model"></a>
|
||||
## Model
|
||||
|
||||
如果使用 Eloquent ORM 来实现 Repository 模式,需要修改 model 不要包含数据库逻辑,仅保留以下部分:
|
||||
|
||||
- Property : 如$table,$fillable…等。
|
||||
- Mutator: 包括 mutator 與 accessor。
|
||||
- Method : relation 類的 method,如使用 hasMany() 與 belongsTo()。
|
||||
|
||||
简化后的Eloquent 的 model 类我们可以称之为`Eloquent Class`,由于继承了`Illuminate\Database\Eloquent\Model` 类,所以`Eloquent Class`仍然有很多方法可以操作数据库。
|
||||
|
||||
Eloquent Class 代码结构如下:
|
||||
|
||||
```php
|
||||
namespace MyBlog;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
/**
|
||||
* MyBlog\User
|
||||
*
|
||||
*/
|
||||
class User extends Model {
|
||||
|
||||
/**
|
||||
* The database table used by the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['name', 'email', 'password'];
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['password', 'remember_token'];
|
||||
}
|
||||
```
|
||||
|
||||
而在 ECShopX 中,由于我们采用了 Doctrine ORM,其自带Repository模式。在 Doctrine ORM 中也有简化的 model 类: `Entity Class`。
|
||||
|
||||
相比 `Eloquent Class` `Entity Class` 更简单,其只包含 `protected` 或 `private`的属性和`getter` 和 `setter` 方法(在 JAVA 中,这种对象成为值对象)。
|
||||
|
||||
Entity Class 代码结构如下:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace MyBlog;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="users")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
protected $id;
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
protected $name;
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
protected $email;
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
protected $password;
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
protected $remember_token;
|
||||
// .. (other code getter setter )
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Repository"></a>
|
||||
## Repository
|
||||
|
||||
初学者常会在 controller 直接调用 model 写数据库逻辑:
|
||||
|
||||
```php
|
||||
public function index()
|
||||
{
|
||||
$users = User::where('age', '>', 20)
|
||||
->orderBy('age')
|
||||
->get();
|
||||
|
||||
return view('users.index', compact('users'));
|
||||
}
|
||||
```
|
||||
|
||||
数据库逻辑是`获取 20 岁以上的用户`。
|
||||
|
||||
在中大型项目中,会有几个问题 :
|
||||
|
||||
- 将数据库逻辑写在 controller,造成 controller 的肥大难以维护。
|
||||
- 违反 SOLID 的单一职责原则 : 数据库逻辑不应该写在 controller。
|
||||
- controller 直接相依于model,使得我们无法对 controller 做单元测试。
|
||||
|
||||
比较好的方式是使用 repository :
|
||||
|
||||
- 将 model 依赖注入到 repository。
|
||||
- 将数据库逻辑写在 repository。
|
||||
- 将 repository 依赖注入到 service。
|
||||
|
||||
以此原则,我们看下,采用 Eloquent ORM 如何使用 Repository 模式。
|
||||
|
||||
UserRepository.php
|
||||
|
||||
```php
|
||||
namespace MyBlog\Repositories;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use MyBlog\User;
|
||||
|
||||
class UserRepository
|
||||
{
|
||||
/** @var User 注入的User model */
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* UserRepository constructor.
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回年龄大于$age的用户
|
||||
* @param integer $age
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAgeLargerThan($age)
|
||||
{
|
||||
return $this->user
|
||||
->where('age', '>', $age)
|
||||
->orderBy('age')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
第 8 行
|
||||
|
||||
```php
|
||||
/** @var User 注入的User model */
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* UserRepository constructor.
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
```
|
||||
|
||||
将依赖的 User model 依赖注入到 UserRepository。
|
||||
|
||||
UserController.php
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests;
|
||||
use MyBlog\Repositories\UserRepository;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/** @var UserRepository 注入的UserRepository */
|
||||
protected $userRepository;
|
||||
|
||||
/**
|
||||
* UserController constructor.
|
||||
*
|
||||
* @param UserRepository $userRepository
|
||||
*/
|
||||
public function __construct(UserRepository $userRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$users = $this->userRepository
|
||||
->getAgeLargerThan(20);
|
||||
|
||||
return view('users.index', compact('users'));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
将依赖的 UserRepository 依赖注入到 UserController。
|
||||
|
||||
26行
|
||||
|
||||
```php
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$users = $this->userRepository
|
||||
->getAgeLargerThan(20);
|
||||
|
||||
return view('users.index', compact('users'));
|
||||
}
|
||||
```
|
||||
|
||||
从原本直接依赖 User model,改成依赖注入的 UserRepository。
|
||||
|
||||
改用这种写法,有几个优点:
|
||||
|
||||
- 将数据库逻辑写在存储库中,解决控制器肥大问题。
|
||||
- 符合SOLID的单一职责原则:数据库逻辑写在repository,没写在controller。
|
||||
- 符合SOLID的依赖转换原则:controller 并非直接相依于存储库,而是将存储库依赖注入进controller。
|
||||
|
||||
>
|
||||
|
||||
**实际上建议repository仅依赖注入于service,而不要直接注入在controller,本范例因为还没介绍到servie模式,以便简化起见,所以直接注入于controller**。<br />
|
||||
<br />
|
||||
|
||||
>
|
||||
|
||||
**是否该建立存储库接口?**<br />
|
||||
|
||||
|
||||
理论上使用依赖注入时,应该使用接口,不过接口目的在于通过抽象化,方便在后期更换接口的具体实现,以便让程序达到开放封闭的要求,但是实际上要更换存储库的机会不高,除非你有更换资料库的需求,如从MySQL抽换到MongoDB,此时就该建立存储库接口。
|
||||
|
||||
不过由于我们使用了依赖注入,将来要从类改成interface也很方便,只要在构造方法的类型提示改成interface即可,维护成本很低,所以在此大可使用的存储库类,用接口反而会造成设计过度,等真正需求来时再重构成接口即可。
|
||||
|
||||
采用 Doctrine ORM 如何使用 Repository 模式。
|
||||
|
||||
由于 Doctrine ORM 自带 Repository 模式,所以直接使用即可。
|
||||
|
||||
UserRepository.php
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class UserRepository extends EntityRepository {
|
||||
|
||||
/**
|
||||
* 返回年龄大于$age的用户
|
||||
* @param integer $age
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAgeLargerThan($age)
|
||||
{
|
||||
return $this->user
|
||||
->where('age', '>', $age)
|
||||
->orderBy('age')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
UserController.php
|
||||
|
||||
```php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests;
|
||||
use MyBlog\Repositories\UserRepository;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/** @var UserRepository 注入的UserRepository */
|
||||
protected $userRepository;
|
||||
|
||||
/**
|
||||
* UserController constructor.
|
||||
*
|
||||
* @param UserRepository $userRepository
|
||||
*/
|
||||
public function __construct(UserRepository $userRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$users = $this->userRepository
|
||||
->getAgeLargerThan(20);
|
||||
|
||||
return view('users.index', compact('users'));
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user