在 CakePHP 中使用特定命令进行任何数据库访问

发布于 2024-12-10 04:45:52 字数 2317 浏览 0 评论 0 原文

我是 CakePHP 的新手,使用的是 1.3 版。

在任何数据库操作之前,如何动态更改 DATABASE_CONFIG 中找到的“架构”属性?在任何数据库交互之前,我可以在哪个类中执行 postgres 特定命令“set search_path to 'schema_xyz'”?

我想利用 Postgres 的能力在单个数据库中维护多个不同的命名空间(也称为 postgres 术语中的架构),以在我的应用程序中实现多租户。也就是说,每个名称空间将包含相同的一组表,但显然具有不同的内容。在这里,重要的是不要将模式理解为表元数据,而是将其理解为postgres 特定的命名空间概念,其中 schema 是一个容器表。确切的 Postgres 命令并不重要。是什么,是它可以被调用的机制,并避开 Cake 的表描述的典型含义,如 SchemaShell 中所示。我发现 Cake 暴露命名空间概念的唯一地方是在database.php 文件中,该文件在首次建立数据库连接时使用。请参阅:api13.cakephp.org/view_source/dbo-postgres/#line-113(新用户链接限制,抱歉)

    if ($this->connection) {
      $this->connected = true;
      $this->_execute("SET search_path TO " . $config['schema']);

我想在所有数据库查询之前设置该 search_path,而不仅仅是在当前完成的连接时设置。

作为概念证明,我尝试在模型中设置 $useDbConfig,但根据打印 SQL 命令的调试输出,这似乎只影响所有查询的子集。我已将其移至 app_model.php 中,结果相同。与通过动态创建 db_config 实例并通过 loadDataSource 传递给 ConnectionManager 来增强它一样。也许我应该将该代码放入所有类型的 before... 方法中。

我在网上看到很多帖子,人们讨论使用 database.php 中的几种数据库配置之一来为开发、实验室和生产环境使用不同的数据库。但我有一个具有多个命名空间/模式的数据库。另外,我的此类命名空间的数量太大且动态太大,无法在 database.php 中对新变量进行硬编码。

因此,CakePHP 中的哪个位置可以在任何数据库命令之前插入一些内容来设置 search_path?稍后我会处理优化问题。请记住,我是 Cake 新手,所以请尽量具体。如果我可以澄清这个问题,请告诉我。

提前致谢。这是部分工作的代码片段:

class AppModel extends Model {
  function beforeFind($queryData)
  {
    App::import("ConnectionManager");
    $cm = &ConnectionManager::getInstance();

    $namespace = 'xyz_namespace';            //name of the new schema/namespace/search path 
    $new_db_config_name = 'new_config';      //name for the new DB config to be used in the ConnectionManager
    $new_db_config = $cm->config->default; //copy the 'default' DB config into an array
    $new_db_config['schema'] = $namespace; //change/add the new schema/namespace/search path

    $cm->create($new_db_config_name, $new_db_config);   //turn the array into a DbConfig object
    $cm->loadDataSource($new_db_config_name);           //load the new DbConfig into the ConnectionManager
    $this->useDbConfig = $new_db_config_name;       //tell the model to new use the Db Config


    return $queryData;
  } 
}

I'm new to CakePHP and using version 1.3.

How can I dynamically change the 'schema' property as found in DATABASE_CONFIG prior to any database operation? What is the class where I could have the postgres-specific command "set search_path to 'schema_xyz'" executed before any database interaction?

I want to use Postgres' ability to maintain multiple distinct namespaces (aka schema in postgres parlance) within a single database to implement multi-tenancy in my application. That is, every namespace will contain the same set of tables, but evidently with different content. Here, it's important not to understand schema as meaning table metadata, but rather as the postgres-specific concept of namespace where a schema is a container for tables. The exact Postgres command isn't important. What is, is the mechanism by which it can be invoked, and steering clear of Cake's typical meaning of table description, as seen in the SchemaShell. The only place I have found where Cake exposes the concept of namespace is in the database.php file, which is then used when the DB connection is first established. See: api13.cakephp.org/view_source/dbo-postgres/#line-113 (new user link limit, sorry)

    if ($this->connection) {
      $this->connected = true;
      $this->_execute("SET search_path TO " . $config['schema']);

I want to set that search_path before ALL DB queries, not just at connection time as is currently done.

As a proof of concept, I have tried setting $useDbConfig in my models, but according to the debug output where the SQL commands are printed, this only seems to affect a subset of all queries. I've moved this up into app_model.php with the same result. As did augmenting that with creating a db_config instance on the fly and passing to the ConnectionManager through loadDataSource. Maybe I should slap that code in all flavors of before... methods.

I have seen many posts online where people discuss using one of several DB configurations in database.php to use different databases for dev, lab and production environments. But I have a single database with multiple namespaces/schemas. Also, my number of such namespaces will be too high and dynamic to make hardcoding a new variable in database.php practical.

Thus, where is the spot in CakePHP where I could insert something to set the search_path prior to any database command? I'll deal with optimizing that later. Remember that I'm new to Cake, so try to be as specific as you can. Let me know if I can clarify this question.

Thanks in advance. Here's the partially working code snippet:

class AppModel extends Model {
  function beforeFind($queryData)
  {
    App::import("ConnectionManager");
    $cm = &ConnectionManager::getInstance();

    $namespace = 'xyz_namespace';            //name of the new schema/namespace/search path 
    $new_db_config_name = 'new_config';      //name for the new DB config to be used in the ConnectionManager
    $new_db_config = $cm->config->default; //copy the 'default' DB config into an array
    $new_db_config['schema'] = $namespace; //change/add the new schema/namespace/search path

    $cm->create($new_db_config_name, $new_db_config);   //turn the array into a DbConfig object
    $cm->loadDataSource($new_db_config_name);           //load the new DbConfig into the ConnectionManager
    $this->useDbConfig = $new_db_config_name;       //tell the model to new use the Db Config


    return $queryData;
  } 
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小猫一只 2024-12-17 04:45:52

如果您想为每个登录角色切换模式,PostgreSQL 中有一种非常简单的方法:

ALTER ROLE foo SET search_path=bar, public;
ALTER ROLE baz SET search_path=bam, public;

因此,由该角色启动的连接会自动设置 search_path。
如果您的登录名与所需的架构名称相同,还有一种更简单的方法,我引用 精美手册

如果列表项之一是特殊值 $user,则架构
如果存在,则替换为 SESSION_USER 返回的名称
这样的模式。 (如果不是,$user 将被忽略。)

但请注意 - 罚款再次手动:

角色特定的变量设置仅在登录时生效;设定角色
和 SET SESSION AUTHORIZATION 不处理特定于角色的变量
设置。

There is a very simple way in PostgreSQL if you want to switch schema per login role:

ALTER ROLE foo SET search_path=bar, public;
ALTER ROLE baz SET search_path=bam, public;

Thus a connection initiated by that role has that search_path set automatically.
If your login names are the same as the desired schema names, there is an even simpler way, I quote the fine manual:

If one of the list items is the special value $user, then the schema
having the name returned by SESSION_USER is substituted, if there is
such a schema. (If not, $user is ignored.)

But be advised that - the fine manual again:

Role-specific variable settings take effect only at login; SET ROLE
and SET SESSION AUTHORIZATION do not process role-specific variable
settings.

掩于岁月 2024-12-17 04:45:52

如果我正确理解你的问题,(请耐心等待,我对 Postgres 知之甚少,但基本上我认为你的意思是,每当表透视发生变化时就重新加载架构?),以下是如何在控制器中动态切换架构:

// Model::getDataSource()->configKeyName holds whichever db config you're using
if ($this->Model->getDataSource()->configKeyName != 'default') {
    // do something...
    $this->loadModel("Special")
    $this->Model->table = "extras";
    $this->Model->schema(true);
} else {
    // predictably, Model::setDataSource($configKey) changes configs
    $this->Model->setDataSource("offsite"); // this could be a string variable
}

或者从模型, $this->getDataSource()->configKeyName$this->schema(true) 等等。注意 $this->schema(true) 实际上会重新加载模型架构并将其注册到 cake 中。 app_model、组件或 config/bootstrap 可能是合适的位置。我不确定 Cake 第一次在哪里定义 search_path ,但它几乎肯定是 dataSource 对象的一个​​属性,并且可以在那里重新定义,就像表名等一样。然后重新加载 Cake 的模式以注册更改的内容小路。有必要确保 Cake 卸载它可能获取的任何默认值,并根据当前定义的表加载正确的模式。 (听起来这可能是您错过的唯一一步。)

如果这不能回答您的问题或者我误解了,请告诉我。 HTH。 :)

If I understand your question correctly, (bear with me, I know little about Postgres but basically I think you mean, reloading the schema whenever the table perspective changes?), here's how to dynamically switch schemas in your controller:

// Model::getDataSource()->configKeyName holds whichever db config you're using
if ($this->Model->getDataSource()->configKeyName != 'default') {
    // do something...
    $this->loadModel("Special")
    $this->Model->table = "extras";
    $this->Model->schema(true);
} else {
    // predictably, Model::setDataSource($configKey) changes configs
    $this->Model->setDataSource("offsite"); // this could be a string variable
}

Or from the model, $this->getDataSource()->configKeyName and $this->schema(true) and so forth. Note $this->schema(true) actually reloads the model schema and registers it with cake. app_model, a component, or config/bootstrap might be an appropriate place for this. I'm not sure where Cake would have defined the search_path the first time, but it would almost certainly be a property of the dataSource object and could be redefined there just like the table name, etc. And then reload Cake's schema to register the changed path. It is necessary to ensure Cake unloads any default it may have picked up, and load the correct schema based on the currently defined table. (It sounds like this may have been the only step you were missing.)

If this does not answer your question or if I misunderstood, let me know. HTH. :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文