0

我在pgsql 8.4中有以下表格:

user
permission
role
user_permission (n:m)
role_permission (n:m)
user_role (n:1)

我有一个 REST 服务,GET /user/1我必须从数据库中获取类似的东西:

{
    id:1,
    name: "John Smith",
    roles: [
        {id: 1, name: "Administrator"},
        {id: 2, name: "Editor"}
    ],
    permissions: [
        {id: 1, method: "GET", resource: "^/$"}
    ]
}

其中角色来自表角色和用户角色,权限来自表权限和用户权限。通过PUT /user/1我想将类似的数据发送到数据库。

是否可以使用 pl/pgsql 函数来做到这一点,或者我必须编写简单的查询,并从 php 中一个接一个地执行它们?

我不一定想将 json 发送到 pgsql,如果这对于没有连接字符串的 php 数组是可能的,等等......那就绰绰有余了。

我无法将服务器升级到更高版本。

关系映射对我不好,我对所有事情都使用存储过程,因为我不想在 php 中编写 sql 模板。我只需要原始数据,我唯一要做的就是json_encode或json_decode,所以我认为经典的ORM方式在这里没用,因为我在数据库中的pl/pgsql函数中有业务逻辑,而php只是验证和转换消息.

4

2 回答 2

1

请注意user并且role是数据库保留字。

只需通过一个查询询问数据库您想要什么:

SELECT
    my_user.id
    my_user.name,
    array_agg(my_role) AS "roles",
    array_agg(permission) AS "permissions"
FROM
  "user" my_user
    LEFT JOIN user_permission up ON my_user.id = up.user_id
    LEFT JOIN permission ON up.permission_id = permission.id
    LEFT JOIN user_role ur ON user.id = ur.user_id
    LEFT JOIN "role" my_role ON my_role.id = ur.role_id

你会得到一个丑陋的字符串,其中包含 dobule 转义的对象到数组中。使用Pomm层将允许您使用转换器直接从此查询中获取 PHP 对象数组。

<?php

public function getUserPermissionsAndRoles($user_id)
{
    $sql = <<<SQL
SELECT
    :user_fields_as_my_user,
    array_agg(my_role) AS "roles",
    array_agg(permission) AS "permissions"
FROM
  ":user_table" my_user
    LEFT JOIN :user_permission_table up ON my_user.id = up.user_id
    LEFT JOIN :permission_table ON up.permission_id = permission.id
    LEFT JOIN :user_role_table ur ON user.id = ur.user_id
    LEFT JOIN ":role_table" my_role ON my_role.id = ur.role_id
WHERE
  my_user.id = ?
SQL;

    $sql = strtr($sql, array(
        ":user_fields_as_my_user" => $this->formatFieldsWithAlias('getSelectFields', 'my_user'),
        ":user_table" => $this->getTableName(),
        ":user_permission_table" => $this->getConnection()->getMapFor("\Db\App\UserPermission")->getTableName(),
        ":permission_table" => $this->getConnection()->getMapFor("\Db\App\Permission")->getTableName(),
        ":user_role_table" => $this->getConnection()->getMapFor("\Db\App\UserRole")->getTableName(),
        ":role_table" => $this->getConnection()->getMapFor("\Db\App\Role")->getTableName(),
        ));

    return $this->query($sql, array($user_id));
}

为了告诉 Pomm 它可以是 user_role、user_permission、permission 和 role 数据库对象的转换器,在实例化您的数据库时,添加以下内容:

$database = new \Pomm\Database(array("dsn" => "pgsql://user:pass@host:port/db_name", "name" => "app"));

$cnct = $database->getConnection();

$database
    ->registerConverter('UserPermission', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\UserPermission')), array('user_permission'))
    ->registerConverter('Permission', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\Permission')), array('permission'))
    ->registerConverter('UserRole', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\UserRole')), array('user_role'))
    ->registerConverter('Role', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\Role')), array('role'))
    ;

现在数据库知道可以使用这些转换器,但必须知道何时解析名为“roles”和“permissions”的结果字段,它必须将它们作为实体UserMap数组处理。这就是类的功能所代表的含义:RolePermissioninitialize()UserMap

<?php

class UserMap extends \Db\App\Base\UserMap
{
    public function initialize()
    {
        parent::initialize();

        $this->addVirtualField('roles', 'Role[]');
        $this->addVirtualField('permissions', 'Permission[]');
    }

现在,在您的控制器中,您只需调用模型的方法并将其转换为 JSON 响应:

    <?php

    $database = new \Pomm\Database(array("dsn" => "pgsql://user:pass@host:port/db_name", "name" => "app"));

    $cnct = $database->getConnection();

    $database
        ->registerConverter('UserPermission', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\UserPermission')), array('user_permission'))
        ->registerConverter('Permission', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\Permission')), array('permission'))
        ->registerConverter('UserRole', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\UserRole')), array('user_role'))
        ->registerConverter('Role', new \Pomm\Converter\PgEntity($cnct->getMapFor('\Db\App\Role')), array('role'))
        ;

    $collection = $cnct->getMapFor('\Db\App\User')
        ->getUserPermissionsAndRoles($_GET['user_id']) // <- get an iterator over fetched users with extra info.
        ;

    echo json_encode($collection->extract()); // <- dump an array from the iterator and cast it to json. 

当然,纯 PHP 部分使用像Silex这样的微框架会更容易,您的控制器将是:

<?php //...

$app->get('/user/{user_id}', function($user_id) use ($app) {
    $collection = $app['pomm.connection']
        ->getMapFor('\Db\App\User')
        ->getUserPermissionsAndRoles($user_id);

    if ($collection->count() === 0)
    {
        $this->abort(404, sprintf("No such user '%s'.", $user_id));
    }

    return $app->json($collection->extract());
});

干杯!

于 2013-07-05T14:11:13.770 回答
1

有可能:“从 PL/pgSQL 函数返回嵌套的复合类型”,但是由于 pgsql 中没有内置的关联数组,因此很难将数据转换为 php 关联数组和 json 之后。所以我认为最好的解决方案是通过多个查询读取数据并使用 php 构建关联数组。

在 pgsql 9.2 中,使用 json 数据类型和自定义记录集-> json 聚合函数会更容易。在 8.4 中,我认为这不值得努力......

于 2013-07-08T13:41:41.773 回答