* @license PHP License * @package WB * @subpackage db */ WBClass::load('WBDatasource_Callback' , 'patI18n'); /** * Store and access key value pairs * * Use database to store key value pairs in a normalized form. * Microcache is used to shortcut DB queries for repetitive loading * * @version 1.0.0 * @package WB * @subpackage db */ class WBDatasource_KeyValuePair extends WBStdClass implements WBDatasource_Callback { /** * Table name: key */ const TABLE_KEY = 'keyvaluepairkey'; /** * Table name: value */ const TABLE_VALUE = 'keyvaluepairvalue'; /** * Table name: valueid */ const TABLE_RELID = 'keyvaluepairkeyvalueid'; /** * Table name: key value relation */ const TABLE_REL = 'keyvaluepairkeyvalue'; /** * datasource * @var WBDatasource_Table */ private $table; /** * microcache * @var WBDatasource_TableMicroCache */ private $mc; /** * Key domain * @var string */ private $domain = ''; /** * Use translation * * @var bool */ private $doTranslation = true; /** * Constructor * * @param array $parameter */ public function __construct($parameter = array()) { $this->table = WBClass::create('WBDatasource_Table', $parameter); $parameter['name'] = $this->table->getDbName(); //$this->mc = WBClass::create('WBDatasource_TableMicroCache', $parameter); //$this->mc->setTable(self::TABLE_REL); } /** * Switch on/off translation feature * * @param bool $switch */ public function switchTranslation($switch = false) { $this->doTranslation = $switch; $this->table->switchTranslation($switch); } /** * Set domain * * Optionally define domain for keys. Using domains, allows you store keys separately. * The default text domain ist empty. * * @param string $domain */ public function setDomain($domain = '') { $this->domain = $domain; } /** * Fetch key values pairs by id * * @param string $id * @param bool $asList * @return array */ public function get($id, $asList = false) { if (empty($id)) { return array(); } $list = $this->getListFromDb($id); if ($asList) { return $list; } $data = array(); foreach ($list as $l) { $data[$l['key']] = $l['value']; } return $data; } /** * Fetch key values pairs from database * * @param array $id list of ids * @return array */ private function getListFromDb($id) { if (!is_array($id)) { $id = array($id); } /* * Build query SELECT k.word AS key, v.word AS value, kv.keyvalueid AS id FROM unittestkeyvaluepairkeyvalue kv JOIN unittestkeyvaluepairkey k USING (keyid) JOIN unittestkeyvaluepairvalue v USING (valueid) WHERE kv.keyvalueid IN ($id) */ // sort by keys as defined in config $tInfo = $this->table->getTableInfo(self::TABLE_KEY); $order = array( array( 'table' => self::TABLE_KEY, 'field' => 'word', 'asc' => 1 ) ); if (isset($tInfo['order']) && is_array($tInfo['order'])) { $order = array(); foreach ($tInfo['order'] as $o) { $o['table'] = self::TABLE_KEY; $order[] = $o; } } $clause = array(); $clause[] = array( 'field' => $this->table->getIdentifier(self::TABLE_RELID), 'relation' => 'in', 'value' => $id ); $options = array(); $options['order'] = $order; $options['join'] = array(); $options['join'][] = array( 'table' => self::TABLE_KEY, 'using' => $this->table->getIdentifier(self::TABLE_KEY) ); $options['join'][] = array( 'table' => self::TABLE_VALUE, 'using' => $this->table->getIdentifier(self::TABLE_VALUE) ); $options['column'] = array(); $options['column'][]= array( 'table' => self::TABLE_KEY, 'field' => 'word', 'as' => 'key' ); $options['column'][]= array( 'table' => self::TABLE_VALUE, 'field' => 'word', 'as' => 'value' ); $options['column'][]= array( 'field' => $this->table->getIdentifier(self::TABLE_RELID), 'as' => 'id' ); $options['column'][]= array( 'field' => $this->table->getIdentifier(self::TABLE_KEY), 'as' => 'keyid' ); if ($this->doTranslation) { $options['callback'] = $this; } $list = $this->table->get(self::TABLE_REL, null, null, $clause, $options); //$this->mc->add($id, $list); return $list; } /** * Save list of key value pairs * * Save new list or add list * * @param array $data associative array (the actual key value pairs) * @param string $id * @return string */ public function set($data = array(), $id = '__new') { if (!is_array($data)) { WBClass::load('WBException_Call'); throw new WBException_Call('Invalid call, data parameter must be an array'); } if (isset($data[0])) { WBClass::load('WBException_Call'); throw new WBException_Call('Invalid call, data must be an associative array'); } $kPrimary = $this->table->getIdentifier(self::TABLE_KEY); $vPrimary = $this->table->getIdentifier(self::TABLE_VALUE); $kvPrimary = $this->table->getIdentifier(self::TABLE_RELID); // use existing id or add new one if ('__new' == $id) { $id = $this->table->save(self::TABLE_RELID, '__new', array('created' => gmdate('Y-m-d H:i:s'))); } else { $clause = array(); $clause[] = array( 'field' => $kvPrimary, 'value' => $id ); $this->table->delete(self::TABLE_REL, null, null, $clause); } if (empty($data)) { //$this->mc->add($id, array()); return $id; } $save = array(); $cache = array(); foreach ($data as $key => $value) { $s = array( $kvPrimary => $id ); $s[$kPrimary] = $this->findOrAddWord(self::TABLE_KEY, $key); $s[$vPrimary] = $this->findOrAddWord(self::TABLE_VALUE, $value); $save[] = $s; $cache[] = array( 'key' => $key, 'value' => $value ); } $this->table->save(self::TABLE_REL, '__new', $save); //$this->mc->add($id, $cache); return $id; } /** * Remove key value pair list * * Delete rows from TABLE_REL and TABLE_RELID * * @param string $id */ public function delete($id) { $clause = array(); $clause[] = array( 'field' => $this->table->getIdentifier(self::TABLE_RELID), 'value' => $id ); $this->table->delete(self::TABLE_REL, null, null, $clause); $this->table->delete(self::TABLE_RELID, $id); //$this->mc->add($id, array()); } /** * Get all known keys * * @param array $ids limit keys on ids * @return array */ public function getKeys($ids = array()) { $clause = array(); $clause[] = array( 'field' => 'domain', 'value' => $this->domain ); if (!empty($ids) && is_array($ids)) { $clause[] = array( 'field' => $this->table->getIdentifier(self::TABLE_KEY), 'relation' => 'in', 'value' => $ids ); } $options = array( 'column' => 'word' ); return $this->table->getColumn(self::TABLE_KEY, null, $clause, $options); } /** * Find word in table * * Look up word in table. If it is not there, yet, add it. * Return word's id of found or added record * * @param string $table * @param string $word * @return string $id */ private function findOrAddWord($table, $word) { $word = trim($word); // Look up for word $clause = array(); $clause[] = array( 'field' => 'word', 'value' => $word, ); if (self::TABLE_VALUE == $table) { $clause[0]['cast'] = 'char'; } if (self::TABLE_KEY == $table) { $clause[] = array( 'field' => 'domain', 'value' => $this->domain ); } $old = $this->table->getIds($table, null, $clause); if (count($old)) { return $old[0]; } // add word if it was not found $save = array( 'word' => $word ); if (self::TABLE_KEY == $table) { $save['domain'] = $this->domain; } return $this->table->save($table, '__new', $save); } /** * callback when receive whole entry * * @param string $src name of source * @param string $key * @param array $data loaded data * @return bool true on success */ public function onDatasourceGet($src, $key, &$data) { if (self::TABLE_REL != $src) { return true; } $data['value'] = patI18n::gettext($data['value']); $data['key'] = patI18n::gettext($data['key']); return true; } }