1: <?php
2: /**
3: * QWatcher is a helper class that allows controls to watch a database table
4: * and automatically update when changes are detected. It works together with the codegened
5: * model classes, the controls, and the QForm class to draw when needed.
6: *
7: * It relies on the presence of a SQL database table in the system. Define the following
8: * in your config file to tell it which tables to use:
9: * __WATCHER_DB_INDEX__ - The database index to look for the table
10: * __WATCHER_TABLE_NAME__ - The name of the table.
11: *
12: * To create the database, use the following SQL:
13: * CREATE TABLE IF NOT EXISTS qc_watchers (
14: * table_key varchar(200) NOT NULL,
15: * ts varchar(40) NOT NULL,
16: * PRIMARY KEY (table_key)
17: * );
18:
19: *
20: * Static functions handle the database updating, while member variables store the current state
21: * of a control's watched tables.
22: */
23:
24: if (!defined('__WATCHER_DB_INDEX__')) define ('__WATCHER_DB_INDEX__', 1);
25: if (!defined('__WATCHER_TABLE_NAME__')) define ('__WATCHER_TABLE_NAME__', 'qc_watchers');
26:
27: include ('QWatcherBase.class.php');
28:
29: class QWatcherDB extends QWatcherBase {
30:
31: /**
32: * The table name which will keep info about changed tables. It must have the following columns:
33: * 1. table_key: varchar(largest key size)
34: * 2. time: varchar(30)
35: *
36: */
37: public static $intDbIndex = __WATCHER_DB_INDEX__;
38:
39: public static $strTableName = __WATCHER_TABLE_NAME__;
40:
41: /**
42: * @var string[] Caches results of database lookups. Will not be saved with the formstate.
43: */
44: private static $strKeyCaches = null;
45:
46: /**
47: * Override
48: */
49: public function MakeCurrent() {
50: $objDatabase = QApplication::$Database[__WATCHER_DB_INDEX__];
51: $strIn = implode (',', $objDatabase->EscapeValues(array_keys($this->strWatchedKeys)));
52: $strSQL = sprintf ("SELECT * FROM %s WHERE %s in (%s)",
53: $objDatabase->EscapeIdentifier(__WATCHER_TABLE_NAME__),
54: $objDatabase->EscapeIdentifier("table_key"),
55: $strIn);
56:
57: $objDbResult = $objDatabase->Query($strSQL);
58:
59: while ($strRow = $objDbResult->FetchRow()) {
60: $this->strWatchedKeys[$strRow[0]] = $strRow[1];
61: }
62: }
63:
64: /**
65: * Returns true if the watcher is up to date, and false if something has
66: * changed. Caches the results so it only hits the database minimally for each
67: * read.
68: *
69: * @return bool
70: */
71: public function IsCurrent() {
72: // check cache
73: $ret = true;
74:
75: foreach ($this->strWatchedKeys as $key=>$ts) {
76: if (!isset (self::$strKeyCaches[$key])) {
77: $ret = false;
78: break;
79: }
80: if (self::$strKeyCaches[$key] !== $ts) {
81: return false;
82: }
83: }
84: if ($ret) return true; // cache had everything we were looking for
85:
86: // cache did not have what we were looking for, so check database
87: $objDatabase = QApplication::$Database[__WATCHER_DB_INDEX__];
88: $strIn = implode (',', $objDatabase->EscapeValues(array_keys($this->strWatchedKeys)));
89: $strSQL = sprintf ("SELECT * FROM %s WHERE %s in (%s)",
90: $objDatabase->EscapeIdentifier(__WATCHER_TABLE_NAME__),
91: $objDatabase->EscapeIdentifier("table_key"),
92: $strIn);
93:
94: $objDbResult = $objDatabase->Query($strSQL);
95:
96: // fill cache and check result
97: while ($strRow = $objDbResult->FetchRow()) {
98: self::$strKeyCaches[$strRow[0]] = $strRow[1];
99: if ($ret && $this->strWatchedKeys[$strRow[0]] !== $strRow[1]) {
100: $ret = false;
101: }
102: }
103:
104: return $ret;
105: }
106:
107: /**
108: * Override
109: *
110: * @param string $strTableName
111: * @throws QCallerException
112: */
113: static public function MarkTableModified ($strDbName, $strTableName) {
114: $key = static::GetKey ($strDbName, $strTableName);
115: $objDatabase = QApplication::$Database[__WATCHER_DB_INDEX__];
116: $time = microtime();
117:
118: $objDatabase->InsertOrUpdate(__WATCHER_TABLE_NAME__,
119: array ('table_key'=>$key,
120: 'ts'=>$time));
121: $objDatabase->InsertOrUpdate(__WATCHER_TABLE_NAME__,
122: array ('table_key'=>static::GetKey ('', static::$strAppKey),
123: 'ts'=>$time));
124:
125: }
126: }
127: