bigsinoos / laravel-repository-response
PHP 仓库类方法响应解决方案,帮助实现真正的仓库模式。
Requires
- illuminate/container: ~5.0
- illuminate/contracts: ~5.0
- illuminate/database: ~5.0
- illuminate/support: ~5.0
Requires (Dev)
- mockery/mockery: *
- phpunit/phpunit: 4.4.*
This package is not auto-updated.
Last update: 2024-09-14 16:44:59 UTC
README
仓库模式的趋势使我们都在我们的 Laravel 项目中实现它。但我们所有人都执行得不当或至少是不完整的。问题在于,在真正的仓库模式中,除了输入契约(我们的仓库接口)之外,我们还对方法响应进行了协商,这在动态语言如 PHP 中是不可能的(已经在 PHP7 中添加)。此软件包可以帮助您近似实现真正的模式。
我们应该知道,没有必要实现完整的真正模式,模式已经被制定来解决问题,而不是成为问题本身。当前流行的模式不完整,足以简化测试。
安装
composer require bigsinoos/laravel-repository-response
工作原理
真正的模式与实体而不是我们的 Eloquent 模型交互。因此,使用 Bigsinoos\RepositoryResponse\BaseEloquentEntity
,这个问题得到了解决。因此,我们不是将消息传递给 Eloquent
模型,而是必须传递我们的实体。当使用 Eloquent 仓库时,我们可以将模型放入我们的实体中,该实体仅由我们的 EloquentRepository
实现通过帮助 友元 / 同族 类访问。
友元类和 debug_back_trace 函数
在面向对象设计的范围内,友元类可以打破彼此的封装层,例如,它们可以调用彼此的 protected
方法。PHP 完全不支持友元类。
当我们从我们的仓库方法返回 Eloquent 模型时,我们允许外部访问我们的数据库层,这应该通过我们的仓库契约完成,这简单地打破了模式,为了解决这个问题,而不是从仓库返回 Eloquent 模型,我们简单地为模型创建一个新的实体,并将其包装起来,所以每次从实体调用方法或属性时,实体都会表现得如下
- 检查它是否是从友元类中调用的(使用 debug_backtract)。
- 如果是这样,就调用它的模型的方法,否则它会抛出一个访问拒绝异常。
当尝试在实体上设置属性时,它会被设置在它的模型上。
通过这种方式,您可以向多个仓库传递实体,只要它们是友好的,它们的 Eloquent 模型就可以使用,友好性在实体的 Eloquent 实现中定义。
但是,这也打破了另一个规则。**就像仓库类的具体实现一样,我们应该为每个实现定义不同的实体。**(例如,MongoBlogeEntity
、EloquentBlogEntity
、DoctorineBlogEntity
,但只要您为它们定义一个 BlogEntityInterface
合约,就没有问题,至少它允许在不同实现之间切换,这是我们的先前方法(从方法返回 Eloquent 模型)所不可能的)。
用法
用户仓库的工作流程如下
- UserEntityContract.php
- EloquentUserEntity.php:这个类应该扩展
Bigsinoos\RepositoryResponse\BaseEloquentEntity
类,以获得类的友好功能。 - User.php
- UserRepositoryContract.php
- EloquentUserRepository.php
UserEntityContract.php
<?php interface UserEntityContract {}
EloquentUserEntity.php
<?php use \Bigsinoos\RepositoryResponse\BaseEloquentEntity; class EloquentUserEntity extends BaseEloquentEntity implements \UserEntityContract { /** * Friends of the user repository * * @var array */ protected $friends = [ 'EloquentUserRepository' ]; /** * Get new eloquent model for each entity, it is used when you set * an attribute on the entity to the entity will make an instance of the model * and sets the attribute on it. * * @param array $attributes * @return \Illuminate\Database\Eloquent\Model */ protected function getNewModel($attributes = []) { return new \User($attributes); } }
User.php
<?php class User extends \Eloquent { protected $table = 'users'; }
UserRepositoryContract.php
interface UserRepositoryContract { /** * Finds the user gieven his/her id * * @param int $id * @return \UserEntityContract */ public function findById($id); /** * Take a collection of users * * @param int $howMuch * @param bool $decreasing * @return \Illuminate\Support\Collection */ public function take($howMuch = 10, $sortBy = 'created_at', $decreasing = true); }
EloquentUserRepository.php
class EloquentUserRepository implements UserRepositoryContract { protected $userEntity; public function __construct(\UserEntityContract $userEntity) { $this->userEntity = $userEntity; } public function findById($id) { $model = $this->userEntity->getModel(); // Only friend class can do this. $found = $model->newInstance()->findOrFail($id); $entity = $this->userEntity->newInstance(); $entity->setModel($found); // Only friend class can do this. return $entity; } public function take($howMuch = 10, $sortBy = 'created_at', $decreasing = true) { $collection = $this->userEntity ->getModel() ->newInstance() ->orderBy($sortBy, ((bool) $decreasing) ? 'desc' ? 'asc') ->take((int) $howMuch)->get(); // Don't do this for large data sets. return $this->buildEntityCollection($collection); } protected function buildEntityCollection(\Illuminate\Support\Collection $collection) { $class = 'Illuminate\Support\Collection'; $items = []; $collection->each(function($item)){ $entity = $this->userEntity->newInstance(); $entity->setModel($item); $items [] = $entity; }); return app($class)->make($items); } }
ExampleController.php
class ExampleController extends \BaseController { protected $userRepo; public function __construct(\UserRepositoryContract $userRepo) { $this->userRepo = $userRepo; } public function show($id) { $user = $this->userRepo->findById($id); // $user->getModel(); throws a MethodNotAllowedException // $user->delete(); throws a MethodNotAllowedException // $user->somethingStupid(); throws as MethodNotFoundException return view('user', compact('user'); } }
异常
Bigsinoos\RepositoryResponse\Exceptions\EntityExceptionInterface
所有异常都实现此契约。Bigsinoos\RepositoryResponse\Exceptions\EntityException
上面的契约的基本实现,用于意外的行为。Bigsinoos\RepositoryResponse\Exceptions\MethodNotAllowedException
如果尝试从非友元类访问eloquent模型,将会抛出此异常。Bigsinoos\RepositoryResponse\Exceptions\MethodNotAllowedException
如果请求的模型类中找不到所需的内容,将会抛出此异常。
从方法仓库返回简单的eloquent模型实际上是完全可以接受的,当我们需要编写测试时它们非常有用,但它们不允许我们在不同的实现之间切换,因为我们打破了模式。