存储自由格式数据的最佳方法是什么?

时间:2011-04-03 01:46:21

标签: php mysql zend-framework database-design

我正在使用Zend Framework和MySQL。我的应用程序的用户将能够存储任意数量的数据,结构如表。问题是,我不会事先知道“列”。因此,我无法创建现成的表或现成的Zend Framework模型。

在以前版本的应用程序中,自动从数据生成表模式。这很好用。这次的问题是我不知道如何创建Zend框架模型来表示任意数据源。有谁知道我的案例最好的策略是什么?不,重新考虑我的申请要求不是一个选择:)。

顺便说一句:在应用程序的更旧版本中,数据存储为JSON。这种方法很慢。

2 个答案:

答案 0 :(得分:5)

如果您不能使用NoSQL,请查看实体属性值(EAV)模型。它通过一种元数据模型允许灵活的属性。

如果您拥有大量数据,它可能无法正常运行,并且维护与完全关系模式相比可能会有点痛苦。我不推荐它作为第一个选项,但如果你真的不能预先了解架构,那么你可以采用一种方式。

http://en.wikipedia.org/wiki/Entity-attribute-value_model

答案 1 :(得分:0)

自从我提供答案后,我感到很受鼓舞......我掀起了一个小小的课程,为你做这个......

kick_ass_data_t

模式

CREATE TABLE `kick_ass_data_t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `key_text` varchar(1024) NOT NULL,
  `value_text` mediumtext,
  `create_date` datetime NOT NULL,
  `lmod_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

<?php
/**
 * Property Bag
 */

class KickAssProps
{
    /**
     * gets a property from a database bag.
     * If $remove is true, item is deleted after retrieved.
     * @param string $key
     * @param mixed $defaultValue
     * @param bool $remove
     * @return mixed
     */
    public function getProperty( $key, $defaultValue = null, $remove = false )
    {
        $_result = $defaultValue;

        //  Lookup key
        if ( null === ( $_model = $this->_loadProperty( $key ) ) )
        {
            $_model = new KickAssData();
            $_model->key_text = $key;
            $_model->value_text = self::_serialize( $_result );
            $_model->create_date = date( 'Y-m-d H:i:s' );

            if ( ! $_model->save() )
            {
                //  bitch alot...
            }
        }
        else
            $_result = self::_unserialize( $_model->value_text );

        if ( $remove )
        {
            if ( ! $_model->delete() )
            {
                //  bitch alot...
                }
        }

        return $_result;
    }

    /**
     * sets a property in a database bag.
     * @param string $key
     * @param mixed $value
     * @return \KickAssProps $this
     */
    public function setProperty( $key, $value = null )
    {
        //  Lookup key
        if ( null === ( $_model = $this->_loadProperty( $key ) ) )
        {
            $_model = new KickAssData();
            $_model->key_text = $key;
            $_model->create_date = date( 'Y-m-d H:i:s' );
        }

        $_model->value_text = self::_serialize( $value );

        if ( ! $_model->save() )
        {
            //  bitch alot...
        }

        //  Allow chaining...
        return $this;
    }

    /**
     * Retrieves a property from the database bag
     * @param string $key
     * @return KickAssData
     */
    protected function _loadProperty( $key )
    {
        //  Example sql
        //  $_sql = 'select * from kick_ass_data_t where key_text = :key_text';

        //  read row from database however you want... this is how I'd do it in Yii (http://www.yiiframework.com)
        $_model = KickAssData::model()->find(
            'key_text = :key_text',
            array(
                 ':key_text' => $key
            )
        );

        return $_model;
    }

    /**
     * Serializer that can handle SimpleXmlElement objects
     * @param mixed $value
     * @return mixed
     */
    protected static function _serialize( $value )
    {
        try
        {
            if ( $value instanceof SimpleXMLElement )
                return $value->asXML();

            if ( is_object( $value ) )
                return serialize( $value );
        }
        catch ( Exception $_ex )
        {
        }

        return $value;
    }

    /**
     * Unserializer that can handle SimpleXmlElement objects
     * @param mixed $value
     * @return mixed
     */
    protected static function _unserialize( $value )
    {
        try
        {
                if ( self::_isSerialized( $value ) )
            {
                if ( $value instanceof SimpleXMLElement )
                    return simplexml_load_string( $value );

                return @unserialize( $value );
            }
        }
        catch ( Exception $_ex )
        {
        }

        return $value;
    }

    /**
     * Tests if a value needs unserialization by unserializing the value then 
     * re-serializing. If both are successful then it's cool. I know this is 
     * slower but it guarantees data integrity in my database.
     * @param mixed $value
     * @return boolean
     */
    protected static function _isSerialized( $value )
    {
        return !( false === @unserialize( $value ) && $value != @serialize( false ) );
    }
}