PageRenderTime 62ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/relations/class.atkonetoonerelation.inc

https://github.com/ibuildingsnl/ATK
PHP | 1296 lines | 668 code | 99 blank | 529 comment | 197 complexity | 6797ab6407eb5551e3fcc9b546d29cb8 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, LGPL-3.0
  1. <?php
  2. /**
  3. * This file is part of the Achievo ATK distribution.
  4. * Detailed copyright and licensing information can be found
  5. * in the doc/COPYRIGHT and doc/LICENSE files which should be
  6. * included in the distribution.
  7. *
  8. * @package atk
  9. * @subpackage relations
  10. *
  11. * @copyright (c)2000-2004 Ivo Jansch
  12. * @license http://www.achievo.org/atk/licensing ATK Open Source License
  13. *
  14. * @version $Revision: 6320 $
  15. * $Id$
  16. */
  17. /**
  18. * flags specific for atkOneToOneRelation
  19. */
  20. /**
  21. * Override the default no add flag
  22. */
  23. define("AF_ONETOONE_ADD", AF_SPECIFIC_1);
  24. /**
  25. * Enable error notifications / triggers
  26. */
  27. define("AF_ONETOONE_ERROR", AF_SPECIFIC_2);
  28. /**
  29. * Invisibly integrate a onetoonerelation as if the fields where part of the current node.
  30. * If the relation is integrated, no divider is drawn, and the section heading is suppressed.
  31. * (Integration does not affect the way data is stored or manipulated, only how it is displayed.)
  32. */
  33. define("AF_ONETOONE_INTEGRATE", AF_SPECIFIC_3);
  34. /**
  35. * Use lazy loading instead of query addition.
  36. */
  37. define("AF_ONETOONE_LAZY", AF_SPECIFIC_4);
  38. /**
  39. * Respects tab/sections that have been assigned to this attribute instead of using the
  40. * tabs assigned for the attributes in the destination node. This flag is only useful in
  41. * integration mode.
  42. */
  43. define("AF_ONETOONE_RESPECT_TABS", AF_SPECIFIC_5);
  44. /**
  45. * @internal Include the base class.
  46. */
  47. userelation("atkrelation");
  48. /**
  49. * Implementation of one-to-one relationships.
  50. *
  51. * An atkOneToOneRelation defines a relation between two tables where there
  52. * is one record in the first table that belongs to one record in the
  53. * second table.
  54. *
  55. * When editing a one-to-one relation, the form for the destination record
  56. * is embedded in the form of the master record. When using the flag
  57. * AF_ONETOONE_INTEGRATE, this is done transparantly so the user does not
  58. * even notice that the data he's editing comes from 2 separate tables.
  59. *
  60. * @author Ivo Jansch <ivo@achievo.org>
  61. * @package atk
  62. * @subpackage relations
  63. *
  64. */
  65. class atkOneToOneRelation extends atkRelation
  66. {
  67. /**
  68. * The name of the referential key attribute in the target node.
  69. * @access private
  70. * @var String
  71. */
  72. var $m_refKey="";
  73. /**
  74. * Default Constructor
  75. *
  76. * The atkOneToOneRelation supports two configurations:
  77. * - Master mode: The current node is considered the master, and the
  78. * referential key pointing to the master record is in the
  79. * destination node.
  80. * - Slave mode: The current node is considered the child, and the
  81. * referential key pointing to the master record is in the
  82. * current node.
  83. * The mode to use is detected automatically based on the value of the
  84. * $refKey parameter.
  85. *
  86. * <b>Example:</b>
  87. * <code>
  88. * $this->add(new atkOneToOneRelation("child", "mymod.childnode", "parent_id"));
  89. * </code>
  90. *
  91. * @param String $name The unique name of the attribute. In slave mode,
  92. * this corresponds to the foreign key field in the
  93. * database table. (The name is also used as the section
  94. * heading.)
  95. * @param String $destination the destination node (in module.nodename
  96. * notation)
  97. * @param String $refKey In master mode, this specifies the foreign key
  98. * field from the destination node that points to
  99. * the master record. In slave mode, this parameter
  100. * should be empty.
  101. * @param int $flags Attribute flags that influence this attributes'
  102. * behavior.
  103. */
  104. function atkOneToOneRelation($name, $destination, $refKey="", $flags=0)
  105. {
  106. if ($flags & AF_ONETOONE_ADD != AF_ONETOONE_ADD) $flags |= AF_NO_ADD;
  107. $this->atkRelation($name, $destination, $flags|AF_ONETOONE_LAZY);
  108. $this->m_refKey = $refKey;
  109. }
  110. /**
  111. * Returns a displayable string for this value, to be used in HTML pages.
  112. *
  113. * The atkOneToOneRelation displays all values from the destination
  114. * records in "view" mode. In "list" mode, the record descriptor of the
  115. * target record is displayed.
  116. *
  117. * @param array $record The record that holds the value for this attribute
  118. * @param String $mode The display mode ("view" for viewpages, or "list"
  119. * for displaying in recordlists)
  120. * @return String HTML String
  121. */
  122. function display($record, $mode="list")
  123. {
  124. if ($mode == 'view')
  125. {
  126. return null;
  127. }
  128. $myrecord = $record[$this->fieldName()];
  129. if ($this->createDestination() && is_array($myrecord))
  130. {
  131. $result = $this->m_destInstance->descriptor($myrecord);
  132. }
  133. else
  134. {
  135. $result = $this->text('none');
  136. }
  137. return $result;
  138. }
  139. /**
  140. * Returns a piece of html code that can be used in a form to edit this
  141. * attribute's value.
  142. *
  143. * Because of the AF_INTEGRATE feature, the edit() method has a void
  144. * implementation. The actual edit code is handled by addToEditArray().
  145. */
  146. function edit()
  147. {
  148. }
  149. /**
  150. * Set the initial values of this attribute
  151. *
  152. * @return array Array with initial values
  153. */
  154. function initialValue()
  155. {
  156. if ($this->m_initialValue!==null) return parent::initialValue();
  157. if ($this->createDestination())
  158. {
  159. return $this->m_destInstance->initial_values();
  160. }
  161. return null;
  162. }
  163. /**
  164. * Adds this attribute to database queries.
  165. *
  166. * Database queries (select, insert and update) are passed to this method
  167. * so the attribute can 'hook' itself into the query.
  168. *
  169. * Framework method. It should not be necessary to call this method
  170. * directly. This implementation performs a join to retrieve the
  171. * target records' data, unless AF_ONETOONE_LAZY is set, in which case
  172. * loading is delayed and performed later using the load() method.
  173. * For update and insert queries, this method does nothing. These are
  174. * handled by the store() method.
  175. *
  176. * @param atkQuery $query The SQL query object
  177. * @param String $tablename The name of the table of this attribute
  178. * @param String $fieldaliasprefix Prefix to use in front of the alias
  179. * in the query.
  180. * @param Array $rec The record that contains the value of this attribute.
  181. * @param int $level Recursion level if relations point to eachother, an
  182. * endless loop could occur if they keep loading
  183. * eachothers data. The $level is used to detect this
  184. * loop. If overriden in a derived class, any subcall to
  185. * an addToQuery method should pass the $level+1.
  186. * @param String $mode Indicates what kind of query is being processing:
  187. * This can be any action performed on a node (edit,
  188. * add, etc) Mind you that "add" and "update" are the
  189. * actions that store something in the database,
  190. * whereas the rest are probably select queries.
  191. */
  192. function addToQuery(&$query, $tablename="", $fieldaliasprefix="", $rec="", $level=0, $mode="")
  193. {
  194. if ($this->createDestination())
  195. {
  196. if ($mode!= "update" && $mode!="add")
  197. {
  198. if ($this->hasFlag(AF_ONETOONE_LAZY))
  199. {
  200. if ($this->m_refKey=="")
  201. {
  202. return parent::addToQuery($query, $tablename, $fieldaliasprefix, $rec, $level, $mode);
  203. }
  204. }
  205. if ($tablename!="") $tablename.=".";
  206. if ($this->m_refKey!="")
  207. {
  208. // Foreign key is in the destination node.
  209. $condition = $tablename.$this->m_ownerInstance->m_primaryKey[0]."=".$fieldaliasprefix.$this->fieldName().".".$this->m_refKey;
  210. }
  211. else
  212. {
  213. // Foreign key is in the source node
  214. $condition = $tablename.$this->fieldName()."=".$fieldaliasprefix.$this->fieldName().".".$this->m_destInstance->m_primaryKey[0];
  215. }
  216. $condition.= $this->getDestinationFilterCondition($fieldaliasprefix);
  217. $query->addJoin($this->m_destInstance->m_table, $fieldaliasprefix.$this->fieldName(),$condition, true,$mode);
  218. // we pass true as the last param to addToQuery, because we need all fields..
  219. $this->m_destInstance->addToQuery($query, $fieldaliasprefix.$this->fieldName(), $level+1, true,$mode);
  220. }
  221. // When storing, we don't add to the query.. we have our own store() method..
  222. // With one exception. If the foreign key is in the source node, we also need to update
  223. // the refkey value.
  224. if ($this->m_refKey=="" && $mode=="add")
  225. {
  226. $query->addField($this->fieldName(),$rec[$this->fieldName()][$this->m_destInstance->m_primaryKey[0]],"","",!$this->hasFlag(AF_NO_QUOTES));
  227. }
  228. }
  229. }
  230. /**
  231. * Retrieve detail records from the database.
  232. *
  233. * Called by the framework to load the detail records.
  234. *
  235. * @param atkDb $db The database used by the node.
  236. * @param array $record The master record
  237. * @param String $mode The mode for loading (admin, select, copy, etc)
  238. *
  239. * @return array Recordset containing detailrecords, or NULL if no detail
  240. * records are present. Note: when $mode is edit, this
  241. * method will always return NULL. This is a framework
  242. * optimization because in edit pages, the records are
  243. * loaded on the fly.
  244. */
  245. function load(&$db, $record, $mode)
  246. {
  247. if ($this->createDestination())
  248. {
  249. if ($this->m_refKey=="")
  250. {
  251. // Foreign key in owner
  252. //$condition = $this->m_destInstance->m_primaryKey[0]."=".$record[$this->fieldName()];
  253. $condition = $this->m_destInstance->m_table.'.'.$this->m_destInstance->m_primaryKey[0].
  254. "='".$record[$this->fieldName()]."'";
  255. }
  256. else
  257. {
  258. // Foreign key in destination
  259. $condition = $this->m_destInstance->m_table.'.'.$this->m_refKey."='".
  260. $this->m_ownerInstance->m_attribList[$this->m_ownerInstance->primaryKeyField()]->value2db($record)."'";
  261. $destfilter = $this->getDestinationFilter();
  262. if(is_string($destfilter) && $destfilter!="")
  263. {
  264. $condition .= " AND ".$this->m_destInstance->m_table.".".$destfilter;
  265. }
  266. }
  267. $recs = $this->m_destInstance->selectDb($condition, "", "", "", "", $mode);
  268. return $recs[0];
  269. }
  270. }
  271. /**
  272. * Construct the filter statement for filters that are set for the
  273. * destination node (m_destinationFilter).
  274. * @access private
  275. * @param string $fieldaliasprefix
  276. * @return String A where clause condition.
  277. */
  278. function getDestinationFilterCondition($fieldaliasprefix="")
  279. {
  280. $condition="";
  281. if(is_array($this->m_destinationFilter))
  282. {
  283. for($i=0,$_i=count($this->m_destinationFilter);$i<$_i;$i++)
  284. {
  285. $condition.=" AND ".$fieldaliasprefix.$this->m_name.".".$this->m_destinationFilter[$i];
  286. }
  287. }
  288. elseif ($this->m_destinationFilter!="")
  289. {
  290. $condition.=" AND ".$fieldaliasprefix.$this->m_name.".".$this->m_destinationFilter;
  291. }
  292. return $condition;
  293. }
  294. /**
  295. * The delete method is called by the framework to inform the attribute
  296. * that the master record is deleted.
  297. *
  298. * Note that the framework only calls the method when the
  299. * AF_CASCADE_DELETE flag is set. When calling this method, the detail
  300. * record belonging to the master record is deleted.
  301. *
  302. * @param array $record The record that is deleted.
  303. * @return boolean true if cleanup was successful, false otherwise.
  304. */
  305. function delete($record)
  306. {
  307. $classname = $this->m_destination;
  308. $cache_id = $this->m_owner.".".$this->m_name;
  309. $rel = &atkGetNode($classname,true,$cache_id);
  310. atkdebug("O2O DELETE for $classname: ".$this->m_refKey."=".$record[$this->m_ownerInstance->primaryKeyField()]);
  311. if ($this->m_refKey!="")
  312. {
  313. // Foreign key is in the destination node
  314. $condition = $rel->m_table.'.'.$this->m_refKey."=".
  315. $this->m_ownerInstance->m_attribList[$this->m_ownerInstance->primaryKeyField()]->value2db($record);
  316. }
  317. else
  318. {
  319. // Foreign key is in the source node.
  320. $condition = $rel->m_table.'.'.$rel->m_primaryKey[0]."=".$record[$this->fieldName()][$this->m_ownerInstance->primaryKeyField()];
  321. }
  322. return $rel->deleteDb($condition);
  323. }
  324. /**
  325. * Converts the internal attribute value to one that is understood by the
  326. * database.
  327. *
  328. * For the regular atkAttribute, this means escaping things like
  329. * quotes and slashes. Derived attributes may reimplement this for their
  330. * own conversion.
  331. * This is the exact opposite of the db2value method.
  332. *
  333. * @param array $rec The record that holds this attribute's value.
  334. * @return String The database compatible value
  335. */
  336. function db2value($rec)
  337. {
  338. // we need to pass all values to the destination node, so it can
  339. // run it's db2value stuff over it..
  340. if ($this->hasFlag(AF_ONETOONE_LAZY)&&$this->m_refKey=="")
  341. {
  342. return parent::db2value($rec);
  343. }
  344. if ($this->createDestination())
  345. {
  346. (isset($rec[$this->fieldName()][$this->m_destInstance->primaryKeyField()]))?
  347. $pkval = $rec[$this->fieldName()][$this->m_destInstance->primaryKeyField()]:$pkval=NULL;
  348. if ($pkval!=NULL && $pkval!="") // If primary key is not filled, there was no record, so we
  349. // should return NULL.
  350. {
  351. foreach (array_keys($this->m_destInstance->m_attribList) as $attribname)
  352. {
  353. $p_attrib = &$this->m_destInstance->m_attribList[$attribname];
  354. $rec[$this->fieldName()][$attribname] = $p_attrib->db2value($rec[$this->fieldName()]);
  355. }
  356. // also set the primkey..
  357. $rec[$this->fieldName()]["atkprimkey"] = $this->m_destInstance->primaryKey($rec[$this->fieldName()]);
  358. return $rec[$this->fieldName()];
  359. }
  360. }
  361. return NULL;
  362. }
  363. /**
  364. * Initialize this destinations attribute sizes.
  365. */
  366. function fetchMeta()
  367. {
  368. if ($this->hasFlag(AF_ONETOONE_INTEGRATE))
  369. {
  370. $this->createDestination();
  371. $this->getDestination()->setAttribSizes();
  372. }
  373. }
  374. /**
  375. * Convert values from an HTML form posting to an internal value for
  376. * this attribute.
  377. *
  378. * This implementation uses the destination node to fetch any field that
  379. * belongs to the other side of the relation.
  380. *
  381. * @param array $postvars The array with html posted values ($_POST, for
  382. * example) that holds this attribute's value.
  383. * @return String The internal value
  384. */
  385. function fetchValue($postvars)
  386. {
  387. // we need to pass all values to the destination node, so it can
  388. // run it's fetchValue stuff over it..
  389. if ($this->createDestination())
  390. {
  391. if ($postvars[$this->fieldName()]!=NULL)
  392. {
  393. foreach (array_keys($this->m_destInstance->m_attribList) as $attribname)
  394. {
  395. $p_attrib = &$this->m_destInstance->m_attribList[$attribname];
  396. $postvars[$this->fieldName()][$attribname] = $p_attrib->fetchValue($postvars[$this->fieldName()]);
  397. }
  398. return $postvars[$this->fieldName()];
  399. }
  400. }
  401. }
  402. /**
  403. * Determine the storage type of this attribute.
  404. *
  405. * With this method, the attribute tells the framework whether it wants
  406. * to be stored in the main query (addToQuery) or whether the attribute
  407. * has its own store() implementation.
  408. * For the atkOneToOneRelation, the results depends on whether the
  409. * relation is used in master or slave mode.
  410. *
  411. * Framework method. It should not be necesary to call this method
  412. * directly.
  413. *
  414. * @param String $mode The type of storage ("add" or "update")
  415. *
  416. * @return int Bitmask containing information about storage requirements.
  417. * POSTSTORE when in master mode.
  418. * PRESTORE|ADDTOQUERY when in slave mode.
  419. */
  420. function storageType($mode)
  421. {
  422. // Mode specific storage type.
  423. if (isset($this->m_storageType[$mode]) && $this->m_storageType[$mode] !== null)
  424. return $this->m_storageType[$mode];
  425. // Global storage type (key null is special!)
  426. else if (isset($this->m_storageType[null]) && $this->m_storageType[null] !== null)
  427. return $this->m_storageType[null];
  428. else if ($this->m_refKey!="")
  429. {
  430. // foreign key is in destination node, so we must store the
  431. // destination AFTER we stored the master record.
  432. return POSTSTORE;
  433. }
  434. else
  435. {
  436. // foreign key is in source node, so we must store the
  437. // relation node first, so we can store the foreign key
  438. // when we store the master record. To store the latter,
  439. // we must also perform an addToQuery.
  440. return PRESTORE|ADDTOQUERY;
  441. }
  442. }
  443. /**
  444. * Determine the load type of this attribute.
  445. *
  446. * With this method, the attribute tells the framework whether it wants
  447. * to be loaded in the main query (addToQuery) or whether the attribute
  448. * has its own load() implementation.
  449. * For the atkOneToOneRelation, this depends on the presence of the
  450. * AF_ONETOONE_LAZY flag.
  451. *
  452. * Framework method. It should not be necesary to call this method
  453. * directly.
  454. *
  455. * @param String $mode The type of load (view,admin,edit etc)
  456. *
  457. * @return int Bitmask containing information about load requirements.
  458. * POSTLOAD|ADDTOQUERY when AF_ONETOONE_LAZY is set.
  459. * ADDTOQUERY when AF_ONETOONE_LAZY is not set.
  460. */
  461. function loadType($mode)
  462. {
  463. if (isset($this->m_loadType[$mode]) && $this->m_loadType[$mode] !== null)
  464. return $this->m_loadType[$mode];
  465. else if (isset($this->m_loadType[null]) && $this->m_loadType[null] !== null)
  466. return $this->m_loadType[null];
  467. else if ($this->hasFlag(AF_ONETOONE_LAZY))
  468. return POSTLOAD|ADDTOQUERY;
  469. else
  470. return ADDTOQUERY;
  471. }
  472. /**
  473. * Store detail record in the database.
  474. *
  475. * @param atkDb $db The database used by the node.
  476. * @param array $record The master record which has the detail records
  477. * embedded.
  478. * @param string $mode The mode we're in ("add", "edit", "copy")
  479. * @return boolean true if store was successful, false otherwise.
  480. */
  481. function store(&$db, &$record, $mode)
  482. {
  483. if ($this->createDestination())
  484. {
  485. $vars = &$this->_getStoreValue($record);
  486. if ($vars["mode"]=="edit")
  487. {
  488. atkdebug("Updating existing one2one record");
  489. // we put the vars in the postvars, because there is information
  490. // like atkorgkey in it that is vital.
  491. // but we restore the postvars after we're done updating
  492. $oldpost = $this->m_destInstance->m_postvars;
  493. $this->m_destInstance->m_postvars = $vars;
  494. $res = $this->m_destInstance->updateDb($vars);
  495. $this->m_destInstance->m_postvars = $oldpost;
  496. return $res;
  497. }
  498. elseif ($vars["mode"]=="add" || $mode == "add" || $mode == "copy")
  499. {
  500. if (!empty($vars["atkprimkey"]) && $mode!="copy")
  501. {
  502. // destination record already exists, and we are not copying.
  503. $result = true;
  504. }
  505. else
  506. {
  507. atkdebug("atkonetoonerelation->store(): Adding new one2one record for mode $mode");
  508. $this->m_destInstance->preAdd($vars);
  509. $result = $this->m_destInstance->addDb($vars, true, $mode);
  510. }
  511. if ($this->m_refKey=="")
  512. {
  513. // Foreign key is in source node, so we must update the record value with
  514. $record[$this->fieldName()][$this->m_destInstance->m_primaryKey[0]] = $vars[$this->m_destInstance->m_primaryKey[0]];
  515. }
  516. return $result;
  517. }
  518. else
  519. {
  520. atkdebug("atkonetoonerelation->store(): Nothing to store in one2one record");
  521. return true;
  522. }
  523. }
  524. }
  525. /**
  526. * Needs update?
  527. *
  528. * @param array $record the record
  529. * @return boolean needs update
  530. */
  531. function needsUpdate($record)
  532. {
  533. return $this->m_forceupdate ||
  534. (parent::needsUpdate($record) &&
  535. $this->createDestination() &&
  536. !$this->m_destInstance->hasFlag(NF_READONLY));
  537. }
  538. /**
  539. * Gets the value to store for the onetoonerelation
  540. *
  541. * @param Array &$record The record to get the value from
  542. * @return mixed The value to store
  543. */
  544. function &_getStoreValue(&$record)
  545. {
  546. $vars = &$record[$this->fieldName()];
  547. if ($this->m_refKey!="")
  548. {
  549. // Foreign key is in destination node
  550. if ($this->destinationHasRelation())
  551. {
  552. $vars[$this->m_refKey][$this->m_ownerInstance->primaryKeyField()] = $record[$this->m_ownerInstance->primaryKeyField()];
  553. }
  554. else
  555. {
  556. //if the a onetoonerelation has no relation on the other side the m_refKey is not an array
  557. // experimental, will the next line always work?
  558. $refattr = $this->m_destInstance->getAttribute($this->m_refKey);
  559. if ($refattr->m_destination)
  560. {
  561. /**
  562. * If we have a destination, the ref key is a non-onetoone relation!
  563. * So we have to treat the record as such... this is specifically geared towards
  564. * the manytoone relation and may not work for others
  565. * A way should be found to make this work for whatever
  566. * maybe use value2db and db2value on eachother or something?
  567. */
  568. $vars[$this->m_refKey][$this->m_ownerInstance->primaryKeyField()] = $this->m_ownerInstance->m_attribList[$this->m_ownerInstance->primaryKeyField()]->value2db($vars[$this->m_refKey]);
  569. }
  570. else
  571. {
  572. $vars[$this->m_refKey] = $this->m_ownerInstance->m_attribList[$this->m_ownerInstance->primaryKeyField()]->value2db($record);
  573. }
  574. }
  575. }
  576. else
  577. {
  578. // Foreign key is in source node
  579. // After add, we must store the key value.
  580. }
  581. return $vars;
  582. }
  583. /**
  584. * Determine the type of the attribute on the other side.
  585. *
  586. * On the other side of a oneToOneRelation (in the destination node),
  587. * there may be a regular atkAttribute for the referential key, or an
  588. * atkOneToOneRelation pointing back at the source. This method discovers
  589. * which of the 2 cases we are dealing with.
  590. * @return boolean True if the attribute on the other side is a
  591. * relation, false if not.
  592. */
  593. function destinationHasRelation()
  594. {
  595. if ($this->createDestination())
  596. {
  597. if (isset($this->m_refKey) && !empty($this->m_refKey))
  598. {
  599. // foreign key is in the destination node.
  600. $attrib = $this->m_destInstance->m_attribList[$this->m_refKey];
  601. }
  602. else
  603. {
  604. // foreign key is in the source node. In this case, we must check the primary key
  605. // of the target node.
  606. $attrib = $this->m_destInstance->m_attribList[$this->m_destInstance->m_primaryKey[0]];
  607. }
  608. if (is_object($attrib) && strpos(get_class($attrib), "elation")!==false) return true;
  609. }
  610. return false;
  611. }
  612. /**
  613. * Returns a piece of html code for hiding this attribute in an HTML form,
  614. * while still posting its values. (<input type="hidden">)
  615. *
  616. * @param array $record The record that holds the value for this attribute
  617. * @param String $fieldprefix The fieldprefix to put in front of the name
  618. * of any html form element for this attribute.
  619. * @return String A piece of htmlcode with hidden form elements that post
  620. * This attribute's value without showing it.
  621. */
  622. function hide($record="", $fieldprefix="")
  623. {
  624. atkdebug("hide called for ".$this->fieldName());
  625. if ($this->createDestination())
  626. {
  627. if ($record[$this->fieldName()]!=NULL)
  628. {
  629. $myrecord = $record[$this->fieldName()];
  630. if ($myrecord[$this->m_destInstance->primaryKeyField()]==NULL)
  631. {
  632. // rec has no primkey yet, so we must add instead of update..
  633. $mode = "add";
  634. }
  635. else
  636. {
  637. $mode = "edit";
  638. $myrecord["atkprimkey"] = $this->m_destInstance->primaryKey($myrecord);
  639. }
  640. }
  641. else
  642. {
  643. $mode = "add";
  644. }
  645. $output.='<input type="hidden" name="'.$fieldprefix.$this->fieldName().'[mode]" value="'.$mode.'">';
  646. $forceList = decodeKeyValueSet($this->getFilter());
  647. $output.= $this->m_destInstance->hideform($mode,$myrecord,$forceList,$fieldprefix.$this->fieldName()."_AE_");
  648. return $output;
  649. }
  650. return "";
  651. }
  652. /**
  653. * Adds the attribute's edit / hide HTML code to the edit array.
  654. *
  655. * This method is called by the node if it wants the data needed to create
  656. * an edit form. The method is an override of atkAttribute's method,
  657. * because in the atkOneToOneRelation, we need to implement the
  658. * AF_ONETOONE_INTEGRATE feature.
  659. *
  660. * This is a framework method, it should never be called directly.
  661. *
  662. * @param String $mode the edit mode ("add" or "edit")
  663. * @param array $arr pointer to the edit array
  664. * @param array $defaults pointer to the default values array
  665. * @param array $error pointer to the error array
  666. * @param String $fieldprefix the fieldprefix
  667. */
  668. function addToEditArray($mode, &$arr, &$defaults, &$error, $fieldprefix)
  669. {
  670. /* hide */
  671. if (($mode == "edit" && $this->hasFlag(AF_HIDE_EDIT)) || ($mode == "add" && $this->hasFlag(AF_HIDE_ADD)))
  672. {
  673. /* when adding, there's nothing to hide... */
  674. if ($mode=="edit" || ($mode == "add" && !$this->isEmpty($defaults)))
  675. $arr["hide"][] = $this->hide($defaults, $fieldprefix, $mode);
  676. }
  677. /* edit */
  678. else
  679. {
  680. /* we first check if there is no edit override method, if there
  681. * is this method has the same behaviour as the atkAttribute's method
  682. */
  683. if (method_exists($this->m_ownerInstance, $this->m_name."_edit") ||
  684. $this->edit($defaults, $fieldprefix, $mode) !== NULL)
  685. {
  686. atkAttribute::addToEditArray($mode, $arr, $defaults, $error, $fieldprefix);
  687. }
  688. /* how we handle 1:1 relations normally */
  689. else
  690. {
  691. if (!$this->createDestination()) return;
  692. /* readonly */
  693. if ($this->m_destInstance->hasFlag(NF_READONLY) || ($mode=="edit" && $this->hasFlag(AF_READONLY_EDIT)) || ($mode=="add" && $this->hasFlag(AF_READONLY_ADD)))
  694. {
  695. $this->createDestination();
  696. $attrNames = $this->m_destInstance->getAttributeNames();
  697. foreach ($attrNames as $attrName)
  698. {
  699. $attr = &$this->m_destInstance->getAttribute($attrName);
  700. $attr->addFlag(AF_READONLY);
  701. }
  702. }
  703. /* we first check if the record doesn't already exist */
  704. if (isset($defaults[$this->fieldName()]) && !empty($defaults[$this->fieldName()]))
  705. {
  706. /* record has no primarykey yet, so we must add instead of update */
  707. $myrecord = $defaults[$this->fieldName()];
  708. if (empty($myrecord[$this->m_destInstance->primaryKeyField()]))
  709. {
  710. $mode = "add";
  711. }
  712. /* record exists! */
  713. else
  714. {
  715. $mode = "edit";
  716. $myrecord["atkprimkey"] = $this->m_destInstance->primaryKey($myrecord);
  717. }
  718. }
  719. /* record does not exist */
  720. else
  721. {
  722. $mode = "add";
  723. }
  724. /* mode */
  725. $arr["hide"][] = '<input type="hidden" name="'.$fieldprefix.$this->fieldName().'[mode]" value="'.$mode.'">';
  726. /* add fields */
  727. $forceList = decodeKeyValueSet($this->m_destinationFilter);
  728. if ($this->m_refKey!="")
  729. {
  730. if($this->destinationHasRelation())
  731. {
  732. $forceList[$this->m_refKey][$this->m_ownerInstance->primaryKeyField()] = $defaults[$this->m_ownerInstance->primaryKeyField()];
  733. }
  734. else
  735. {
  736. // its possible that the destination has no relation back. In that case the refKey is just an attribute
  737. $forceList[$this->m_refKey] = $defaults[$this->m_ownerInstance->primaryKeyField()];
  738. }
  739. }
  740. $a = $this->m_destInstance->editArray($mode, $myrecord, $forceList, array(), $fieldprefix.$this->fieldName()."_AE_", false, false);
  741. /* hidden fields */
  742. $arr["hide"] = array_merge($arr["hide"], $a["hide"]);
  743. /* editable fields, if AF_NOLABEL is specified or if there is just 1 field with the
  744. * same name as the relation we don't display a label
  745. * TODO FIXME
  746. */
  747. if(!is_array($arr['fields'])) $arr['fields']=array();
  748. if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) && !$this->hasFlag(AF_NOLABEL) && !(count($a["fields"]) == 1 && $a["fields"][0]["name"] == $this->m_name))
  749. {
  750. /* separator and name */
  751. if ($arr['fields'][count($arr['fields'])-1]['html']!=='-') $arr["fields"][] = array("html"=>"-", "tabs"=>$this->m_tabs,'sections'=>$this->getSections());
  752. $arr["fields"][] = array("line"=>"<b>".atktext($this->m_name, $this->m_ownerInstance->m_module, $this->m_ownerInstance->m_type)."</b>", "tabs"=>$this->m_tabs,'sections'=>$this->getSections());
  753. }
  754. if (is_array($a["fields"]))
  755. {
  756. // in non-integration mode we move all the fields to the one-to-one relations tabs/sections
  757. if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) || $this->hasFlag(AF_ONETOONE_RESPECT_TABS))
  758. {
  759. foreach (array_keys($a['fields']) as $key)
  760. {
  761. $a['fields'][$key]['tabs'] = $this->m_tabs;
  762. $a['fields'][$key]['sections'] = $this->getSections();
  763. }
  764. }
  765. $arr["fields"] = array_merge($arr["fields"], $a["fields"]);
  766. }
  767. if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) && !$this->hasFlag(AF_NOLABEL) && !(count($a["fields"]) == 1 && $a["fields"][0]["name"] == $this->m_name))
  768. {
  769. /* separator */
  770. $arr["fields"][] = array("html"=>"-", "tabs"=>$this->m_tabs,'sections'=>$this->getSections());
  771. }
  772. $fields = $arr['fields'];
  773. foreach ($fields as &$field)
  774. {
  775. $field['attribute']='';
  776. }
  777. }
  778. }
  779. }
  780. /**
  781. * Adds the attribute's view / hide HTML code to the view array.
  782. *
  783. * This method is called by the node if it wants the data needed to create
  784. * a view form.
  785. *
  786. * This is a framework method, it should never be called directly.
  787. *
  788. * @param String $mode the mode ("view")
  789. * @param array $arr pointer to the view array
  790. * @param array $defaults pointer to the default values array
  791. */
  792. function addToViewArray($mode, &$arr, &$defaults)
  793. {
  794. if ($this->hasFlag(AF_HIDE_VIEW)) return;
  795. /* we first check if there is no display override method, if there
  796. * is this method has the same behaviour as the atkAttribute's method
  797. */
  798. if (method_exists($this->m_ownerInstance, $this->m_name."_display") ||
  799. $this->display($defaults, 'view') !== NULL)
  800. {
  801. atkAttribute::addToViewArray($mode, $arr, $defaults);
  802. }
  803. /* how we handle 1:1 relations normally */
  804. else
  805. {
  806. if (!$this->createDestination()) return;
  807. $record = $defaults[$this->fieldName()];
  808. $a = $this->m_destInstance->viewArray($mode, $record, false);
  809. /* editable fields, if AF_NOLABEL is specified or if there is just 1 field with the
  810. * same name as the relation we don't display a label
  811. * TODO FIXME
  812. */
  813. if (!is_array($arr['fields'])) $arr['fields']=array();
  814. if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) && !$this->hasFlag(AF_NOLABEL) && !(count($a["fields"]) == 1 && $a["fields"][0]["name"] == $this->m_name))
  815. {
  816. /* separator and name */
  817. if ($arr['fields'][count($arr['fields'])-1]['html']!=='-') $arr["fields"][] = array("html"=>"-", "tabs"=>$this->m_tabs,'sections'=>$this->getSections());
  818. $arr["fields"][] = array("line"=>"<b>".atktext($this->m_name, $this->m_ownerInstance->m_module, $this->m_ownerInstance->m_type)."</b>", "tabs"=>$this->m_tabs,'sections'=>$this->getSections());
  819. }
  820. if (is_array($a["fields"]))
  821. {
  822. if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) || $this->hasFlag(AF_ONETOONE_RESPECT_TABS))
  823. {
  824. foreach (array_keys($a['fields']) as $key)
  825. {
  826. $a['fields'][$key]['tabs'] = $this->m_tabs;
  827. $a['fields'][$key]['sections'] = $this->getSections();
  828. }
  829. }
  830. $arr["fields"] = array_merge($arr["fields"], $a["fields"]);
  831. }
  832. if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) && !$this->hasFlag(AF_NOLABEL) && !(count($a["fields"]) == 1 && $a["fields"][0]["name"] == $this->m_name))
  833. {
  834. /* separator */
  835. $arr["fields"][] = array("html"=>"-", "tabs"=>$this->m_tabs,'sections'=>$this->getSections());
  836. }
  837. }
  838. }
  839. /**
  840. * Check if a record has an empty value for this attribute.
  841. * @param array $record The record that holds this attribute's value.
  842. * @todo This method is not currently implemented properly and returns
  843. * false in all cases.
  844. * @return boolean
  845. */
  846. function isEmpty($record)
  847. {
  848. return false;
  849. }
  850. /**
  851. * Checks if a value is valid.
  852. *
  853. * For the atkOneToOneRelation, this method delegates the actual
  854. * validation of values to the destination node.
  855. *
  856. * @param array $record The record that holds the value for this
  857. * attribute. If an error occurs, the error will
  858. * be stored in the 'atkerror' field of the record.
  859. * @param String $mode The mode for which should be validated ("add" or
  860. * "update")
  861. */
  862. function validate(&$record, $mode)
  863. {
  864. // zitten AF_ONETOONE_ERROR en AF_OBLIGATORY elkaar soms in de weg
  865. if ($this->hasFlag(AF_ONETOONE_ERROR) &&
  866. ($mode!="add" || !$this->hasFlag(AF_HIDE_ADD)) &&
  867. $this->createDestination())
  868. {
  869. $this->m_destInstance->validate($record[$this->fieldName()], $mode, array($this->m_refKey));
  870. // only add 'atkerror' record when 1:1 relation contains error
  871. if (!isset($record[$this->fieldName()]["atkerror"]))
  872. {
  873. return;
  874. }
  875. foreach ($record[$this->fieldName()]["atkerror"] as $error)
  876. {
  877. $error['tab'] = $this->hasFlag(AF_ONETOONE_RESPECT_TABS) ? $this->m_tabs[0] : $error['tab'];
  878. $record["atkerror"][] = $error;
  879. }
  880. }
  881. }
  882. /**
  883. * @deprecated Use getDestinationFilterCondition() instead.
  884. */
  885. function getFilter()
  886. {
  887. $filter = $this->m_destinationFilter;
  888. if(is_array($filter))
  889. {
  890. $tmp_filter="";
  891. for($i=0,$_i=count($filter);$i<$_i;$i++)
  892. {
  893. if($tmp_filter!="") $tmp_filter.=" AND ";
  894. $tmp_filter.=$filter[$i];
  895. }
  896. return $tmp_filter;
  897. }
  898. else
  899. {
  900. return $filter;
  901. }
  902. }
  903. /**
  904. * Get list of additional tabs.
  905. *
  906. * Attributes can add new tabs to tabbed screens. This method will be
  907. * called to retrieve the tabs. When AF_ONETOONE_INTEGRATE is set, the
  908. * atkOneToOneRelation adds tabs from the destination node to the tab
  909. * screen, so the attributes are seamlessly integrated but still on their
  910. * own tabs.
  911. *
  912. * @param String $action The action for which additional tabs should be
  913. * loaded.
  914. * @return array The list of tabs to add to the screen.
  915. */
  916. function getAdditionalTabs($action)
  917. {
  918. if ($this->hasFlag(AF_ONETOONE_INTEGRATE) && $this->createDestination())
  919. {
  920. $detailtabs = $this->m_destInstance->getTabs($action);
  921. if (count($detailtabs)==1 && $detailtabs[0]=="default")
  922. {
  923. // All elements in the relation are on the default tab. That means we should
  924. // inherit the tab from the onetoonerelation itself.
  925. return parent::getAdditionalTabs($action);
  926. }
  927. return $detailtabs;
  928. }
  929. return parent::getAdditionalTabs($action);
  930. }
  931. /**
  932. * Check if the attribute wants to be shown on a certain tab.
  933. *
  934. * @param String $tab The name of the tab to check.
  935. * @return boolean
  936. */
  937. function showOnTab($tab)
  938. {
  939. if ($this->hasFlag(AF_ONETOONE_INTEGRATE) && $this->createDestination())
  940. {
  941. foreach (array_keys($this->m_destInstance->m_attribList) as $attribname)
  942. {
  943. $p_attrib = &$this->m_destInstance->m_attribList[$attribname];
  944. if ($p_attrib->showOnTab($tab)) return true; // If we have one match, we can return true.
  945. }
  946. // None of the destionation attributes wants to be displayed on the tab.
  947. // If the entire onetoone itself is on that tab however, we should put all attribs on
  948. // this tab.
  949. return parent::showOnTab($tab);
  950. }
  951. return parent::showOnTab($tab);
  952. }
  953. /**
  954. * Adds the attribute / field to the list header. This includes the column name and search field.
  955. *
  956. * Framework method. It should not be necessary to call this method directly.
  957. *
  958. * @param String $action the action that is being performed on the node
  959. * @param array $arr reference to the the recordlist array
  960. * @param String $fieldprefix the fieldprefix
  961. * @param int $flags the recordlist flags
  962. * @param array $atksearch the current ATK search list (if not empty)
  963. * @param String $atkorderby the current ATK orderby string (if not empty)
  964. * @see atkNode::listArray
  965. */
  966. function addToListArrayHeader($action, &$arr, $fieldprefix, $flags, $atksearch, $columnConfig, atkDataGrid $grid=null, $column='*')
  967. {
  968. if ($this->hasFlag(AF_HIDE_LIST) || !$this->createDestination())
  969. {
  970. return;
  971. }
  972. if ((!$this->hasFlag(AF_ONETOONE_INTEGRATE) && $column == '*') || $column == null)
  973. {
  974. // regular behaviour.
  975. parent::addToListArrayHeader($action, $arr, $fieldprefix, $flags, $atksearch, $columnConfig, $grid, $column);
  976. return;
  977. }
  978. else if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) || ($column != '*' && $this->getDestination()->getAttribute($column) == null))
  979. {
  980. throw new Exception("Invalid list column {$column} for atkOneToOneRelation ".$this->getOwnerInstance()->atkNodeType().'::'.$this->fieldName());
  981. }
  982. // integrated version, don't add ourselves, but add all columns from the destination.
  983. $prefix = $fieldprefix.$this->fieldName()."_AE_";
  984. foreach (array_keys($this->m_destInstance->m_attribList) as $attribname)
  985. {
  986. if ($column != '*' && $column != $attribname)
  987. {
  988. continue;
  989. }
  990. $p_attrib = &$this->m_destInstance->getAttribute($attribname);
  991. $p_attrib->addToListArrayHeader($action, $arr, $prefix, $flags, $atksearch[$this->fieldName()], $columnConfig, $grid, null);
  992. }
  993. }
  994. /**
  995. * Adds the attribute / field to the list row. And if the row is totalisable also to the total.
  996. *
  997. * Framework method. It should not be necessary to call this method directly.
  998. *
  999. * @param String $action the action that is being performed on the node
  1000. * @param array $arr reference to the the recordlist array
  1001. * @param int $nr the current row number
  1002. * @param String $fieldprefix the fieldprefix
  1003. * @param int $flags the recordlist flags
  1004. * @see atkNode::listArray
  1005. */
  1006. function addToListArrayRow($action, &$arr, $nr, $fieldprefix, $flags, $edit=false, atkDataGrid $grid=null, $column='*')
  1007. {
  1008. if ($this->hasFlag(AF_HIDE_LIST) || !$this->createDestination())
  1009. {
  1010. return;
  1011. }
  1012. if ((!$this->hasFlag(AF_ONETOONE_INTEGRATE) && $column == '*') || $column == null)
  1013. {
  1014. parent::addToListArrayRow($action, $arr, $nr, $fieldprefix, $flags, $edit, $grid, $column);
  1015. return;
  1016. }
  1017. else if (!$this->hasFlag(AF_ONETOONE_INTEGRATE) || ($column != '*' && $this->getDestination()->getAttribute($column) == null))
  1018. {
  1019. throw new Exception("Invalid list column {$column} for atkOneToOneRelation ".$this->getOwnerInstance()->atkNodeType().'::'.$this->fieldName());
  1020. }
  1021. // integrated version, don't add ourselves, but add all columns from the destination
  1022. // small trick, the destination record is in a subarray. The destination
  1023. // addToListArrayRow will not expect this though, so we have to modify the
  1024. // record a bit before passing it to the detail columns.
  1025. $oldrecord = $arr["rows"][$nr]["record"];
  1026. $arr["rows"][$nr]["record"] = $arr["rows"][$nr]["record"][$this->fieldName()];
  1027. $prefix = $fieldprefix.$this->fieldName()."_AE_";
  1028. foreach (array_keys($this->m_destInstance->m_attribList) as $attribname)
  1029. {
  1030. if ($column != '*' && $column != $attribname)
  1031. {
  1032. continue;
  1033. }
  1034. $p_attrib = &$this->m_destInstance->getAttribute($attribname);
  1035. $p_attrib->addToListArrayRow($action, $arr, $nr, $prefix, $flags, $edit, $grid, null);
  1036. }
  1037. $arr["rows"][$nr]["record"] = $oldrecord;
  1038. }
  1039. /**
  1040. * Creates a searchcondition for the field,
  1041. * was once part of searchCondition, however,
  1042. * searchcondition() also immediately adds the search condition.
  1043. *
  1044. * @param atkQuery $query The query object where the search condition should be placed on
  1045. * @param String $table The name of the table in which this attribute
  1046. * is stored
  1047. * @param mixed $value The value the user has entered in the searchbox
  1048. * @param String $searchmode The searchmode to use. This can be any one
  1049. * of the supported modes, as returned by this
  1050. * attribute's getSearchModes() method.
  1051. * @return String The searchcondition to use.
  1052. */
  1053. function getSearchCondition(&$query, $table, $value, $searchmode)
  1054. {
  1055. if ($this->createDestination() && is_array($value))
  1056. {
  1057. // we are a relation, so instead of hooking ourselves into the
  1058. // query, hook the attributes in the destination node onto the query
  1059. foreach($value as $key => $val)
  1060. {
  1061. // if we aren't searching for anything in this field, there is no need
  1062. // to look any further:
  1063. if ($val==="" || $val===null) continue;
  1064. $p_attrib = &$this->m_destInstance->m_attribList[$key];
  1065. if (is_object($p_attrib))
  1066. {
  1067. if ($this->m_refKey && $this->createDestination())
  1068. {
  1069. // master mode
  1070. $new_table = &$this->fieldName();
  1071. }
  1072. else
  1073. {
  1074. // slave mode
  1075. $new_table = &$this->m_destInstance->m_table;
  1076. // we need to left join the destination table into the query
  1077. // (don't worry ATK won't add it when it's already there)
  1078. $query->addJoin($new_table,$new_table,($this->getJoinCondition($query)),false);
  1079. }
  1080. $p_attrib->searchCondition($query,$new_table,$val,$this->getChildSearchMode($searchmode, $p_attrib->formName()));
  1081. }
  1082. else
  1083. {
  1084. // attribute not found in destination, so it should
  1085. // be in the owner (this is the case when extra fields
  1086. // are in the relation
  1087. $p_attrib = &$this->m_ownerInstance->m_attribList[$key];
  1088. if (is_object($p_attrib))
  1089. {
  1090. $p_attrib->searchCondition($query,$p_attrib->getTable($key),$val,$this->getChildSearchMode($searchmode, $p_attrib->formName()));
  1091. }
  1092. else atkdebug("Field $key was not found in this relation (this is very weird)");
  1093. }
  1094. }
  1095. }
  1096. else
  1097. {
  1098. // we were passed a value that is not an array, so appearantly the function calling us
  1099. // does not know we are a relation, not just another attrib
  1100. // so we assume that it is looking for something in the descriptor def of the destination
  1101. if ($this->createDestination())
  1102. {
  1103. $descfields = $this->m_destInstance->descriptorFields();
  1104. foreach ($descfields as $key)
  1105. {
  1106. $p_attrib = &$this->m_destInstance->m_attribList[$key];
  1107. if (is_object($p_attrib))
  1108. {
  1109. if ($this->m_refKey && $this->createDestination())
  1110. {
  1111. // master mode
  1112. $new_table = &$this->fieldName();
  1113. }
  1114. else
  1115. {
  1116. // slave mode
  1117. $new_table = &$this->m_destInstance->m_table;
  1118. // we need to left join the destination table into the query
  1119. // (don't worry ATK won't add it when it's already there)
  1120. $query->addJoin($new_table,$new_table,($this->getJoinCondition()),false);
  1121. }
  1122. $p_attrib->searchCondition($query,$new_table,$value,$searchmode);
  1123. }
  1124. }
  1125. }
  1126. }
  1127. }
  1128. /**
  1129. * Returns the condition which can be used when calling atkQuery's addJoin() method
  1130. * Joins the relation's owner with the destination
  1131. *
  1132. * @param atkQuery $query The query object
  1133. * @param string $tablename The name of the table
  1134. * @param string $fieldalias The field alias
  1135. * @return string condition the condition that can be pasted into the query
  1136. */
  1137. function getJoinCondition(&$query, $tablename="",$fieldalias="")
  1138. {
  1139. $condition = $this->m_ownerInstance->m_table . "." . $this->fieldName();
  1140. $condition .= "=";
  1141. $condition .= $this->m_destInstance->m_table . "." . $this->m_destInstance->primaryKeyField();
  1142. return $condition;
  1143. }
  1144. /**
  1145. * Overridden method; in the integrated version, we should let the destination
  1146. * attributes hook themselves into the fieldlist instead of hooking the relation
  1147. * in it.
  1148. * For original documentation for this method, please see the atkAttribute class
  1149. *
  1150. * @param array $fields The array containing fields to use in the
  1151. * extended search
  1152. * @param atkNode $node The node where the field is in
  1153. * @param array $record A record containing default values to put
  1154. * into the search fields.
  1155. * @param array $fieldprefix search / mode field prefix
  1156. * @param array $currentSearchMode current search mode
  1157. */
  1158. function addToSearchformFields(&$fields, &$node, &$record, $fieldprefix = "", $currentSearchMode=array())
  1159. {
  1160. if (!is_array($currentSearchMode))
  1161. $currentSearchMode = array();
  1162. if ($this->hasFlag(AF_ONETOONE_INTEGRATE) && $this->createDestination())
  1163. {
  1164. $prefix = $fieldprefix.$this->fieldName()."_AE_";
  1165. foreach (array_keys($this->m_destInstance->m_attribList) as $attribname)
  1166. {
  1167. $p_attrib = &$this->m_destInstance->m_attribList[$attribname];
  1168. if (!$p_attrib->hasFlag(AF_HIDE_SEARCH))
  1169. {
  1170. $p_attrib->addToSearchformFields($fields, $node, $record[$this->fieldName()], $prefix, $currentSearchMode[$this->fieldName()]);
  1171. }
  1172. }
  1173. }
  1174. else
  1175. {
  1176. parent::addToSearchformFields($fields,$node,$record, $fieldprefix, $currentSearchMode);
  1177. }
  1178. }
  1179. /**
  1180. * Convert the internal value to the database value
  1181. *
  1182. * @param array $rec The record that holds the value for this attribute
  1183. * @return mixed The database value
  1184. */
  1185. function value2db($rec)
  1186. {
  1187. if (is_array($rec)&&isset($rec[$this->fieldName()]))
  1188. {
  1189. if (is_array($rec[$this->fieldName()]))
  1190. return $this->escapeSQL($rec[$this->fieldName()][$this->m_destInstance->primaryKeyField()]);
  1191. else
  1192. return $rec[$this->fieldName()];
  1193. }
  1194. return NULL;
  1195. }
  1196. }
  1197. ?>