确保应用程序的用户始终在应用程序的演示版本中

时间:2011-09-19 14:25:22

标签: php mysql symfony1 doctrine sync

我正在创建一个现有symfony php应用程序的版本,该应用程序将用作沙盒,即应用程序的一种演示版本。 这两个应用程序将在同一台服务器上使用单独的mysql架构。 这两个模式是相同的,沙盒模式将被删除,并在每天开始时使用主应用程序中的数据重新创建。 在白天,可以在主应用程序中创建/更新用户,我希望这些更改立即反映在沙盒应用程序中 - 因此,我需要在主应用程序中更改时从大约三个相关表中复制更改。 / p>

我考虑过在主模式中的必需表上创建触发器,但是我很幸运找到了AFTER INSERT和AFTER UPDATE trigger_body的例子。

我考虑过修改与这三个表关联的Doctrine对象,以便通过单独的Doctrine_Connection(对于沙箱dsn)进行保存。

我考虑在主应用程序中扩展sfDoctrineGuardPlugin以为这两个应用程序提供身份验证,但这仍然需要从三个表中传输数据。

我有没有考虑过这方法?哪种方法最好?

1 个答案:

答案 0 :(得分:0)

我通过在沙箱代码库中执行以下操作解决了这个问题:

  • 为sfGuardValidatorUser实现了另一个验证器,并使用auth_user_validator键将app.yml指向此类。
  • 实现了私有验证方法_checkMainApp,如果在沙箱用户表中找不到用户,则使用用户提供的凭据调用该方法。它执行以下操作:
    • 设置与主app db的临时连接(解决一些错误symfonydoctrine
    • 查询用户的主数据库(然后恢复原始数据库连接)
    • 如果找到用户且密码正常,请执行对象的深层复制
    • 最后,只应复制用户及其配置文件,并且任何其他相关对象(可通过深度复制的用户获得)应位于沙箱数据库中,并应替换现有关系 - 这是_fixCopiedRelations所做的(在繁琐的时尚)。


# lib/validator/yiValidatorUserSandbox.class.php
    protected function doClean($values)
    {
        // snip

        // don't allow to sign in with an empty username
        if ($username)
        {
            // snip

            // user exists?
            if ($user) {
                // password is ok?
                // snip
            } else if ($user = $this->_checkMainApp($username, $password)) {
                return array_merge($values, array('user' => $user));
            }
        }
        // snip
    }

    private function _checkMainApp($username, $password)
    {
        $sandConn     = Doctrine_Core::getTable('sfGuardUser')->getConnection();
        $readOnlyConn = Doctrine_Manager::connection(
            'mysql://root@localhost/maindb', 'readonly' # readonly is only the conn name, not its state
        );
        $user = Doctrine_Core::getTable('sfGuardUser')
            ->getAllUserDetailsUsingConnection($username, $readOnlyConn);
        Doctrine_Manager::getInstance()->closeConnection($readOnlyConn);
        Doctrine_Manager::getInstance()->setCurrentConnection($sandConn->getName());
        if (   $user instanceof sfGuardUser && $user->getIsActive()
            && $user->checkPassword($password)
        ) {
            $sandboxUser = $user->copy(true);
            $this->_fixCopiedRelations($sandboxUser);
            $sandboxUser->setPasswordHash($user['password']);
            $sandboxUser->save($sandConn);
            return $sandboxUser;
        }
        return false;
    }

    private function _fixCopiedRelations(Doctrine_Record $rec)
    {
        $rel = $rec->getReferences();
        foreach ($rel as $name => $related) {
            if ($name == 'Responsibilities') {
                $coll = new Doctrine_Collection('Client');
                foreach ($related as $client) {
                    $o = Doctrine_Core::getTable('Client')->findOneByCode($client['code']);
                    if ($o instanceof Client == false) {
                        throw new UnexpectedValueException(
                            'Cannot find related object in the sandbox database, therefore not copying sfGuardUser to the sandbox.'
                        );
                    }
                    $coll->add($o);
                }
                $rec[$name] = $coll;
            } else if ($name == 'Groups' || $name == 'Permissions') {
                $coll = new Doctrine_Collection(($name == 'Groups' ? 'sfGuardGroup' : 'sfGuardPermission'));
                foreach ($related as $instance) {
                    $o = Doctrine_Core::getTable(($name == 'Groups' ? 'sfGuardGroup' : 'sfGuardPermission'))->findOneByName($instance['name']);
                    if ($o instanceof Doctrine_Record == false) {
                        throw new UnexpectedValueException(
                            'Cannot find related object in the sandbox database, therefore not copying sfGuardUser to the sandbox.'
                        );
                    }
                    $coll->add($o);
                }
                $rec[$name] = $coll;
            } else if ($name == 'Profile') {
                $this->_fixCopiedRelations($related);
            } else {
                throw new UnexpectedValueException(
                    'Method does not know how to copy this related object to the sandbox, therefore not copying sfGuardUser to the sandbox'
                );
            }
        }
    }

值得注意的是,为了防止_fixCopiedRelations失败,我必须确保主数据库中存在的任何相关对象也存在于沙箱数据库中,但是创建新的此类对象非常有限所以它是在这种情况下并不是真正的问题。

我并不是特别迷恋这个解决方案,但它在这个有限的环境中工作,这已经足够了。