【问题标题】:How to decouple eloquent from the service layer?如何将 eloquent 与服务层解耦?
【发布时间】:2015-07-21 23:28:25
【问题描述】:

我正在尝试创建一个简洁的服务层,其中服务层作用于一个或多个存储库,每个存储库都作用于自己的雄辩模型。

例如,我可能有:

ForumService
  |
  +-- PostRepo extends PostInterface
  |     |
  |     +-- Post (Eloquent)
  |
  +-- UserRepo extends UserInterface
        |
        +-- User (Eloquent)

每个服务都通过ioc 定义其所需的依赖项。所以,类似:

// MessageService
// ..
public function __construct(UserInterface $userRepository, 
                            MessageInterface $messageRepository) {
    // ..
}

我的存储库是通过它们在各自服务提供商中的绑定来解析的,例如:

class UserRepositoryServiceProvider extends ServiceProvider 
{
    public function register()
    {
        $this->app>bind(
            'App\Models\Repositories\User\UserInterface',
            'App\Models\Repositories\User\UserRepository');
    }
}

这一切都很好。每个服务都获得它需要的存储库。

为了使服务层不依赖于 eloquent,任何离开 repo 的东西都是一个简单的、不可变的数据对象。

日常用语要点:

  • 只有 repo 直接与他们自己的模型对话
  • Repo 返回简单、不可变的数据对象
  • 服务将多个 repo 绑定在一起,并将简化的对象呈现给控制器,最终呈现给视图。

但是我无法在服务或 repo 层为associate 雄辩的模型提出一个干净的模式。

鉴于Post 模型具有belongsTo(User::class) 关系,我如何在Post 存储库层干净地创建该关系。

我试过了:

public function associate($authorId) 
{
    $post->author()->associate($authorId);
}

但是associate 需要一个user 雄辩的对象,而不仅仅是一个id。我可以这样做:

public function associate($authorId) 
{
    $post->from()->associate($userRepo->findEloquent($authorId));
}

但我觉得我正在将一个雄辩的模型呈现到一个不应该对其采取行动的回购中。

【问题讨论】:

  • 您还有其他问题吗?或者答案就是你问的?

标签: laravel eloquent repository-pattern service-layer


【解决方案1】:

我过去所做的为我带来了一些理智的这种情况是做类似于你在第二个 associate 方法中所做的事情,并在存储库前面加上 Eloquent 所以如果我使用Eloquent 之外的其他东西,我只是创建了一个新的存储库实现。

所以在这种情况下,我最终会得到class EloquentUserRepository implements UserInterface。我通常会得到一些公共方法,它们只接受和返回原语,可能还有一些私有方法,这些方法将耦合到 Eloquent,所以我最终要做的是将这些公共方法放入 AbstractUserRepository,或者一个特征,如果它产生更多感觉,保持代码干燥。

【讨论】:

    【解决方案2】:

    这真的取决于情况,我对这些操作以及我的存储库有很多想法。

    我的建议是干脆不使用“关联”功能,你可以简单地这样做:

    $post->user_id = $userID;
    $post->save();
    

    ** 当然,您需要确保具有该 id 的用户存在。

    A) 您可以通过“associatingUser”的特殊服务在外部进行 B)您可以像使用 UserRepositoryInterface 一样进行操作, 我认为将接口添加为依赖项没有问题。

    选项 A:

    class AssociateUserToPost {
    
    private $userRepo;
    private $postRepo;
    
    public function __construct(UserRepoInterface $userRepo, PostRepoInterface $postRepo) {
        $this->userRepo = $userRepo;
        $this->postRepo = $postRepo;
    }
    
    public function associate($userId, $postId) {
        $user = $this->userRepo->getUser($userId);
        if ( ! $user )
            throw new UserNotExistException();
    
        $post = $this->postRepo->getPost($postId);
        if ( ! $post )
            throw new PostNotExistException();
    
        $this->postRepo->AttachUserToPost($postId, $userId);
    }
    
    }
    

    选项 B(完全一样,只是代码位置不同)

    class PostRepository implements PostRepoInterface {
    
    private $userRepo;
    
    public function __construct(UserRepoInterface $userRepo) {
        $this->userRepo = $userRepo;
    }
    
    public function associate($userId, $postId) {
        $user = $this->userRepo->getUser($userId);
        if ( ! $user )
            throw new UserNotExistException();
    
        $post = $this->getPost($postId);
        if ( ! $post )
            throw new PostNotExistException();
    
        $this->AttachUserToPost($postId, $userId);
    }
    
    }
    

    【讨论】:

      【解决方案3】:

      简单的方法:

      public function assignToAuthor($postId, $authorId) 
      {
          $post = $this->find($postId); // or whatever method you use to find by id
      
          $post->author_id = $authorId;
      }
      

      现在,以上意味着您知道关系的外键author_id。为了稍微抽象一下,使用这个:

      public function assignToAuthor($postId, $authorId) 
      {
          $post = $this->find($postId);
      
          $foreignKey = $post->author()->getForeignKey();
      
          $post->{$foreignKey} = $authorId;
      }
      

      请注意,您仍然需要 save $post 模型,但我想您已经知道了。


      根据您使用的简单、不可变的数据对象的实现,您还可以允许传递对象而不是原始 ID。言外之意:

      public function assignToAuthor($postId, $authorId) 
      {
          if ($postId instanceof YourDataOject) {
             $postId = $postId->getId();
          }
      
          if ($authorId instanceof YourDataOject) {
             $authorId = $authorId->getId();
          }
      
          // ...
      }
      

      【讨论】:

        【解决方案4】:

        补水!

        我假设在 post 服务中调用 findEloquent 的另一个原因似乎很恶心,因为您可能已经在控制器中检索了该数据。简而言之,您可以使用 Eloquent 用于将原始查询结果转换为功能齐全的模型的相同方法。

        $userData = array(
            // simple, immutable data
        );
        
        $userCollection = User::hydrate(array($userData));
        
        $userModel = $userCollection->first();
        

        【讨论】:

          【解决方案5】:

          我认为你实际上需要一个额外的层,我称之为经理。这将包含所有业务逻辑,并且仅适用于接口。在后台它将调用服务(每个都知道使用特定的资源/模型)

          【讨论】:

            猜你喜欢
            • 2023-03-31
            • 2021-06-25
            • 1970-01-01
            • 1970-01-01
            • 2011-03-05
            • 1970-01-01
            • 2011-04-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多