* @license PHP License * @package WB * @subpackage db */ /** * Database relation table * * Generic utility datasource to load, delete and store 1:n foreign key relations. * * @version 1.2.1 * @package WB * @subpackage db */ class WBDatasource_RelationTable extends WBStdClass { /** * Table * @var WBDatasource_Table */ private $table; /** * list of known relations * @var array */ private $config = array(); /** * key of primary table * @var string */ private $primary; /** * list of current relations * @var array */ private $relations = array(); /** * Inject WBDatasource_Table * * Optionally inject table object - this prevents from creating a new one * * @param WBDatasource_Table $table */ public function setTable($table) { $this->table = $table; } /** * Set relation config for table * * Tell which table needs foreign relations and which. * * Usually the configuration comes from form definitions like: * * * * userdownloaddir
* vfsdir *
* * userorganisation
* organisation * orgkey *
* * newslettersubscribertopic
* newslettertopic * * somedata * goes * *
*
* * This XML boils down to an array: * $config => array( * 'downloaddir' => array( * 'table' => 'article', * ), * 'downloaddir' => array( * 'table' => 'userdownloaddir', * 'foreigntable' => 'vfsdir' * ), * 'organisation' => array( * 'table' => 'userorganisation', * 'foreigntable' => 'organisation' * 'foreignprimarymap' => 'orgkey' * ), * 'tagrelation' => array( * 'table' => 'tagreference', * 'foreigntable' => 'tag', * 'namespace' => 'somestring' * ), * 'newslettertopiclist' => array( * 'table' => 'newslettersubscribertopic', * 'foreigntable' => 'newslettertopic', * 'fixed' => array( * 'somekey' => 'somedata', * 'anything' => 'goes' * ) * ), * ); * * Fixed data will be added automatically to on add. Also it will be used in clause on delete * * @param string $table * @param array $config */ public function setConfig($table, $config) { $this->startTable(); $this->primary = $this->table->getIdentifier($table); if (!is_array($this->primary)) { $this->primary = array($this->primary); } // primary key mapping in relation table $this->config = $config; foreach ($this->config as $t => &$rel) { if (!isset($rel['foreigntable'])) { $rel['foreigntable'] = $rel['table']; } if (!isset($rel['foreignprimarymap']) || empty($rel['foreignprimarymap'])) { $rel['foreignprimarymap'] = $this->table->getIdentifier($rel['foreigntable']); } } } /** * Get Configuration for Key * * @return array */ public function getConfig4Key($key) { if (!empty($this->config[$key])) { return $this->config[$key]; } return array(); } /** * Set Configuration for Key * * @pram string key * @param array configuration */ public function setConfig4Key($key, $config) { $this->config[$key] = $config; } /** * Inject foreign keys * * Load foreign keys from table and inject into data. * Also return native relation data * * @param array $data * @return array */ public function inject(&$data) { if (empty($this->primary)) { throw new WBException_Call('Call setConfig() first!', 1, __CLASS__); } $relData = array(); foreach ($this->config as $k => $rel) { $clause = array(); // use primary key if (empty($rel['namespace'])) { foreach ($this->primary as $p) { $clause[] = array( 'field' => $p, 'value' => $data[$p] ); } } // namespace settings else { $p = $this->primary[count($this->primary) - 1]; $clause[] = array( 'field' => 'xid', 'value' => $data[$p] ); if ('__empty' == $rel['namespace']) { $rel['namespace'] = ''; } $clause[] = array( 'field' => 'namespace', 'value' => trim($rel['namespace']) ); } // add fixed data to clause if (!empty($rel['fixed']) && is_array($rel['fixed'])) { foreach ($rel['fixed'] as $p => $q) { $clause[] = array( 'field' => $p, 'value' => $q ); } } $key = $rel['foreignprimarymap']; if (!is_array($key)) { $key = array($key); } $key = array_pop($key); $list = $this->table->get($rel['table'], null, null, $clause); $relData[$k] = $list; $data[$k] = array(); foreach ($list as $l) { $data[$k][] = $l[$key]; } } return $relData; } /** * Extract foreign keys from data * * Find foreign key values in data array and remove items * * @param array $data */ public function extract(&$data) { if (empty($this->primary)) { throw new WBException_Call('Call setConfig() first!', 1, __CLASS__); } $this->relations = array(); foreach ($this->config as $k => $rel) { if (!isset($data[$k])) { continue; } $this->relations[$k] = array(); if (!is_array($data[$k])) { $data[$k] = trim($data[$k]); if (empty($data[$k])) { $data[$k] = array(); } else { $data[$k] = explode(',', $data[$k]); } } $this->relations[$k] = $data[$k]; unset($data[$k]); } } /** * Remove foreign keys * * Wipe out relation * * @param string $id */ public function delete($id, $extracted = false) { if ('__new' == $id) { return; } if (empty($this->primary)) { throw new WBException_Call('Call setConfig() first!', 1, __CLASS__); } $id = explode('-', $id); foreach ($this->config as $k => $rel) { if ($extracted && !isset($this->relations[$k])) { continue; } $primary = $this->primary; $tmpId = $id; $clause = array(); if (!empty($rel['namespace'])) { $primary = array('xid'); if ('__empty' == $rel['namespace']) { $rel['namespace'] = ''; } $clause[] = array( 'field' => 'namespace', 'value' => trim($rel['namespace']) ); } for ($i = count($primary) - 1; $i >= 0; --$i) { $tmp = array_pop($tmpId); if (empty($tmpId)) { $tmpId = array(0); } $clause[] = array( 'field' => $primary[$i], 'value' => $tmp ); } // add fixed data to clause if (!empty($rel['fixed']) && is_array($rel['fixed'])) { foreach ($rel['fixed'] as $k => $v) { $clause[] = array( 'field' => $k, 'value' => $v ); } } if ($rel['table'] == $rel['foreigntable']) { $primary = array_pop($primary); $save = array( $primary => 0 ); $this->table->save($rel['table'], null, $save, $clause); } else { $this->table->delete($rel['table'], null, null, $clause); } } } /** * Add foreign keys * * Save foreign keys to relation table. Please note, that the foreign keys need * to be extracted from data first. * * @see extract() * @param string $id */ public function add($id) { if (empty($this->primary)) { throw new WBException_Call('Call setConfig() first!', 1, __CLASS__); } $id = explode('-', $id); foreach ($this->config as $k => $rel) { if (!isset($this->relations[$k]) || empty($this->relations[$k])) { continue; } $key = $rel['foreignprimarymap']; if (!is_array($key)) { $key = array($key); } $key = array_pop($key); if ($rel['table'] == $rel['foreigntable']) { // save key in payload table $clause = array(); $clause[] = array( 'field' => $key, 'relation' => 'in', 'value' => $this->relations[$k] ); $save = array(); $idTmp = $id; for ($i = count($this->primary) - 1; $i >= 0; --$i) { $tmp = array_pop($idTmp); if (empty($idTmp)) { $idTmp = array(0); } $save[$this->primary[$i]] = $tmp; } // add fixed data if (!empty($rel['fixed']) && is_array($rel['fixed'])) { $save = array_merge($save, $rel['fixed']); } $this->table->save($rel['table'], null, $save, $clause); } else { // save in relation table $save = array(); $p = $this->primary; if (!empty($rel['namespace'])) { $p = array('xid'); } $s = array( $key => '' ); $idTmp = $id; for ($i = count($p) - 1; $i >= 0; --$i) { $tmp = array_pop($idTmp); if (empty($idTmp)) { $idTmp = array(0); } $s[$p[$i]] = $tmp; } if (!empty($rel['namespace'])) { if ('__empty' == $rel['namespace']) { $rel['namespace'] = ''; } $s['namespace'] = $rel['namespace']; } foreach ($this->relations[$k] as $v) { $s[$key] = $v; // add fixed data if (!empty($rel['fixed']) && is_array($rel['fixed'])) { $s = array_merge($s, $rel['fixed']); } $save[] = $s; } $this->table->save($rel['table'], '__new', $save); } } } /** * Start table * * If not injected, instantiate WBDatasource_Table */ private function startTable() { if ($this->table) { return; } $this->table = WBClass::create('WBDatasource_Table'); } }