PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Adapto/Relation.php

http://github.com/egeniq/adapto
PHP | 393 lines | 274 code | 18 blank | 101 comment | 5 complexity | 3f0e9eddac2d829e44c80c02658e05b5 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Adapto Toolkit.
  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 adapto
  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. */
  15. /**
  16. * The Adapto_Relation class defines a relation to another entity.
  17. *
  18. * @author ijansch
  19. * @package adapto
  20. * @subpackage relations
  21. * @abstract
  22. *
  23. */
  24. class Adapto_Relation extends Adapto_Attribute
  25. {
  26. /**
  27. * @var String Destination entity.
  28. */
  29. public $m_destination; // defaulted to public
  30. /**
  31. * @var atkEntity Destination instance.
  32. */
  33. public $m_destInstance=""; // defaulted to public
  34. /**
  35. * @var String Filter for destination records.
  36. */
  37. public $m_destinationFilter=""; // defaulted to public
  38. /**
  39. * Descriptor template for destination entity.
  40. * @var String
  41. */
  42. public $m_descTemplate = NULL; // defaulted to public
  43. /**
  44. * Descriptor handler.
  45. * @var Object
  46. */
  47. public $m_descHandler = NULL; // defaulted to public
  48. /**
  49. * Constructor
  50. * @param String $name The name of the relation.
  51. * @param String $destination The destination entity (in module.name notation)
  52. * @param int $flags Flags for the relation
  53. */
  54. public function __construct($name, $destination, $flags=0)
  55. {
  56. parent::__construct($name, $flags);
  57. $this->m_destination = $destination;
  58. }
  59. /**
  60. * Returns the destination filter.
  61. * @return String The destination filter.
  62. */
  63. function getDestinationFilter()
  64. {
  65. return $this->m_destinationFilter;
  66. }
  67. /**
  68. * Sets the destination filter.
  69. * @param String $filter The destination filter.
  70. */
  71. function setDestinationFilter($filter)
  72. {
  73. $this->m_destinationFilter = $this->_cleanupDestinationFilter($filter);
  74. }
  75. /**
  76. * Remove redundant (more than 1 subsequently) spaces from the filter string.
  77. *
  78. * This prevents the filter from rapidly becoming too long to be passed in the URL if
  79. * enters are used in code to make the filter readable.
  80. *
  81. * @param string $filter
  82. * @return string
  83. */
  84. function _cleanupDestinationFilter($filter)
  85. {
  86. $result = '';
  87. $filter_length = strlen($filter);
  88. $quotes = array("'",'"','`');
  89. $quoteStack = array();
  90. $lastChar = '';
  91. for ($i = 0; $i < $filter_length; $i++)
  92. {
  93. $currentChar = $filter[$i];
  94. if(in_array($currentChar,$quotes))
  95. {
  96. if(sizeof($quoteStack) > 0 && $currentChar == $quoteStack[sizeof($quoteStack) - 1])
  97. {
  98. array_pop($quoteStack);
  99. }
  100. else
  101. {
  102. array_push($quoteStack,$currentChar);
  103. }
  104. }
  105. // not between quotes
  106. if(!($currentChar === ' ' && $lastChar === ' ' && sizeof($quoteStack) == 0))
  107. {
  108. if($currentChar != "\n") $result .= $currentChar;
  109. }
  110. $lastChar = $currentChar;
  111. }
  112. return $result;
  113. }
  114. /**
  115. * Adds a filter value to the destination filter.
  116. * @param String $filter Filter to be added to the destination filter.
  117. */
  118. function addDestinationFilter($filter)
  119. {
  120. $filter = $this->_cleanupDestinationFilter($filter);
  121. if($this->m_destinationFilter != "")
  122. $this->m_destinationFilter = "({$this->m_destinationFilter}) AND ({$filter})";
  123. else
  124. $this->m_destinationFilter = $filter;
  125. return $this;
  126. }
  127. /**
  128. * Get descriptor handler.
  129. * @return Object descriptor handler
  130. */
  131. function &getDescriptorHandler()
  132. {
  133. return $this->m_descHandler;
  134. }
  135. /**
  136. * Set descriptor handler.
  137. * @param Object $handler The descriptor handler.
  138. */
  139. function setDescriptorHandler(&$handler)
  140. {
  141. $this->m_descHandler = &$handler;
  142. }
  143. /**
  144. * Returns the descriptor template for the destination entity.
  145. * @return String The descriptor Template
  146. */
  147. function getDescriptorTemplate()
  148. {
  149. return $this->m_descTemplate;
  150. }
  151. /**
  152. * Sets the descriptor template for the destination entity.
  153. * @param String $template The descriptor template.
  154. */
  155. function setDescriptorTemplate($template)
  156. {
  157. $this->m_descTemplate = $template;
  158. }
  159. /**
  160. * Descriptor handler. Forwards description handler calls
  161. * to the real description handler.
  162. *
  163. * @param array $record The record
  164. * @param atkEntity $entity The atkentity object
  165. * @return String with the descriptor
  166. */
  167. function descriptor($record, &$entity)
  168. {
  169. $method = $this->m_name."_descriptor";
  170. if (method_exists($this->m_descHandler, $method))
  171. return $this->m_descHandler->$method($record, $entity);
  172. else return $this->m_descHandler->descriptor($record, $entity);
  173. }
  174. /**
  175. * Create the instance of the destination.
  176. *
  177. * If succesful, the instance is stored in the m_destInstance member variable.
  178. *
  179. * @return boolean true if succesful, false if something went wrong.
  180. */
  181. function createDestination()
  182. {
  183. if (!is_object($this->m_destInstance))
  184. {
  185. $cache_id = $this->m_owner.".".$this->m_name;
  186. $this->m_destInstance = &getEntity($this->m_destination, true, $cache_id);
  187. // Validate if destination was created succesfully
  188. if (!is_object($this->m_destInstance))
  189. {
  190. throw new Adapto_Exception("Relation with unknown entitytype '".$this->m_destination."' (in entity '".$this->m_owner."')");
  191. $this->m_destInstance = NULL;
  192. return false;
  193. }
  194. if ($this->hasFlag(AF_NO_FILTER))
  195. $this->m_destInstance->m_flags |= EF_NO_FILTER;
  196. foreach (array_keys($this->m_destInstance->m_attribList) as $key)
  197. {
  198. $attribute = &$this->m_destInstance->m_attribList[$key];
  199. if (is_subclass_of($attribute, "atkrelation") && is_object($this->m_ownerInstance) && $attribute->m_destination == $this->m_ownerInstance->atkEntityType())
  200. {
  201. $attribute->m_destInstance = &$this->m_ownerInstance;
  202. if (count($attribute->m_tabs) == 1 && $attribute->m_tabs[0] == "default")
  203. {
  204. $attribute->setTabs($this->m_tabs);
  205. }
  206. }
  207. }
  208. if (!empty($this->m_descHandler))
  209. $this->m_destInstance->setDescriptorHandler($this);
  210. if (!empty($this->m_descTemplate))
  211. $this->m_destInstance->setDescriptorTemplate($this->m_descTemplate);
  212. }
  213. return true;
  214. }
  215. /**
  216. * Return a displayable string for a record.
  217. * @param array $record The record that contains the information to display.
  218. * @return String a displayable string for this value.
  219. */
  220. function display($record)
  221. {
  222. return $record[$this->fieldName()];
  223. }
  224. /**
  225. * Validation method. Empty implementation. Derived classes may override
  226. * this function.
  227. * @abstract
  228. *
  229. * @param array $record The record that holds the value for this
  230. * attribute. If an error occurs, the error will
  231. * be stored in the 'atkerror' field of the record.
  232. * @param String $mode The mode for which should be validated ("add" or
  233. * "update")
  234. */
  235. function validate(&$record, $mode)
  236. {
  237. }
  238. /**
  239. * Check if the relation is empty
  240. * @param array $record The record to check
  241. * @return boolean true if a destination record is present. False if not.
  242. */
  243. function isEmpty($record)
  244. {
  245. if ($this->createDestination() && isset($record[$this->fieldName()][$this->m_destInstance->primaryKeyField()]))
  246. {
  247. return empty($record[$this->fieldName()][$this->m_destInstance->primaryKeyField()]);
  248. }
  249. else if ($this->createDestination() && isset($record[$this->fieldName()]))
  250. {
  251. return empty($record[$this->fieldName()]);
  252. }
  253. return true; // always empty if error.
  254. }
  255. /**
  256. * Retrieve the searchmodes supported by the relation.
  257. * @return array A list of supported searchmodes.
  258. */
  259. function getSearchModes()
  260. {
  261. // exact match and substring search should be supported by any database.
  262. // (the LIKE function is ANSI standard SQL, and both substring and wildcard
  263. // searches can be implemented using LIKE)
  264. // Possible values
  265. //"regexp","exact","substring", "wildcard","greaterthan","greaterthanequal","lessthan","lessthanequal"
  266. return array("exact");
  267. }
  268. /**
  269. * Get the searchmode for nested/child attributes.
  270. *
  271. * @param string|array $searchmode searchmode
  272. * @param string $childname the child attribute's name
  273. * @return string|array the child searchmode
  274. */
  275. protected function getChildSearchMode($searchmode, $childname)
  276. {
  277. if (is_array($searchmode) && isset($searchmode[$childname]))
  278. return $searchmode[$childname];
  279. return $searchmode;
  280. }
  281. /**
  282. * Since most relations do not store anything in a field, the default
  283. * fieldtype for relations is "". Exceptions (like the many2oone relation,
  284. * which stores a foreign key) can implement their own dbFieldType().
  285. * @abstract
  286. * @return String
  287. */
  288. function dbFieldType()
  289. {
  290. return "";
  291. }
  292. /**
  293. * Returns the condition (SQL) that should be used when we want to join a relation's
  294. * owner entity with the parent entity.
  295. *
  296. * @param atkQuery $query The query object
  297. * @param string $tablename The tablename
  298. * @param string $fieldalias
  299. * @return String SQL string for joining the owner with the destination.
  300. * Defaults to false.
  301. */
  302. function getJoinCondition(&$query, $tablename="",$fieldalias="")
  303. {
  304. return false;
  305. }
  306. /**
  307. * Returns an instance of the entity that the relation points to.
  308. * @return atkEntity The entity that this relation points to, or
  309. * NULL if the destination is not valid.
  310. */
  311. function &getDestination()
  312. {
  313. if ($this->createDestination())
  314. {
  315. return $this->m_destInstance;
  316. }
  317. return NULL;
  318. }
  319. /**
  320. * Attempts to get a translated label which can be used when composing an "add" link
  321. *
  322. * @return String Localised "add" label
  323. */
  324. function getAddLabel()
  325. {
  326. // Try to get a translation for link_fieldname_add (or if not found, a translation for link_destination_add)
  327. $keys = array("link_" . $this->fieldName() . "_add", "link_" . getEntityType($this->m_destination) . "_add");
  328. $label = atktext($keys, getEntityModule($this->m_destination), "", "", "", true);
  329. // If translation not found, then use a concatenation of translations of the destination and the word "add" as default
  330. if ($label=="")
  331. $label = atktext(getEntityType($this->m_destination), getEntityModule($this->m_destination)) . " " . strtolower(atktext("add", "atk"));
  332. // Return the translation
  333. return $label;
  334. }
  335. /**
  336. * Parses the destination filter
  337. *
  338. * @param string $destFilter filter to parse
  339. * @param array $record the current record
  340. * @return $filter string filter.
  341. */
  342. function parseFilter($destFilter,$record)
  343. {
  344. if($destFilter!="")
  345. {
  346. $parser = new Adapto_StringParser($destFilter);
  347. return $parser->parse($record);
  348. }
  349. return "";
  350. }
  351. }
  352. ?>