首页 > 解决方案 > Cakephp 3 - 如何在表格中集成外部资源?

问题描述

我正在开发一个拥有自己的数据库并从另一个服务获取用户信息的应用程序(在这种情况下是一个 LDAP,通过一个 API 包)。

假设我有一个名为的表Articles,有一个列user_id。没有Users表,而是通过外部 API 检索一个用户或一组用户:

$user = LDAPConnector::getUser($user_id);
$users = LDAPConnector::getUsers([1, 2, 5, 6]);

当然,我希望从控制器内部检索数据尽可能简单,理想情况下仍然使用以下内容:

$articles = $this->Articles->find()->contain('Users');

foreach ($articles as $article) {
    echo $article->user->getFullname();
}

我不知道如何解决这个问题。

我应该将代码放在表对象的什么位置以允许与外部 API 集成?

作为一个额外的问题:如何在填充实体时最小化 LDAP 查询的数量?
即,首先使用单个检索相关用户并稍后放置它们似乎要快得多->getUsers(),即使迭代文章并使用多个->getUser()可能更简单。

标签: cakephpldapcakephp-3.0data-retrieval

解决方案


最简单的解决方案是使用结果格式化程序来获取和注入外部数据。

更复杂的解决方案是自定义关联和自定义关联加载器,但考虑到以数据库为中心的关联的方式,您可能还必须提出一个表和可能的查询实现来处理您的 LDAP 数据源。虽然将它移动到自定义关联中会相当简单,但包含关联将查找匹配表,导致检查架构等。

所以我会坚持为第一个选项提供一个例子。结果格式化程序将非常简单,如下所示:

$this->Articles
    ->find()
    ->formatResults(function (\Cake\Collection\CollectionInterface $results) {
        $userIds = array_unique($results->extract('user_id')->toArray());

        $users = LDAPConnector::getUsers($userIds);
        $usersMap = collection($users)->indexBy('id')->toArray();

        return $results
            ->map(function ($article) use ($usersMap) {
                if (isset($usersMap[$article['user_id']])) {
                    $article['user'] = $usersMap[$article['user_id']];
                }

                return $article;
            });
        });

该示例假设返回的数据LDAPConnector::getUsers()是关联数组的集合,其id键与用户 ID 匹配。您必须相应地调整它,具体取决于确切LDAPConnector::getUsers()返回的内容。

除此之外,该示例应该是不言自明的,首先获取在查询文章中找到的用户 ID 的唯一列表,获取使用这些 ID 的 LDAP 用户,然后将用户注入文章中。

如果您想在结果中包含实体,则从用户数据创建实体,例如:

$userData = $usersMap[$article['user_id']];
$article['user'] = new \App\Model\Entity\User($userData);

为了获得更好的可重用性,请将格式化程序放在自定义查找器中。在你的ArticlesTable课堂上:

public function findWithUsers(\Cake\ORM\Query $query, array $options)
{
    return $query->formatResults(/* ... */);
}

然后你就可以做$this->Articles->find('withUsers'),就像包含一样简单。

也可以看看


推荐阅读