Cakephp 3 - 如何在 table 中集成外部资源?
Cakephp 3 - How to integrate external sources in table?
我正在开发一个应用程序,它有自己的数据库并从另一个服务获取用户信息(在这种情况下是 LDAP,通过 API 包)。
假设我有一个名为 Articles
的 table,其中有一列 user_id
。没有 Users
table,而是通过外部 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();
}
我不确定如何处理这个问题。
我应该将代码放在 table 对象中的什么位置以允许与外部 API 集成?
还有一个额外的问题:如何在填充实体时最小化 LDAP 查询的数量?
即,通过首先使用单个 ->getUsers()
检索相关用户并稍后放置它们似乎要快得多,即使遍历文章并使用多个 ->getUser()
可能更简单。
最简单的解决方案是使用结果格式化程序来获取和注入外部数据。
更复杂的解决方案是自定义关联和自定义关联加载器,但考虑到以数据库为中心的关联,您可能还必须想出一个 table 和一个查询实现处理您的 LDAP 数据源。虽然将其移动到自定义关联中会相当简单,但包含该关联将查找匹配项 table,导致模式被检查等
因此,我将坚持为第一个选项提供示例。结果格式化程序会非常简单,像这样:
$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()
returns.
除此之外,示例应该是不言自明的,首先获取在查询文章中找到的用户 ID 的唯一列表,获取使用这些 ID 的 LDAP 用户,然后将用户注入文章。
如果您想在结果中包含实体,请从用户数据创建实体,例如:
$userData = $usersMap[$article['user_id']];
$article['user'] = new \App\Model\Entity\User($userData);
为了更好的可重用性,请将格式化程序放在自定义查找器中。在你的 ArticlesTable
class:
public function findWithUsers(\Cake\ORM\Query $query, array $options)
{
return $query->formatResults(/* ... */);
}
那你就可以$this->Articles->find('withUsers')
,就这么简单
另见
我正在开发一个应用程序,它有自己的数据库并从另一个服务获取用户信息(在这种情况下是 LDAP,通过 API 包)。
假设我有一个名为 Articles
的 table,其中有一列 user_id
。没有 Users
table,而是通过外部 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();
}
我不确定如何处理这个问题。
我应该将代码放在 table 对象中的什么位置以允许与外部 API 集成?
还有一个额外的问题:如何在填充实体时最小化 LDAP 查询的数量?
即,通过首先使用单个 ->getUsers()
检索相关用户并稍后放置它们似乎要快得多,即使遍历文章并使用多个 ->getUser()
可能更简单。
最简单的解决方案是使用结果格式化程序来获取和注入外部数据。
更复杂的解决方案是自定义关联和自定义关联加载器,但考虑到以数据库为中心的关联,您可能还必须想出一个 table 和一个查询实现处理您的 LDAP 数据源。虽然将其移动到自定义关联中会相当简单,但包含该关联将查找匹配项 table,导致模式被检查等
因此,我将坚持为第一个选项提供示例。结果格式化程序会非常简单,像这样:
$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()
returns.
除此之外,示例应该是不言自明的,首先获取在查询文章中找到的用户 ID 的唯一列表,获取使用这些 ID 的 LDAP 用户,然后将用户注入文章。
如果您想在结果中包含实体,请从用户数据创建实体,例如:
$userData = $usersMap[$article['user_id']];
$article['user'] = new \App\Model\Entity\User($userData);
为了更好的可重用性,请将格式化程序放在自定义查找器中。在你的 ArticlesTable
class:
public function findWithUsers(\Cake\ORM\Query $query, array $options)
{
return $query->formatResults(/* ... */);
}
那你就可以$this->Articles->find('withUsers')
,就这么简单
另见