如何将口才与服务层分离?

时间:2015-05-11 12:45:04

标签: laravel eloquent repository-pattern service-layer

我正在尝试创建一个干净的服务层,服务层作用于一个或多个存储库,每个存储库都依赖于它自己的雄辩模型。

例如,我可能有:

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');
    }
}

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

为了使服务层不受任何特定依赖性的影响,任何离开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));
}

但我觉得我正在将一个雄辩的模型展示成一个不应该采取行动的回购。

5 个答案:

答案 0 :(得分:2)

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

所以在这种情况下,我最终会得到class EloquentUserRepository implements UserInterface。我通常最终会得到一些公共方法,这些公共方法只接受和返回原语以及可能与Eloquent相关联的一些私有方法,所以我最终要做的就是将这些公共方法放入AbstractUserRepository或者特征中。更有意义的是,保持代码DRY。

答案 1 :(得分: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);
}

}

答案 2 :(得分:2)

简单方法:

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();
    }

    // ...
}

答案 3 :(得分:0)

<强>水化<!/强>

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

webdriver.xpi

答案 4 :(得分:0)

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