AuditLog.php 8.37 KB
Newer Older
zYne's avatar
zYne committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.com>.
 */
/**
 * Doctrine_AuditLog
 *
 * @package     Doctrine
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @category    Object Relational Mapping
 * @link        www.phpdoctrine.com
 * @since       1.0
 * @version     $Revision$
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 */
zYne's avatar
zYne committed
32
class Doctrine_AuditLog
zYne's avatar
zYne committed
33
{
zYne's avatar
zYne committed
34 35 36 37 38
    protected $_options = array(
                            'className'     => '%CLASS%Version',
                            'deleteTrigger' => '%TABLE%_ddt',
                            'updateTrigger' => '%TABLE%_dut',
                            'versionTable'  => '%TABLE%_dvt',
zYne's avatar
zYne committed
39
                            'versionColumn'    => 'version',
zYne's avatar
zYne committed
40 41 42
                            );
                            
    protected $_table;
zYne's avatar
zYne committed
43 44
    
    protected $_auditTable;
zYne's avatar
zYne committed
45 46 47 48

    public function __construct(Doctrine_Table $table)
    {
    	$this->_table = $table;
zYne's avatar
zYne committed
49

zYne's avatar
zYne committed
50
    }
zYne's avatar
zYne committed
51 52 53 54 55 56 57 58 59
    /**
     * __get
     * an alias for getOption
     *
     * @param string $option
     */
    public function __get($option)
    {
        if (isset($this->options[$option])) {
zYne's avatar
zYne committed
60
            return $this->_options[$option];
zYne's avatar
zYne committed
61 62 63 64 65 66 67 68 69 70
        }
        return null;
    }
    /**
     * __isset
     *
     * @param string $option
     */
    public function __isset($option) 
    {
zYne's avatar
zYne committed
71
        return isset($this->_options[$option]);
zYne's avatar
zYne committed
72 73 74 75 76 77 78 79 80
    }
    /**
     * getOptions
     * returns all options of this table and the associated values
     *
     * @return array    all options and their values
     */
    public function getOptions()
    {
zYne's avatar
zYne committed
81
        return $this->_options;
zYne's avatar
zYne committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    }
    /**
     * setOption
     * sets an option and returns this object in order to
     * allow flexible method chaining
     *
     * @see slef::$_options             for available options
     * @param string $name              the name of the option to set
     * @param mixed $value              the value of the option
     * @return Doctrine_AuditLog        this object
     */
    public function setOption($name, $value)
    {
        if ( ! isset($this->_options[$name])) {
            throw new Doctrine_Exception('Unknown option ' . $name);
        }
zYne's avatar
zYne committed
98
        $this->_options[$name] = $value;
zYne's avatar
zYne committed
99 100 101 102 103 104 105 106 107 108
    }
    /**
     * getOption
     * returns the value of given option
     *
     * @param string $name  the name of the option
     * @return mixed        the value of given option
     */
    public function getOption($name)
    {
zYne's avatar
zYne committed
109 110
        if (isset($this->_options[$name])) {
            return $this->_options[$name];
zYne's avatar
zYne committed
111 112 113
        }
        return null;
    }
zYne's avatar
zYne committed
114
    public function audit()
zYne's avatar
zYne committed
115
    {
zYne's avatar
zYne committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
        $conn = $this->_table->getConnection();

        // begin new transaction
        $conn->beginTransaction();
        try {

            // create the version table and the triggers
            $this->createVersionTable();
            $conn->execute($this->deleteTriggerSql());
            $conn->execute($this->updateTriggerSql());

            // commit structure changes
            $conn->commit();
        } catch(Doctrine_Connection_Exception $e) {
            $conn->rollback();
        }
    }
zYne's avatar
zYne committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146
    public function getVersion(Doctrine_Record $record, $version)
    {
        $className = str_replace('%CLASS%', $this->_table->getComponentName(), $this->_options['className']);
        
        $q = new Doctrine_Query();
        
        $values = array();
        foreach ((array) $this->_table->getIdentifier() as $id) {
            $conditions[] = $className . '.' . $id . ' = ?';
            $values[] = $record->get($id);
        }
        $where = implode(' AND ', $conditions) . ' AND ' . $className . '.' . $this->_options['versionColumn'] . ' = ?';
        
        $values[] = $version;
zYne's avatar
zYne committed
147

zYne's avatar
zYne committed
148 149 150 151
        return $q->from($className)
                 ->where($where)
                 ->execute($values, Doctrine::FETCH_ARRAY);
    }
zYne's avatar
zYne committed
152 153 154 155 156 157 158 159 160 161 162 163
    public function createVersionTable()
    {
        $data = $this->_table->getExportableFormat(false);
        $conn = $this->_table->getConnection();
        $data['tableName'] = str_replace('%TABLE%', $data['tableName'], $this->_options['versionTable']);

        foreach ($data['columns'] as $name => $def) {
            unset($data['columns'][$name]['autoinc']);
            unset($data['columns'][$name]['autoincrement']);
            unset($data['columns'][$name]['sequence']);
            unset($data['columns'][$name]['seq']);
        }
zYne's avatar
zYne committed
164
        $data['columns'][$this->_options['versionColumn']]['primary'] = true;
zYne's avatar
zYne committed
165

zYne's avatar
zYne committed
166 167
        $className  =  str_replace('%CLASS%', $this->_table->getComponentName(), $this->_options['className']);
        $definition = 'class ' . $className
zYne's avatar
zYne committed
168 169 170 171
                    . ' extends Doctrine_Record { '
                    . 'public function setTableDefinition() { '
                    . '$this->hasColumns(' . var_export($data['columns'], true) . ');'
                    . '$this->option(\'tableName\', \'' . $data['tableName'] . '\'); } }';
zYne's avatar
zYne committed
172 173 174 175 176 177

        $this->_table->getRelationParser()->bind($className, array(
                                  'local'   => $this->_table->getIdentifier(),
                                  'foreign' => $this->_table->getIdentifier(),
                                  'type'    => Doctrine_Relation::MANY));

zYne's avatar
zYne committed
178 179

        $this->_table->addListener(new Doctrine_AuditLog_Listener($this));
zYne's avatar
zYne committed
180

zYne's avatar
zYne committed
181 182 183
        eval($definition);

        $data['options']['primary'][] = $this->_options['versionColumn'];
zYne's avatar
zYne committed
184

zYne's avatar
zYne committed
185

zYne's avatar
zYne committed
186
    	$this->_auditTable = $this->_table->getConnection()->getTable($className);
zYne's avatar
zYne committed
187
    }
zYne's avatar
zYne committed
188 189 190 191 192 193
    /**
     * deleteTriggerSql
     *
     * returns the sql needed for the delete trigger creation
     */
    public function deleteTriggerSql()
zYne's avatar
zYne committed
194
    {
zYne's avatar
zYne committed
195 196
    	$conn = $this->_table->getConnection();
    	$columnNames = $this->_table->getColumnNames();
zYne's avatar
zYne committed
197 198
    	$oldColumns  = array_map(array($this, 'formatOld'), $columnNames);
        $sql  = 'CREATE TRIGGER '
zYne's avatar
zYne committed
199
              . $conn->quoteIdentifier($this->_table->getTableName()) . '_ddt' . ' BEFORE DELETE ON '
zYne's avatar
zYne committed
200
              . $conn->quoteIdentifier($this->_table->getTableName())
zYne's avatar
zYne committed
201
              . ' BEGIN'
zYne's avatar
zYne committed
202
              . ' INSERT INTO ' . $this->_table->getTableName() . '_dvt ('
zYne's avatar
zYne committed
203 204 205 206 207 208 209
              . implode(', ', array_map(array($conn, 'quoteIdentifier'), $columnNames))
              . ') VALUES ('
              . implode(', ', array_map(array($conn, 'quoteIdentifier'), $oldColumns))
              . ');'
              . ' END;';
        return $sql;
    }
zYne's avatar
zYne committed
210 211 212 213 214 215
    /**
     * updateTriggerSql
     *
     * returns the sql needed for the update trigger creation
     */
    public function updateTriggerSql()
zYne's avatar
zYne committed
216
    {
zYne's avatar
zYne committed
217 218
    	$conn = $this->_table->getConnection();
    	$columnNames = $this->_table->getColumnNames();
zYne's avatar
zYne committed
219 220
    	$oldColumns  = array_map(array($this, 'formatOld'), $columnNames);
        $sql  = 'CREATE TRIGGER '
zYne's avatar
zYne committed
221 222
              . $conn->quoteIdentifier($this->_table->getTableName()) . '_dut' . ' UPDATE ON '
              . $conn->quoteIdentifier($this->_table->getTableName())
zYne's avatar
zYne committed
223
              . ' BEGIN'
zYne's avatar
zYne committed
224
              . ' INSERT INTO ' . $this->_table->getTableName() . '_dvt ('
zYne's avatar
zYne committed
225 226 227 228 229 230 231 232 233 234
              . implode(', ', array_map(array($conn, 'quoteIdentifier'), $columnNames))
              . ') VALUES ('
              . implode(', ', array_map(array($conn, 'quoteIdentifier'), $oldColumns))
              . ');'
              . ' END;';
        return $sql;
    }
    public function formatOld($column)
    {
        return 'old.' . $column;
zYne's avatar
zYne committed
235 236
    }
}