00001 <?php
00010 class db_row extends object {
00011
00017 protected $changes = array();
00018
00024 protected $new = true;
00025
00031 protected $table;
00032
00038 protected $linked = array();
00039
00045 protected $related = array();
00046
00052 protected $i18nRows = array();
00053
00054 protected function afterInit() {
00055 if (get_class($this) == 'db_row')
00056 $this->cfg->overload('db_row_'.$this->cfg->table->getName());
00057
00058 $this->table = $this->cfg->table;
00059
00060 if (!empty($this->cfg->data)) {
00061 $this->loadData($this->cfg->data);
00062 } else if (!empty($this->cfg->findId)) {
00063 $tmp = $this->getTable()->find($this->cfg->findId);
00064 if ($tmp) {
00065 $this->cfg->data = $tmp->getValues('data');
00066 $this->setNew(false);
00067 }
00068 unset($tmp);
00069 }
00070 }
00071
00077 public function loadData(array $data) {
00078 $this->cfg->data = $data;
00079 if (array_key_exists($this->getTable()->getIdent(), $data) && $data[$this->getTable()->getIdent()])
00080 $this->setNew(false);
00081 else {
00082 $primary = $this->getTable()->getPrimary();
00083 $p = 0;
00084 foreach($primary as $pp) {
00085 if (array_key_exists($pp, $data) && $data[$pp])
00086 $p++;
00087 }
00088 if ($p == count($primary))
00089 $this->setNew(false);
00090 }
00091
00092 $linkedKey = db::getCfg('linked');
00093 if (array_key_exists($linkedKey, $data)) {
00094 $this->setLinked($data[$linkedKey]);
00095 }
00096
00097 $relatedKey = db::getCfg('related');
00098 if (array_key_exists($relatedKey, $data)) {
00099 $this->setRelated($data[$relatedKey]);
00100 }
00101 }
00102
00108 public function reload($force = true) {
00109 if (!$this->isNew() && $this->getId() && ($force || $this->cfg->needReload)) {
00110 $this->loadData($this->getTable()->find($this->getId())->getValues());
00111 $this->cfg->needReload = false;
00112 }
00113 }
00114
00120 public function getDb() {
00121 return $this->cfg->db;
00122 }
00123
00129 public function getTable() {
00130 return $this->table;
00131 }
00132
00139 public function getWhere(array $prm = array()) {
00140 return $this->getDb()->getWhere($prm);
00141 }
00142
00151 public function getForm($showFields = null, array $formParam = array(), $passConfirm = true) {
00152 $form = factory::get('form_db', array_merge($formParam, array(
00153 'table'=>$this->getTable()
00154 )));
00155
00156 foreach($this->getTable()->getField() as $f) {
00157 if ($f['name'] != $this->getTable()->getIdent() && !$f['auto'] &&
00158 (empty($showFields) || in_array($f['name'], $showFields))) {
00159 $f['label'] = $this->getTable()->getLabel($f['name']);
00160 $f['link'] = $this->getTable()->getLinked($f['name']);
00161 $f['value'] = $this->get($f['name']);
00162 $obj = $form->addFromField($f);
00163 if ($passConfirm && $obj instanceof form_password) {
00164 $name = $f['name'];
00165 $f['name'].= 'Confirm';
00166 $f['label'] = $this->getTable()->getLabel($f['name']);
00167 $moreConf = $this->getTable()->getCfg()->getInArray('fields', $f['name']);
00168 if (is_array($moreConf) && array_key_exists('comment', $moreConf)) {
00169 factory::mergeCfg($f, $moreConf);
00170 }
00171 $form->addFromField($f)->addRule('equal', $obj);
00172 $form->addNotValue($f['name']);
00173 if (!$this->isNew()) {
00174
00175 $form->get($name)->getValid()->delRule('required');
00176 $form->get($f['name'])->getValid()->delRule('required');
00177 }
00178 if (!empty($showFields) && !in_array($f['name'], $showFields)) {
00179 $key = array_search($name, $showFields);
00180 array_splice($showFields, $key+1, 0, array($f['name']));
00181 }
00182 }
00183 }
00184 }
00185
00186 foreach($this->getTable()->getRelated() as $t=>$r) {
00187 if (empty($showFields) || in_array($r['tableLink'], $showFields)) {
00188 $r['name'] = $r['tableLink'];
00189 $r['label'] = $this->getTable()->getLabel($r['table']);
00190 $r['valid'] = false;
00191 $form->addFromRelated($r);
00192 }
00193 }
00194
00195 $form->setValues($this->getValues('flat'));
00196
00197 $i18nFields = $this->getTable()->getI18nFields();
00198 $i18nFieldsT = array();
00199
00200 foreach($i18nFields as $f) {
00201 if ((empty($showFields) || in_array(db::getCfg('i18n').$f['name'], $showFields))) {
00202 $i18nFieldsT[] = $f['name'];
00203 $f['label'] = $this->getTable()->getI18nTable()->getLabel($f['name']);
00204 $form->addFromField($f, true);
00205 }
00206 }
00207
00208 $form->finalize();
00209
00210 if ($i18nRows = $this->getI18nRows()) {
00211 $tmp = array();
00212 $primary = $this->getTable()->getI18nTable()->getPrimary();
00213 foreach($i18nRows as $r) {
00214 $lang = $r->get($primary[1]);
00215 foreach($r->getValues() as $k=>$v) {
00216 $tmp[db::getCfg('i18n').'['.$lang.']['.$k.']'] = $v;
00217 }
00218 }
00219 $form->setValues($tmp);
00220 }
00221 $form->setBound(false);
00222 if (is_array($showFields) && !empty($showFields))
00223 $form->reOrder($showFields);
00224
00225 return $form;
00226 }
00227
00233 public function isNew() {
00234 return $this->new;
00235 }
00236
00242 public function setNew($new) {
00243 $this->new = (bool) $new;
00244 }
00245
00251 public function insert() {
00252 $values = array_merge($this->getValues('flat'), $this->getChangesTable());
00253 unset($values[$this->getTable()->getIdent()]);
00254 $id = $this->getTable()->insert($values);
00255 $this->set($this->getTable()->getIdent(), $id);
00256 $this->setNew(false);
00257 $this->saveRelated();
00258 $this->saveI18n();
00259 $this->getTable()->clearCache();
00260 return $id;
00261 }
00262
00268 public function save() {
00269 if ($this->isNew())
00270 return $this->insert();
00271
00272 if (!$this->hasChange())
00273 return true;
00274
00275 if ($changesTable = $this->getChangesTable())
00276 $this->getTable()->update($changesTable, $this->whereClause());
00277
00278 $this->saveRelated();
00279 $this->saveI18n();
00280 $this->getTable()->clearCache();
00281
00282 return true;
00283 }
00284
00290 public function saveRelated() {
00291 if ($this->isNew())
00292 throw new nException('db_row::saveRelated: try to save related for a new row');
00293
00294 $changes = $this->getChangesOther();
00295 foreach($this->getTable()->getRelated() as $r) {
00296 if (array_key_exists($r['tableLink'], $changes)) {
00297 $values = array(
00298 $r['fk1']['name'] => $this->getId()
00299 );
00300 $r['tableObj']->delete($values);
00301 $hasFields = isset($r['fields']) && count($r['fields']);
00302 if (($tmp = $changes[$r['tableLink']]) && is_array($tmp)) {
00303 foreach($tmp as $t) {
00304 $curValues = $values;
00305 if ($hasFields) {
00306 foreach($t as $k=>$v) {
00307 if ($k == db::getCfg('relatedValue'))
00308 $curValues[$r['fk2']['name']] = $v;
00309 else
00310 $curValues[$k] = $v;
00311 }
00312 } else
00313 $curValues = array_merge($values, array($r['fk2']['name'] => $t));
00314 $r['tableObj']->insert($curValues);
00315 }
00316 }
00317 }
00318 }
00319 }
00320
00326 public function saveI18n() {
00327 if ($this->isNew())
00328 throw new nException('db_row::saveI18n: try to save i18n for a new row');
00329
00330 if (!empty($this->i18nRows)) {
00331 list($fkId, $lang) = ($this->getTable()->getI18nTable()->getPrimary());
00332 foreach($this->i18nRows as $r) {
00333 if ($r->isNew())
00334 $r->set($fkId, $this->getId());
00335 $r->save();
00336 }
00337 }
00338 }
00339
00345 public function delete() {
00346 foreach($this->getTable()->getRelated() as $related) {
00347 $related['tableObj']->delete(array(
00348 $related['fk1']['name']=>$this->getId()
00349 ));
00350 }
00351 $nb = $this->getTable()->delete($this->whereClause());
00352 return ($nb > 0);
00353 }
00354
00360 public function getId() {
00361 return $this->get($this->getTable()->getIdent());
00362 }
00363
00367 public function clear() {
00368 $this->changes = array();
00369 }
00370
00376 public function keyExists($key) {
00377 return in_array($key, $this->getTable()->getCols());
00378 }
00379
00387 public function get($key, $mode = 'flat') {
00388 if (db::isI18nName($key))
00389 return $this->getI18n(db::unI18nName($key), $mode);
00390
00391 if ($this->keyExists($key)) {
00392 if ($mode == 'flat' && $this->hasChange($key))
00393 $val = $this->changes[$key];
00394 else
00395 $val = $this->cfg->getInArray('data', $key);
00396 return $this->getTable()->getField($key, 'htmlOut') ? utils::htmlOut($val) : $val;
00397 } else if ($val = $this->cfg->getInArray('data', $key)) {
00398 return $val;
00399 } else if ($this->getTable()->isRelated($key)) {
00400 $key = $this->getTable()->getRelatedTableName($key);
00401 $values = $this->getValues($mode);
00402 return array_key_exists($key, $values) ? $values[$key] : null;
00403 }
00404 return null;
00405 }
00406
00414 public function getI18n($key, $mode='flat', $lang=null) {
00415 return $this->getI18nRow($lang)->get($key, $mode);
00416 }
00417
00424 public function getI18nRow($lang = null) {
00425 if (is_null($lang) || !$lang)
00426 $lang = request::get('lang');
00427 if (!array_key_exists($lang, $this->i18nRows)) {
00428 if ($this->getTable()->getCfg()->i18nGetDefaultLangIfNotExist && $lang != request::getDefaultLang())
00429 return $this->getI18nRow(request::getDefaultLang());
00430 $primary = $this->getTable()->getI18nTable()->getPrimary();
00431 $this->i18nRows[$lang] = $this->getTable()->getI18nTable()->getRow();
00432 $this->i18nRows[$lang]->setValues(array(
00433 $primary[0]=>$this->getId(),
00434 $primary[1]=>$lang
00435 ));
00436 }
00437 return $this->i18nRows[$lang];
00438 }
00439
00445 public function getI18nRows() {
00446 return $this->i18nRows;
00447 }
00448
00455 public function getValues($mode = 'data') {
00456 switch ($mode) {
00457 case 'flat':
00458 case 'flatNoRelated':
00459 $data = array_merge($this->cfg->data, $this->getChanges());
00460 $tmp = $this->getTable()->getCols();
00461
00462 if ($mode == 'flat') {
00463 $linked = $this->getTable()->getLinked();
00464 if (is_array($linked)) {
00465 foreach($linked as $k=>$v) {
00466 $tmp[] = $k;
00467 if (array_key_exists($key = $k.'_'.$v['ident'], $data))
00468 $data[$k] = $data[$k.'_'.$v['ident']];
00469 }
00470 }
00471 if (array_key_exists('related', $data)) {
00472 foreach($this->getTable()->getRelated() as $k=>$v) {
00473 $tmp[] = $k;
00474 $data[$k] = array();
00475 $hasFields = isset($v['fields']) && count($v['fields']);
00476 foreach($data['related'][$v['fk2']['link']['table']] as $vv) {
00477 if ($hasFields) {
00478 $curVal = array(
00479 db::getCfg('relatedValue')=>$vv[$v['fk2']['link']['ident']]
00480 );
00481 foreach($v['fields'] as $kF=>$vF) {
00482 $curVal[$kF] = $vv[$kF];
00483 }
00484 $data[$k][] = $curVal;
00485 } else {
00486 $data[$k][] = $vv[$v['fk2']['link']['ident']];
00487 }
00488 }
00489 }
00490 }
00491 }
00492 return array_intersect_key($data, array_flip($tmp));
00493 break;
00494 case 'flatReal':
00495 case 'flatRealNoRelated':
00496 $data = array_merge($this->cfg->data, $this->getChanges());
00497 $tmp = $this->getTable()->getCols();
00498
00499 if ($mode == 'flatReal' && array_key_exists('related', $data)) {
00500 foreach($this->getTable()->getRelated() as $k=>$v) {
00501 $tmp[] = $k;
00502 $data[$k] = array();
00503 $fields = explode(',', $v['fk2']['link']['fields']);
00504 $i18nFields = explode(',', $v['fk2']['link']['i18nFields']);
00505 array_walk($fields, create_function('&$v', '$v = "'.$v['fk2']['link']['table'].'_".$v;'));
00506 array_walk($i18nFields, create_function('&$v', '$v = "'.$v['fk2']['link']['table'].'_'.db::getCfg('i18n').'".$v;'));
00507 $fields = array_merge($fields, $i18nFields);
00508 foreach($data['related'][$v['fk2']['link']['table']] as $vv) {
00509 $tmp2 = array();
00510 foreach($fields as $f)
00511 if (array_key_exists($f, $vv))
00512 $tmp2[] = $vv[$f];
00513 $data[$k][] = implode($v['fk2']['link']['sep'], $tmp2);
00514 }
00515 $data[$k] = utils::htmlOut($data[$k]);
00516 }
00517 }
00518
00519 return array_intersect_key($data, array_flip($tmp));
00520 break;
00521 case 'data':
00522 default:
00523 return $this->cfg->data;
00524 break;
00525 }
00526 }
00527
00536 public function getInValues($name = null, $mode = 'flatReal') {
00537 $tmp = $this->getValues($mode);
00538 if(array_key_exists($name, $tmp))
00539 return $tmp[$name];
00540 return null;
00541 }
00542
00551 public function set($key, $value, $force = false) {
00552 if ($key == db::getCfg('i18n'))
00553 return $this->setI18n($value, $force);
00554
00555 $field = $this->getTable()->getField($key);
00556 $fct = null;
00557
00558 if (isset($field['comment']) && is_array($field['comment'])) {
00559 foreach($field['comment'] as $k=>$v) {
00560 if (!is_array($v) && strpos($v, 'fct:') === 0) {
00561 $fct = substr($v, 4);
00562 break;
00563 }
00564 }
00565 }
00566 if (!is_null($fct) && function_exists($fct))
00567 $value = $fct($value);
00568 if ($force || $this->get($key) != $value)
00569 $this->changes[$key] = $value;
00570 }
00571
00579 public function setI18n(array $values, $force = false, $lg = null) {
00580 if (!is_null($lg) && $lg) {
00581 if (count($values))
00582 $this->getI18nRow($lg)->setValues($values, $force);
00583 } else {
00584 foreach($values as $lg=>$val)
00585 $this->setI18n($val, $force, $lg);
00586 }
00587 }
00588
00595 public function setValues(array $values, $force = false) {
00596 foreach($values as $k=>$v)
00597 $this->set($k, $v, $force);
00598 }
00599
00606 public function getChanges($name = null) {
00607 if (is_null($name))
00608 return $this->changes;
00609
00610 if ($this->hasChange($name))
00611 return $this->changes[$name];
00612 return null;
00613 }
00614
00620 public function getChangesTable() {
00621 return array_intersect_key($this->getChanges(), $this->getTable()->getField());
00622 }
00623
00629 public function getChangesOther() {
00630 return array_diff_key($this->getChanges(), array_merge($this->getTable()->getField(), array(db::getCfg('i18n')=>true)));
00631 }
00632
00638 public function getChangesI18n() {
00639 $tmp = $this->getChanges();
00640 if (array_key_exists(db::getCfg('i18n'), $tmp))
00641 return $tmp[db::getCfg('i18n')];
00642 return array();
00643 }
00644
00651 public function hasChange($name = null) {
00652 if (is_null($name))
00653 return !empty($this->changes) || $this->hasChange(db::getCfg('i18n'));
00654 else if ($name == db::getCfg('i18n')) {
00655 $hasChange = false;
00656 foreach($this->i18nRows as $r)
00657 $hasChange = $hasChange || $r->hasChange();
00658 return $hasChange;
00659 }
00660 return array_key_exists($name, $this->changes);
00661 }
00662
00670 public function getLinked($field = null, $reload = false) {
00671 if ($this->getTable()->isLinked($field)) {
00672 if (!array_key_exists($field, $this->linked)) {
00673 $data = array();
00674 if ($val = $this->get($field, 'flatReal')) {
00675 $tmp = $this->getTable()->getLinked($field);
00676 $data[$tmp['ident']] = $val;
00677 } else if ($val = $this->get($field, 'flat')) {
00678 $tmp = $this->getTable()->getLinked($field);
00679 $data[$tmp['ident']] = $val;
00680 }
00681 $this->linked[$field] = $this->getTable()->getLinkedTableRow($field, $data);
00682 }
00683 if ($reload && array_key_exists($field, $this->linked) && !is_null($this->linked[$field]))
00684 $this->linked[$field]->reload(false);
00685 return $this->linked[$field];
00686 }
00687 return null;
00688 }
00689
00696 public function setLinked($linked, $field = null) {
00697 if (!is_null($field)) {
00698 if ($this->getTable()->isLinked($field)) {
00699 if ($linked instanceof db_row)
00700 $this->linked[$field] = $linked;
00701 else
00702 $this->linked[$field] = $this->getTable()->getLinkedTableRow($field, $linked);
00703 }
00704 } else {
00705 foreach($linked as $f=>$v)
00706 $this->setLinked($v, $f);
00707 }
00708 }
00709
00716 public function getRelated($name) {
00717 $related = $this->getTable()->getRelated($this->getTable()->getRelatedTableName($name));
00718 $id = $this->getId();
00719 if (!$id)
00720 return array();
00721 return $related['tableObj']->getLinkedTable($related['fk2']['name'])->select(array(
00722 'where'=>$this->getWhere(array(
00723 'clauses'=>factory::get('db_whereClause', array(
00724 'name'=>$related['fk2']['link']['table'].'.'.$related['fk2']['link']['ident'],
00725 'in'=>$this->getDb()->selectQuery(array(
00726 'fields'=>$related['fk2']['name'],
00727 'table'=>$related['tableLink'],
00728 'where'=>$related['fk1']['name'].'='.$id
00729 ))
00730 ))
00731 ))
00732 ));
00733 }
00734
00741 public function setRelated($related, $name = null) {
00742 if (!is_null($name)) {
00743 if ($this->getTable()->getI18nTable() && $name == $this->getTable()->getI18nTable()->getName()) {
00744 $primary = $this->getTable()->getI18nTable()->getPrimary();
00745 foreach($related as $r) {
00746 $this->i18nRows[$r[$primary[1]]] = $this->getTable()->getI18nTable()->getRow(
00747 array_merge($r, array($primary[0]=>$this->getId())));
00748 }
00749 } else {
00750 $name = $this->getTable()->getRelatedTableName($name);
00751 if ($this->getTable()->isRelated($name)) {
00752 if (!array_key_exists($name, $this->related))
00753 $this->related[$name] = array();
00754
00755 if ($related instanceof db_row)
00756 $this->related[$name][] = $related;
00757 else {
00758 foreach($related as $v) {
00759 if ($v instanceof db_row)
00760 $this->related[$name][] = $v;
00761 else
00762 $this->related[$name][] = $this->getTable()->getRelatedTableRow($name, $v);
00763 }
00764 }
00765 }
00766 }
00767 } else {
00768 foreach($related as $t=>$v)
00769 $this->setRelated($v, $t);
00770 }
00771 }
00772
00778 public function whereClause() {
00779 $primary = $this->getTable()->getPrimary();
00780 $values = $this->getValues('flat');
00781 $where = array();
00782 foreach($primary as $p) {
00783 $where[] = $this->getTable()->getRawName().'.'.$p.='="'.$values[$p].'"';
00784 }
00785 return implode(' AND ', $where);
00786 }
00787
00797 public function getAround(array $prm = array()) {
00798 config::initTab($prm, array(
00799 'field'=>null,
00800 'where'=>null,
00801 'asRow'=>false
00802 ));
00803
00804 $field = $prm['field'];
00805 $where = $this->getDb()->makeWhere($prm['where']);
00806 if (empty($where))
00807 $where = 'WHERE 1';
00808
00809 if (!is_null($where))
00810 $where.= ' AND ';
00811 if (is_null($field))
00812 $field = $this->getTable()->getIdent();
00813 $val = $this->get($field);
00814
00815 $query = '(SELECT '.$field.' FROM '.$this->getTable()->getRawName().' '.$where.$field.' < ? ORDER BY '.$field.' DESC LIMIT 1)
00816 UNION
00817 (SELECT '.$field.' FROM '.$this->getTable()->getRawName().' '.$where.$field.' > ? ORDER BY '.$field.' ASC LIMIT 1)';
00818 $vals = $this->getDb()->query($query, array($val, $val))->fetchAll(PDO::FETCH_NUM);
00819 $ret = array(null, null);
00820 if (array_key_exists(1, $vals)) {
00821 $ret[0] = $vals[0][0];
00822 $ret[1] = $vals[1][0];
00823 } else if (array_key_exists(0, $vals)) {
00824 $tmp = $vals[0][0];
00825 if ($tmp > $val)
00826 $ret[1] = $tmp;
00827 else
00828 $ret[0] = $tmp;
00829 }
00830 if ($prm['asRow']) {
00831 if ($ret[0])
00832 $ret[0] = $this->getTable()->find($ret[0]);
00833 if ($ret[1])
00834 $ret[1] = $this->getTable()->find($ret[1]);
00835 }
00836 return $ret;
00837 }
00838
00839 public function __call($name, $prm) {
00840 if (strpos($name, 'get') === 0 || strpos($name, 'set') === 0) {
00841 $tblName = strtolower(substr($name, 3, 1)).substr($name, 4);
00842 $found = false;
00843 $linked = $this->getTable()->getLinked();
00844 if (is_array($linked)) {
00845 foreach(array_keys($linked) as $v) {
00846 if (strpos($v, $tblName.'_') === 0) {
00847 $name = $v;
00848 $found = true;
00849 break;
00850 }
00851 }
00852 }
00853 if (!$found) {
00854 $tblName = $this->getTable()->getName().'_'.substr($tblName, 0, -1);
00855 $related = $this->getTable()->getRelated();
00856 if (is_array($related)) {
00857 foreach(array_keys($related) as $v) {
00858 if ($v == $tblName) {
00859 $name = $v;
00860 $found = true;
00861 break;
00862 }
00863 }
00864 }
00865 }
00866 }
00867 if ($this->getTable()->isLinked($name)) {
00868 if (empty($prm) || is_bool($prm[0])) {
00869 return $this->getLinked($name, isset($prm[0]) ? $prm[0] : false);
00870 } else {
00871 return $this->setLinked($prm[0], $name);
00872 }
00873 } else if ($this->getTable()->isRelated($name)) {
00874 if (empty($prm)) {
00875 return $this->getRelated($name);
00876 } else {
00877 return $this->setRelated($prm[0], $name);
00878 }
00879 }
00880 }
00881
00882 public function __get($name) {
00883 return $this->get($name);
00884 }
00885
00886 public function __set($name, $val) {
00887 return $this->set($name, $val);
00888 }
00889
00890 public function __toString() {
00891 return $this->getTable()->getName().'-'.$this->getId();
00892 }
00893
00894 }