bigsinoos/laravel-repository-response

PHP 仓库类方法响应解决方案,帮助实现真正的仓库模式。

dev-master 2015-01-12 17:53 UTC

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 实现中定义。

但是,这也打破了另一个规则。**就像仓库类的具体实现一样,我们应该为每个实现定义不同的实体。**(例如,MongoBlogeEntityEloquentBlogEntityDoctorineBlogEntity,但只要您为它们定义一个 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模型实际上是完全可以接受的,当我们需要编写测试时它们非常有用,但它们不允许我们在不同的实现之间切换,因为我们打破了模式。