PageRenderTime 76ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 1ms

/data/Link.php

https://github.com/item/sugarcrm_dev
PHP | 1090 lines | 817 code | 136 blank | 137 comment | 181 complexity | 2a8ceda7d409ac0e0580259e0c92505d MD5 | raw file
Possible License(s): AGPL-3.0, LGPL-2.1
  1. <?php
  2. if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
  3. /*********************************************************************************
  4. * SugarCRM is a customer relationship management program developed by
  5. * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
  6. *
  7. * This program is free software; you can redistribute it and/or modify it under
  8. * the terms of the GNU Affero General Public License version 3 as published by the
  9. * Free Software Foundation with the addition of the following permission added
  10. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  11. * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
  12. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  13. *
  14. * This program is distributed in the hope that it will be useful, but WITHOUT
  15. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  17. * details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License along with
  20. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  21. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  22. * 02110-1301 USA.
  23. *
  24. * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
  25. * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
  26. *
  27. * The interactive user interfaces in modified source and object code versions
  28. * of this program must display Appropriate Legal Notices, as required under
  29. * Section 5 of the GNU Affero General Public License version 3.
  30. *
  31. * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
  32. * these Appropriate Legal Notices must retain the display of the "Powered by
  33. * SugarCRM" logo. If the display of the logo is not reasonably feasible for
  34. * technical reasons, the Appropriate Legal Notices must display the words
  35. * "Powered by SugarCRM".
  36. ********************************************************************************/
  37. /*********************************************************************************
  38. * Description: Defines the base class for new data type, Relationship, methods in the class will
  39. * be used to manipulate relationship between object instances.
  40. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
  41. * All Rights Reserved.
  42. * Contributor(s): ______________________________________..
  43. ********************************************************************************/
  44. class Link {
  45. /* Private variables.*/
  46. var $_log;
  47. var $_relationship_name; //relationship this attribute is tied to.
  48. var $_bean; //stores a copy of the bean.
  49. var $_relationship= '';
  50. var $_bean_table_name;
  51. var $_bean_key_name='id';
  52. private $relationship_fields = array();
  53. var $_db;
  54. var $_swap_sides = false;
  55. var $_rhs_key_override = false;
  56. var $_bean_filter_field = '';
  57. //if set to true role column will not be added to the filter criteria.
  58. var $ignore_role_filter=false;
  59. //if set to true distinct clause will be added to the select list.
  60. var $add_distinct=false;
  61. //value of this variable dictates the action to be taken when a duplicate relationship record is found.
  62. //1-ignore,2-update,3-delete.
  63. //var $when_dup_relationship_found=2; // deprecated - only used by Queues, which is also no longer used
  64. // a value for duplicate variable is stored by the _relatinship_exists method.
  65. var $_duplicate_key;
  66. var $_duplicate_where;
  67. /* Parameters:
  68. * $_rel_name: use this relationship key.
  69. * $_bean: reference of the bean that instantiated this class.
  70. * $_fieldDef: vardef entry for the field.
  71. * $_table_name: optional, fetch from the bean's table name property.
  72. * $_key_name: optional, name of the primary key column for _table_name
  73. */
  74. function Link($_rel_name, &$_bean, $fieldDef, $_table_name='', $_key_name=''){
  75. $GLOBALS['log']->debug("Link Constructor, relationship name: ".$_rel_name);
  76. $GLOBALS['log']->debug("Link Constructor, Table name: ".$_table_name);
  77. $GLOBALS['log']->debug("Link Constructor, Key name: ".$_key_name);
  78. //_pp(func_get_args());
  79. $this->_relationship_name=$_rel_name;
  80. $this->relationship_fields = (!empty($fieldDef['rel_fields']))?$fieldDef['rel_fields']: array();
  81. $this->_bean=&$_bean;
  82. $this->_relationship=new Relationship();
  83. //$this->_relationship->retrieve_by_string_fields(array('relationship_name'=>$this->_relationship_name));
  84. $this->_relationship->retrieve_by_name($this->_relationship_name);
  85. $this->_db = DBManagerFactory::getInstance();
  86. //Following behavior is tied to a property(ignore_role) value in the vardef. It alters the values of 2 properties, ignore_role_filter and add_distinct.
  87. //the property values can be altered again before any requests are made.
  88. if (!empty($fieldDef) && is_array($fieldDef)) {
  89. if (array_key_exists('ignore_role', $fieldDef)) {
  90. if ($fieldDef['ignore_role'] == true) {
  91. $this->ignore_role_filter=true;
  92. $this->add_distinct=true;
  93. }
  94. }
  95. }
  96. $this->_bean_table_name=(!empty($_table_name)) ? $_table_name : $_bean->table_name;
  97. if (!empty($key_name)) {
  98. $this->_bean_key_name=$_key_name;
  99. } else {
  100. if ($this->_relationship->lhs_table != $this->_relationship->rhs_table) {
  101. if ($_bean->table_name == $this->_relationship->lhs_table)
  102. $this->_bean_key_name=$this->_relationship->lhs_key;
  103. if ($_bean->table_name == $this->_relationship->rhs_table)
  104. $this->_bean_key_name=$this->_relationship->rhs_key;
  105. }
  106. }
  107. if ($this->_relationship->lhs_table == $this->_relationship->rhs_table && isset($fieldDef['side']) && $fieldDef['side'] == 'right'){
  108. $this->_swap_sides = true;
  109. }
  110. if (!empty($fieldDef['rhs_key_override'])) {
  111. $this->_rhs_key_override = true;
  112. }
  113. if (!empty($fieldDef['bean_filter_field'])) {
  114. $this->_bean_filter_field = $fieldDef['bean_filter_field'];
  115. }
  116. //default to id if not set.
  117. if (empty($this->_bean_key_name))$this->_bean_key_name='id';
  118. $GLOBALS['log']->debug("Link Constructor, _bean_table_name: ".$this->_bean_table_name);
  119. $GLOBALS['log']->debug("Link Constructor, _bean_key_name: ".$this->_bean_key_name);
  120. if (!empty($this->_relationship->id)) $GLOBALS['log']->debug("Link Constructor, relationship record found.");
  121. else $GLOBALS['log']->debug("Link Constructor, No relationship record.") ;
  122. }
  123. /* This method will return the following based on cardinality of the relationship.
  124. * # one-to-many, many-to-many: empty array if not data is found else array of keys.
  125. * # if many-to-many and $role set to true : empty array if not data is found else
  126. * array of array which contain id+other fields.
  127. * # many-to-one, one-to-one: null if no linked data is found, else key value.
  128. *
  129. * For a self referencing relationship the function will behave as if the user is trying
  130. * to access the child records. To get to the parent records use the getParent() method.
  131. */
  132. function get($role = false) {
  133. if($role){
  134. $role_field = $this->_get_link_table_role_field($this->_relationship_name);
  135. if($role_field !== false){
  136. $query = $this->getQuery(false, array(),0, "", false, "", $role_field);
  137. }else{
  138. return array();
  139. }
  140. }else{
  141. $query = $this->getQuery();
  142. }
  143. $result = $this->_db->query($query, true);
  144. $list = Array();
  145. while($row = $this->_db->fetchByAssoc($result))
  146. {
  147. if($role){
  148. $list[] = $row;
  149. }else{
  150. $list[] = $row['id'];
  151. }
  152. }
  153. return $list;
  154. }
  155. function getRelatedTableName() {
  156. $bean_is_lhs=$this->_get_bean_position();
  157. if (!isset($bean_is_lhs)) {
  158. $GLOBALS['log']->debug("Invalid relationship parameters. Exiting..");
  159. return null;
  160. }
  161. if ($bean_is_lhs) {
  162. return $this->_relationship->rhs_table;
  163. } else {
  164. return $this->_relationship->lhs_table;
  165. }
  166. }
  167. function getRelatedModuleName() {
  168. $bean_is_lhs=$this->_get_bean_position();
  169. if (!isset($bean_is_lhs)) {
  170. $GLOBALS['log']->debug("Invalid relationship parameters. Exiting..");
  171. return null;
  172. }
  173. if ($bean_is_lhs) {
  174. return $this->_relationship->rhs_module;
  175. } else {
  176. return $this->_relationship->lhs_module;
  177. }
  178. }
  179. function getRelatedFields(){
  180. return $this->relationship_fields;
  181. }
  182. function getRelatedField($name){
  183. return (!empty($this->relationship_fields[$name]))? $this->relationship_fields[$name]: null;
  184. }
  185. function getRelationshipObject() {
  186. return $this->_relationship;
  187. }
  188. function _get_bean_position() {
  189. //current beans module and table are on the left side or the right side.
  190. $position = false;
  191. if ($this->_relationship->lhs_table == $this->_bean_table_name && $this->_relationship->lhs_key == $this->_bean_key_name) {
  192. $position = true;
  193. }
  194. if ($this->_relationship->rhs_table == $this->_bean_table_name && $this->_relationship->rhs_key == $this->_bean_key_name) {
  195. $position = false;
  196. }
  197. if($this->_swap_sides){
  198. return !$position;
  199. }
  200. return $position;
  201. }
  202. function _is_self_relationship() {
  203. if ($this->_relationship->lhs_table == $this->_relationship->rhs_table) {
  204. return true;
  205. }
  206. return false;
  207. }
  208. function getJoin($params, $return_array =false)
  209. {
  210. $join_type= ' INNER JOIN ';
  211. if(isset($params['join_type'])){
  212. $join_type = $params['join_type'];
  213. }
  214. $id = -1;
  215. $join = '';
  216. $bean_is_lhs=$this->_get_bean_position();
  217. if ($this->_relationship->relationship_type=='one-to-one' or $this->_relationship->relationship_type=='many-to-one' or
  218. ($this->_relationship->relationship_type=='one-to-many' && !$bean_is_lhs))
  219. {
  220. if ($bean_is_lhs) {
  221. $table = $this->_relationship->rhs_table;
  222. $key = $this->_relationship->rhs_key;
  223. // check right table alias
  224. $other_table = (empty($params['left_join_table_alias']) ? $this->_relationship->lhs_table : $params['left_join_table_alias']);
  225. $other_key = $this->_relationship->lhs_key;
  226. } else {
  227. $key = $this->_relationship->lhs_key;
  228. $table = $this->_relationship->lhs_table;
  229. if ( ! empty($params['join_table_alias']))
  230. {
  231. $table_with_alias = $table. " ".$params['join_table_alias'];
  232. $table = $params['join_table_alias'];
  233. }
  234. $other_table = (empty($params['right_join_table_alias']) ? $this->_relationship->rhs_table : $params['right_join_table_alias']);
  235. $other_key = $this->_relationship->rhs_key;
  236. }
  237. $join = $join_type . ' '. $table_with_alias . " ON\n".$table.'.'.$key.'= '.$other_table.'.'.$other_key ." AND ". $table.".deleted=0\n";
  238. }
  239. if ($this->_relationship->relationship_type == 'one-to-many' && $bean_is_lhs) {
  240. $table = $this->_relationship->rhs_table;
  241. $key = $this->_relationship->rhs_key;
  242. $other_table = (empty($params['left_join_table_alias']) ? $this->_relationship->lhs_table : $params['left_join_table_alias']);
  243. $other_key = $this->_relationship->lhs_key;
  244. if ( ! empty($params['join_table_alias']))
  245. {
  246. $table_with_alias = $table. " ".$params['join_table_alias'];
  247. $table = $params['join_table_alias'];
  248. }
  249. $join = $join_type . ' '.$table_with_alias . " ON\n".$table.'.'.$key.'= '.$other_table.'.'.$other_key ." AND ". $table.".deleted=0\n";
  250. }
  251. if ($this->_relationship->relationship_type=='many-to-many' )
  252. {
  253. if ( ! empty($params['join_table_alias']))
  254. {
  255. $table_with_alias = $this->_relationship->join_table. " ".$params['join_table_alias'];
  256. $table = $params['join_table_alias'];
  257. $rel_table_with_alias =
  258. $this->_relationship->join_table. " ".
  259. $params['join_table_link_alias'];
  260. $rel_table = $params['join_table_link_alias'];
  261. }
  262. if ( $bean_is_lhs )
  263. {
  264. $other_table = (empty($params['left_join_table_alias']) ? $this->_relationship->lhs_table : $params['left_join_table_alias']);
  265. $join .= $join_type . ' '.$rel_table_with_alias.' ON '.$other_table.".".$this->_relationship->lhs_key."=".$rel_table.".".$this->_relationship->join_key_lhs." AND ".$rel_table.".deleted=0\n";
  266. } else
  267. {
  268. $other_table = (empty($params['right_join_table_alias']) ? $this->_relationship->rhs_table : $params['right_join_table_alias']);
  269. $join .= $join_type . ' '.$rel_table_with_alias.' ON '.$other_table.".".$this->_relationship->rhs_key."=".$rel_table.".".$this->_relationship->join_key_rhs." AND ".$rel_table.".deleted=0\n";
  270. }
  271. if (!empty($this->_relationship->relationship_role_column) && !$this->ignore_role_filter)
  272. {
  273. $join.=" AND ".$rel_table.'.'.$this->_relationship->relationship_role_column;
  274. //role column value.
  275. if (empty($this->_relationship->relationship_role_column_value))
  276. {
  277. $join.=' IS NULL';
  278. } else {
  279. $join.= "='".$this->_relationship->relationship_role_column_value."'";
  280. }
  281. $join.= "\n";
  282. }
  283. if ( ! empty($params['join_table_alias']))
  284. {
  285. if ( $bean_is_lhs )
  286. {
  287. $table_with_alias = $this->_relationship->rhs_table. " ".$params['join_table_alias'];
  288. } else {
  289. $table_with_alias = $this->_relationship->lhs_table. " ".$params['join_table_alias'];
  290. }
  291. $table = $params['join_table_alias'];
  292. }
  293. if ( $bean_is_lhs )
  294. {
  295. if($this->_rhs_key_override){
  296. $join .= $join_type . ' '.$table_with_alias.' ON '.$table.".".$this->_relationship->rhs_key."=".$rel_table.".".$this->_relationship->join_key_rhs." AND ".$table.".deleted=0";
  297. }else{
  298. $join .= $join_type . ' '.$table_with_alias.' ON '.$table.".".$this->_relationship->lhs_key."=".$rel_table.".".$this->_relationship->join_key_rhs." AND ".$table.".deleted=0";
  299. }
  300. } else {
  301. $join .= $join_type . ' '.$table_with_alias.' ON '.$table.".".$this->_relationship->rhs_key."=".$rel_table.".".$this->_relationship->join_key_lhs." AND ".$table.".deleted=0";
  302. }
  303. $join.= "\n";
  304. }
  305. if($return_array){
  306. $ret_arr = array();
  307. $ret_arr['join'] = $join;
  308. $ret_arr['type'] = $this->_relationship->relationship_type;
  309. if ( $bean_is_lhs ){
  310. $ret_arr['rel_key'] = $this->_relationship->join_key_rhs;
  311. }else{
  312. $ret_arr['rel_key'] = $this->_relationship->join_key_lhs;
  313. }
  314. return $ret_arr;
  315. }
  316. return $join;
  317. }
  318. function _add_deleted_clause($deleted=0,$add_and='',$prefix='') {
  319. if (!empty($prefix)) $prefix.='.';
  320. if (!empty($add_and)) $add_and=' '.$add_and.' ';
  321. if ($deleted==0) return $add_and.$prefix.'deleted=0';
  322. if ($deleted==1) return $add_and.$prefix.'deleted=1';
  323. else return '';
  324. }
  325. function _add_optional_where_clause($optional_array, $add_and='',$prefix='') {
  326. if (!empty($prefix)) $prefix.='.';
  327. if (!empty($add_and)) $add_and=' '.$add_and.' ';
  328. if(!empty($optional_array)){
  329. return $add_and.$prefix."".$optional_array['lhs_field']."".$optional_array['operator']."'".$optional_array['rhs_value']."'";
  330. }
  331. return '';
  332. //end function _add_optional_where_clause
  333. }
  334. function getQuery($return_as_array=false, $sort_array = array(),$deleted=0, $optional_where="", $return_join = false, $bean_filter="", $role="", $for_subpanels = false){
  335. $select='';
  336. $from='';
  337. $join = '';
  338. $where='';
  339. $join_tables = array();
  340. $bean_is_lhs=$this->_get_bean_position();
  341. if (!isset($bean_is_lhs)) {
  342. $GLOBALS['log']->debug("Invalid relationship parameters. Exiting..");
  343. return null;
  344. }
  345. if (empty($bean_filter)) {
  346. if(!empty($this->_bean_filter_field)){
  347. $bean_filter_field = $this->_bean_filter_field;
  348. $bean_filter="= '".$this->_bean->$bean_filter_field."'";
  349. }else{
  350. $bean_filter="= '".$this->_bean->id."'";
  351. }
  352. }
  353. $GLOBALS['log']->debug("getQuery, Bean is LHS: ".$bean_is_lhs);
  354. $GLOBALS['log']->debug("getQuery, Relationship type=".$this->_relationship->relationship_type);
  355. $GLOBALS['log']->debug("getQuery, Relationship role column name=".$this->_relationship->relationship_role_column);
  356. if ($this->_relationship->relationship_type=='one-to-one' or $this->_relationship->relationship_type=='many-to-one' or
  357. ($this->_relationship->relationship_type=='one-to-many' && !$bean_is_lhs)) {
  358. $GLOBALS['log']->debug("Processing one-to-one,many-to-one,one-to-many.");
  359. if ($this->add_distinct) {
  360. $select='SELECT DISTINCT id';
  361. } else {
  362. $select='SELECT id';
  363. }
  364. if ($bean_is_lhs) {
  365. $from= 'FROM '.$this->_relationship->rhs_table;
  366. $where='WHERE '.$this->_relationship->rhs_table.'.'.$this->_relationship->rhs_key.$bean_filter;
  367. if (!empty($this->_relationship->relationship_role_column) && !$this->ignore_role_filter) {
  368. $where.=" AND ".$this->_relationship->rhs_table.'.'.$this->_relationship->relationship_role_column;
  369. //role column value.
  370. if (empty($this->_relationship->relationship_role_column_value)) {
  371. $where.=' IS NULL';
  372. } else {
  373. $where.= "='".$this->_relationship->relationship_role_column_value."'";
  374. }
  375. }
  376. //add deleted clause - but not if we're dealing with a Custom table which will lack the 'deleted' field
  377. if(substr_count($this->_relationship->rhs_table, '_cstm') == 0)
  378. $where.=$this->_add_deleted_clause($deleted,'AND',$this->_relationship->rhs_table );
  379. if($optional_where!=""){
  380. //process optional where
  381. $where.=$this->_add_optional_where_clause($optional_where,'AND');
  382. }
  383. }
  384. else {
  385. $from= 'FROM '.$this->_relationship->lhs_table;
  386. $where='WHERE '.$this->_relationship->lhs_table.'.'.$this->_relationship->lhs_key."= '".$this->_bean->{$this->_relationship->rhs_key}."'";
  387. //added deleted clause.
  388. $where.=$this->_add_deleted_clause($deleted,'AND', $this->_relationship->lhs_table);
  389. if($optional_where!=""){
  390. //process optional where
  391. $where.=$this->_add_optional_where_clause($optional_where,'AND');
  392. }
  393. }
  394. }
  395. if ($this->_relationship->relationship_type == 'one-to-many' && $bean_is_lhs) {
  396. $GLOBALS['log']->debug("Processing one-to-many.");
  397. if ($this->add_distinct) {
  398. $select='SELECT DISTINCT '.$this->_relationship->rhs_table.'.id';
  399. } else {
  400. $select='SELECT '.$this->_relationship->rhs_table.'.id';
  401. }
  402. $from= 'FROM '.$this->_relationship->rhs_table;
  403. $where='WHERE '.$this->_relationship->rhs_table.'.'.$this->_relationship->rhs_key.$bean_filter;
  404. if (!empty($this->_relationship->relationship_role_column) && !$this->ignore_role_filter) {
  405. $where.=" AND ".$this->_relationship->rhs_table.'.'.$this->_relationship->relationship_role_column;
  406. //role column value.
  407. if (empty($this->_relationship->relationship_role_column_value)) {
  408. $where.=' IS NULL';
  409. } else {
  410. $where.= "='".$this->_relationship->relationship_role_column_value."'";
  411. }
  412. }
  413. //add deleted clause - but not if we're dealing with a Custom table which will lack the 'deleted' field
  414. if(substr_count($this->_relationship->rhs_table, '_cstm') == 0)
  415. $where.=$this->_add_deleted_clause($deleted,'AND',$this->_relationship->rhs_table);
  416. if($optional_where!=""){
  417. //process optional where
  418. $where.=$this->_add_optional_where_clause($optional_where,'AND');
  419. }
  420. }
  421. if ($this->_relationship->relationship_type=='many-to-many' ) {
  422. $GLOBALS['log']->debug("Processing many-to-many.");
  423. $swap = !$for_subpanels && $this->_swap_sides;
  424. if (($bean_is_lhs && !$swap) || (!$bean_is_lhs && $swap)) {
  425. if ($this->add_distinct) {
  426. $select="SELECT DISTINCT ".$this->_relationship->rhs_table.".id";
  427. } else {
  428. $select="SELECT ".$this->_relationship->rhs_table.".id";
  429. }
  430. $from= 'FROM '.$this->_relationship->rhs_table;
  431. $subjoin=' INNER JOIN '.$this->_relationship->join_table.' ON ('.$this->_relationship->rhs_table.".".$this->_relationship->rhs_key."=".$this->_relationship->join_table.".".$this->_relationship->join_key_rhs." AND ".$this->_relationship->join_table.".".$this->_relationship->join_key_lhs.$bean_filter;
  432. $join_tables[] = $this->_relationship->join_table;
  433. if (!empty($this->_relationship->relationship_role_column) && !$this->ignore_role_filter) {
  434. $subjoin.=" AND ".$this->_relationship->join_table.'.'.$this->_relationship->relationship_role_column;
  435. //role column value.
  436. if (empty($this->_relationship->relationship_role_column_value)) {
  437. $subjoin.=' IS NULL';
  438. } else {
  439. $subjoin.= "='".$this->_relationship->relationship_role_column_value."'";
  440. }
  441. }
  442. $subjoin.=')';
  443. $join .= $subjoin;
  444. $from .= $subjoin;
  445. //add deleted clause.
  446. if ($deleted == 0 or $deleted==1) {
  447. $where.=' WHERE '.$this->_add_deleted_clause($deleted,'',$this->_relationship->join_table).$this->_add_deleted_clause($deleted,'AND',$this->_relationship->rhs_table);
  448. }
  449. if($optional_where!=""){
  450. //process optional where
  451. $where.=$this->_add_optional_where_clause($optional_where,'AND', $this->_relationship->rhs_table);
  452. }
  453. }
  454. else {
  455. if ($this->add_distinct) {
  456. $select="SELECT DISTINCT ".$this->_relationship->lhs_table.".id";
  457. } else {
  458. $select="SELECT ".$this->_relationship->lhs_table.".id";
  459. }
  460. $from= 'FROM '.$this->_relationship->lhs_table;
  461. $subjoin=' INNER JOIN '.$this->_relationship->join_table.' ON ('.$this->_relationship->lhs_table.".".$this->_relationship->lhs_key."=".$this->_relationship->join_table.".".$this->_relationship->join_key_lhs." AND ".$this->_relationship->join_table.".".$this->_relationship->join_key_rhs.$bean_filter;
  462. $join_tables[] = $this->_relationship->join_table;
  463. if (!empty($this->_relationship->relationship_role_column) && !$this->ignore_role_filter) {
  464. $subjoin.=" AND ".$this->_relationship->relationship_role_column;
  465. //role column value.
  466. if (empty($this->_relationship->relationship_role_column_value)) {
  467. $subjoin.=' IS NULL';
  468. } else {
  469. $subjoin.= "='".$this->_relationship->relationship_role_column_value."'";
  470. }
  471. }
  472. $subjoin.=')';
  473. $join .= $subjoin;
  474. $from .= $subjoin;
  475. //add deleted clause.
  476. if ($deleted == 0 or $deleted==1) {
  477. $where.=' WHERE '.$this->_add_deleted_clause($deleted,'',$this->_relationship->join_table).$this->_add_deleted_clause($deleted,'AND',$this->_relationship->lhs_table);
  478. }
  479. if($optional_where!=""){
  480. //process optional where
  481. $where.=$this->_add_optional_where_clause($optional_where,'AND', $this->_relationship->lhs_table);
  482. }
  483. }
  484. if (!empty($role)){
  485. $select.=", ".$this->_relationship->join_table.".".$role;
  486. }
  487. }
  488. if ($return_as_array) {
  489. $query_as_array['select']=$select;
  490. $query_as_array['from']=$from;
  491. $query_as_array['where']=$where;
  492. if($return_join){
  493. $query_as_array['join'] = $join;
  494. $query_as_array['join_tables'] = $join_tables;
  495. }
  496. return $query_as_array;
  497. }
  498. else {
  499. $query = $select.' '.$from.' '.$where;
  500. $GLOBALS['log']->debug("Link Query=".$query);
  501. return $query;
  502. }
  503. }
  504. function getBeans($template, $sort_array = array(), $begin_index = 0, $end_index = -1, $deleted=0, $optional_where="") {
  505. $query = $this->getQuery(false,array(), $deleted, $optional_where); //get array of IDs
  506. return $this->_bean->build_related_list($query, $template);
  507. }
  508. function _add_one_to_many_table_based($key,$bean_is_lhs) {
  509. if ($bean_is_lhs) {
  510. $set_key_value=$this->_bean->id;
  511. $where_key_value=$key;
  512. }
  513. else {
  514. $set_key_value=$key;
  515. $where_key_value=$this->_bean->id;
  516. }
  517. $query= 'UPDATE '.$this->_relationship->rhs_table;
  518. $query.=' SET '.$this->_relationship->rhs_table.'.'.$this->_relationship->rhs_key."='".$set_key_value."'";
  519. //add role column to the query.
  520. if (!empty($this->_relationship->relationship_role_column)) {
  521. $query.=' ,'.$this->_relationship->relationship_role_column."='".$this->_relationship->relationship_role_column_value."'";
  522. }
  523. $query.=' WHERE '.$this->_relationship->rhs_table.".id='".$where_key_value."'";
  524. $GLOBALS['log']->debug("Relationship Query ".$query);
  525. $result=$this->_db->query($query, true);
  526. }
  527. /* handles many to one*/
  528. function _add_many_to_one_bean_based($key) {
  529. //make a copy of this bean to avoid recursion.
  530. $bean=new $this->_bean->object_name;
  531. $bean->retrieve($this->_bean->id);
  532. $bean->{$this->_relationship->lhs_key}=$key;
  533. //set relationship role.
  534. if (!empty($this->_relationship->relationship_role_column)) {
  535. $bean->{$this->_relationship->relationship_role_column}=$this->_relationship->relationship_role_column_value;
  536. }
  537. $bean->save();
  538. }
  539. /* use this function to create link between 2 objects
  540. * 1:1 will be treated like 1 to many.
  541. * todo handle self referencing relationships
  542. * the function also allows for setting of values for additional field in the table being
  543. * updated to save the relationship, in case of many-to-many relationships this would be the join table.
  544. * the values should be passed as key value pairs with column name as the key name and column value as key value.
  545. */
  546. function add($rel_keys,$additional_values=array()) {
  547. if (!isset($rel_keys) or empty($rel_keys)) {
  548. $GLOBALS['log']->debug("Link.add, Null key passed, no-op, returning... ");
  549. return;
  550. }
  551. //check to ensure that we do in fact have an id on the bean.
  552. if(empty($this->_bean->id)){
  553. $GLOBALS['log']->debug("Link.add, No id on the bean... ");
  554. return;
  555. }
  556. if (!is_array($rel_keys)) {
  557. $keys[]=$rel_keys;
  558. } else {
  559. $keys=$rel_keys;
  560. }
  561. $bean_is_lhs=$this->_get_bean_position();
  562. if (!isset($bean_is_lhs)) {
  563. $GLOBALS['log']->debug("Invalid relationship parameters. Exiting..");
  564. return null;
  565. }
  566. //if multiple keys are passed then check for unsupported relationship types.
  567. if (count($keys) > 1) {
  568. if (($this->_relationship->relationship_type == 'one-to-one')
  569. or ($this->_relationship->relationship_type == 'one-to-many' and !$bean_is_lhs)
  570. or ($this->_relationship->relationship_type == 'many-to-one')) {
  571. $GLOBALS['log']->error("Invalid parameters passed to function, the relationship does not support addition of multiple records.");
  572. return;
  573. }
  574. }
  575. $GLOBALS['log']->debug("Relationship type = {$this->_relationship->relationship_type}");
  576. foreach($keys as $key) {
  577. //fetch the related record using the key and update.
  578. if ($this->_relationship->relationship_type=='one-to-one' or $this->_relationship->relationship_type == 'one-to-many') {
  579. $this->_add_one_to_many_table_based($key,$bean_is_lhs);
  580. }
  581. //updates the bean passed to the instance....
  582. //todo remove this case.
  583. if ($this->_relationship->relationship_type=='many-to-one') {
  584. $this->_add_many_to_one_bean_based($key);
  585. }
  586. //insert record in the link table.
  587. if ($this->_relationship->relationship_type=='many-to-many' ) {
  588. //Swap the bean positions for self relationships not coming from subpanels.
  589. //such as one-to-many relationship fields generated in studio/MB
  590. $swap = !isset($_REQUEST['subpanel_id']) && $this->_swap_sides;
  591. //add keys from the 2 tables to the additional keys array..
  592. if (($bean_is_lhs && !$swap) || (!$bean_is_lhs && $swap)) {
  593. $additional_values[$this->_relationship->join_key_lhs]=$this->_bean->id;
  594. $additional_values[$this->_relationship->join_key_rhs]=$key;
  595. } else {
  596. $additional_values[$this->_relationship->join_key_rhs]=$this->_bean->id;
  597. $additional_values[$this->_relationship->join_key_lhs]=$key;
  598. }
  599. //add the role condition.
  600. if (!empty($this->_relationship->relationship_role_column) && !empty($this->_relationship->relationship_role_column_value)) {
  601. $additional_values[$this->_relationship->relationship_role_column]=$this->_relationship->relationship_role_column_value;
  602. }
  603. //add deleted condition.
  604. $additional_values['deleted']=0;
  605. $this->_add_many_to_many($additional_values);
  606. //reverse will be set to true only for self-referencing many-to-many relationships.
  607. if ($this->_is_self_relationship() && !empty($GLOBALS['dictionary'][$this->_relationship_name]) &&
  608. !empty($GLOBALS['dictionary'][$this->_relationship_name]['true_relationship_type']) &&
  609. $GLOBALS['dictionary'][$this->_relationship_name]['true_relationship_type'] == 'many-to-many' ||
  610. (!empty($this->_relationship->reverse) && $this->_relationship->reverse == true )){
  611. //swap key values;
  612. $temp=$additional_values[$this->_relationship->join_key_lhs];
  613. $additional_values[$this->_relationship->join_key_lhs]=$additional_values[$this->_relationship->join_key_rhs];
  614. $additional_values[$this->_relationship->join_key_rhs]=$temp;
  615. $this->_add_many_to_many($additional_values);
  616. }
  617. }
  618. $custom_logic_arguments = array();
  619. $custom_reverse_arguments = array();
  620. $custom_logic_arguments['related_id'] = $key;
  621. $custom_logic_arguments['id'] = $this->_bean->id;
  622. $custom_reverse_arguments['related_id'] = $this->_bean->id;
  623. $custom_reverse_arguments['id'] = $key;
  624. if($bean_is_lhs) {
  625. $custom_logic_arguments['module'] = $this->_relationship->lhs_module;
  626. $custom_logic_arguments['related_module'] = $this->_relationship->rhs_module;
  627. $custom_reverse_arguments['module'] = $this->_relationship->rhs_module;
  628. $custom_reverse_arguments['related_module'] = $this->_relationship->lhs_module;
  629. } else {
  630. $custom_logic_arguments['related_module'] = $this->_relationship->lhs_module;
  631. $custom_reverse_arguments['related_module'] = $this->_relationship->rhs_module;
  632. $custom_logic_arguments['module'] = $this->_relationship->rhs_module;
  633. $custom_reverse_arguments['module'] = $this->_relationship->lhs_module;
  634. }
  635. /**** CALL IT FROM THE MAIN BEAN FIRST ********/
  636. $this->_bean->call_custom_logic('after_relationship_add', $custom_logic_arguments);
  637. /**** NOW WE HAVE TO CALL THE LOGIC HOOK THE OTHER WAY SINCE IT TAKES TWO FOR A RELATIONSHIP****/
  638. global $beanList;
  639. $class = $beanList[$custom_logic_arguments['related_module']];
  640. if ( !empty($class) ) {
  641. $rbean = new $class();
  642. $rbean->id = $key;
  643. $rbean->call_custom_logic('after_relationship_add', $custom_reverse_arguments);
  644. }
  645. }
  646. }
  647. function _add_many_to_many($add_values) {
  648. //add date modified.
  649. $add_values['date_modified']= gmdate($GLOBALS['timedate']->get_db_date_time_format(), time());
  650. //check whether duplicate exist or not.
  651. if ($this->relationship_exists($this->_relationship->join_table,$add_values)) {
  652. /* switch($this->when_dup_relationship_found) {
  653. case 1: //do nothing.
  654. $GLOBALS['log']->debug("Executing default option, no action.");
  655. break;
  656. case 3: //delete the record first, then create a new entry.
  657. $this->_delete_row($this->_relationship->join_table,$this->_duplicate_key);
  658. $this->_insert_row($add_values);
  659. break;
  660. default:
  661. case 2: //update the record.
  662. */ $this->_update_row($add_values,$this->_relationship->join_table,$this->_duplicate_where);
  663. /* break;
  664. }*/
  665. } else {
  666. $this->_insert_row($add_values);
  667. }
  668. }
  669. function _delete_row($table_name,$key) {
  670. $query='UPDATE '.$table_name." SET deleted=1, date_modified=" .gmdate($GLOBALS['timedate']->get_db_date_time_format(), time())."' WHERE id='".$key."'";
  671. $GLOBALS['log']->debug("Relationship Delete Statement :".$query);
  672. $result=$this->_db->query($query, true);
  673. }
  674. function _update_row(&$value_array,$table_name,$where) {
  675. $query='UPDATE '.$table_name.' SET ';
  676. $delimiter='';
  677. foreach ($value_array as $key=>$value) {
  678. $query.=$delimiter.$key."='".$value."' ";
  679. $delimiter=",";
  680. }
  681. $query.=$where;
  682. $GLOBALS['log']->debug("Relationship Update Statement :".$query);
  683. $result=$this->_db->query($query, true);
  684. }
  685. function _insert_row(&$value_array) {
  686. //add key column
  687. $value_array['id']= create_guid();
  688. $columns_list='';
  689. $values_list='';
  690. $delimiter='';
  691. foreach ($value_array as $key=>$value) {
  692. $columns_list.=$delimiter.$key;
  693. $values_list .=$delimiter."'".$value."'";
  694. $delimiter=",";
  695. }
  696. $insert_string='INSERT into '.$this->_relationship->join_table.' ('.$columns_list.') VALUES ('.$values_list.')';
  697. $GLOBALS['log']->debug("Relationship Insert String :".$insert_string);
  698. $result=$this->_db->query($insert_string, true);
  699. }
  700. /* this method operates on all related record, takes action based on cardinality of the relationship.
  701. * one-to-one, one-to-many: update the rhs table's parent id with null
  702. * many-to-one: update the lhs table's parent-id with null.
  703. * many-to-many: delete rows from the link table. related table must have delted and date_modified column.
  704. * if related_is is null, the methods assumes that the parent bean (whose id is passed) is being deleted.
  705. * if both id and related_id are passed the metod unlinks a single relationship.
  706. * parameters: id of the bean being deleted.
  707. *
  708. */
  709. function delete($id,$related_id='') {
  710. $GLOBALS['log']->debug(sprintf("delete called with these parameter values. id=%s, related_id=%s",$id,$related_id));
  711. $_relationship=&$this->_relationship;
  712. $_bean=&$this->_bean;
  713. $bean_is_lhs=$this->_get_bean_position();
  714. if (!isset($bean_is_lhs)) {
  715. $GLOBALS['log']->debug("Invalid relationship parameters. Exiting..");
  716. return null;
  717. }
  718. if ($_relationship->relationship_type=='one-to-many' or $_relationship->relationship_type=='one-to-one' ) {
  719. if ($bean_is_lhs) {
  720. //update rhs_table set rhs_key = null, relation_column_name = null where rhs_key= this_bean_id
  721. $query='UPDATE '.$_relationship->rhs_table.' SET '.$_relationship->rhs_key."=NULL, date_modified='".gmdate($GLOBALS['timedate']->get_db_date_time_format(), time())."'";
  722. if (!empty($_relationship->relationship_role_column) && !empty($_relationship->relationship_role_column_value)) {
  723. $query.=','.$_relationship->relationship_role_column."= NULL ";
  724. $query.=' WHERE '.$_relationship->relationship_role_column."= '".$_relationship->relationship_role_column_value."' AND ";
  725. } else {
  726. $query.=' WHERE ';
  727. }
  728. $query.=$_relationship->rhs_key."= '".$id."' ";
  729. //restrict to one row if related_id is passed.
  730. if (!empty($related_id)) {
  731. $query.=" AND ".$_relationship->rhs_table.".id='".$related_id."'";
  732. }
  733. }
  734. else {
  735. //do nothing because the row that stores the relationship keys is being deleted.
  736. //todo log an error message here.
  737. //if this is the case and related_id is passed then log a message asking the user
  738. //to clear the relationship using the bean.
  739. }
  740. }
  741. if ($_relationship->relationship_type=='many-to-one') {
  742. //do nothing because the row that stores the relationship keys is being deleted.
  743. //todo log an error message here.
  744. //if this is the case and related_id is passed then log a message asking the user
  745. //to clear the relationship using the bean.
  746. }
  747. if ($_relationship->relationship_type=='many-to-many' ) {
  748. $use_bean_is_lhs = isset($_REQUEST['ajaxSubpanel']) || $this->_swap_sides !== true;
  749. $query='UPDATE '.$_relationship->join_table." SET deleted=1, date_modified='".gmdate($GLOBALS['timedate']->get_db_date_time_format(), time())."'";
  750. if ($bean_is_lhs && $use_bean_is_lhs) {
  751. if (!empty($this->_relationship->reverse) && ($this->_relationship->reverse == true or $this->_relationship->reverse == 1)){
  752. if (empty($related_id)) {
  753. $query.=" WHERE (".$_relationship->join_key_lhs."= '". $id ."' or ".$_relationship->join_key_rhs."='". $id ."')" ;
  754. } else {
  755. $query.=" WHERE (".$_relationship->join_key_lhs."= '". $id ."' AND ".$_relationship->join_key_rhs."='".$related_id."') OR (".$_relationship->join_key_rhs."='". $id ."' AND ".$_relationship->join_key_lhs."='".$related_id."')";
  756. }
  757. } else {
  758. if (empty($related_id)) {
  759. $query.=" WHERE ".$_relationship->join_key_lhs."= '". $id ."'";
  760. } else {
  761. $query.=" WHERE ".$_relationship->join_key_lhs."= '". $id ."' AND ".$_relationship->join_key_rhs."= '". $related_id."'";
  762. }
  763. }
  764. } else {
  765. if (!empty($this->_relationship->reverse) && ($this->_relationship->reverse == true or $this->_relationship->reverse == 1)) {
  766. if (empty($related_id)) {
  767. $query.=" WHERE (".$_relationship->join_key_rhs."= '". $id ."' or ".$_relationship->join_key_lhs."='". $id ."')" ;
  768. } else {
  769. $query.=" WHERE (".$_relationship->join_key_rhs."= '". $id ."' AND ".$_relationship->join_key_lhs."='".$related_id."') OR (".$_relationship->join_key_lhs."='". $id ."' AND ".$_relationship->join_key_rhs."='".$related_id."')";
  770. }
  771. } else {
  772. if (empty($related_id)) {
  773. $query.=" WHERE ".$_relationship->join_key_rhs."= '". $id ."'" ;
  774. } else {
  775. $query.=" WHERE ".$_relationship->join_key_rhs."= '". $id ."' AND ".$_relationship->join_key_lhs."= '". $related_id."'" ;
  776. }
  777. }
  778. if (!empty($_relationship->relationship_role_column) && !empty($_relationship->relationship_role_column_value)) {
  779. $query.=' AND '.$_relationship->relationship_role_column."= '".$_relationship->relationship_role_column_value."'";
  780. }
  781. }
  782. }
  783. //if query string is not empty execute it.
  784. if (isset($query)) {
  785. $GLOBALS['log']->debug('Link.Delete:Delete Query: '.$query);
  786. $this->_db->query($query,true);
  787. }
  788. $custom_logic_arguments = array();
  789. $custom_logic_arguments['id'] = $id;
  790. $custom_logic_arguments['related_id'] = $related_id;
  791. $custom_reverse_arguments = array();
  792. $custom_reverse_arguments['related_id'] = $id;
  793. $custom_reverse_arguments['id'] = $related_id;
  794. if($bean_is_lhs) {
  795. $custom_logic_arguments['module'] = $this->_relationship->lhs_module;
  796. $custom_logic_arguments['related_module'] = $this->_relationship->rhs_module;
  797. $custom_reverse_arguments['module'] = $this->_relationship->lhs_module;
  798. $custom_reverse_arguments['related_module'] = $this->_relationship->rhs_module;
  799. } else {
  800. $custom_logic_arguments['module'] = $this->_relationship->rhs_module;
  801. $custom_logic_arguments['related_module'] = $this->_relationship->lhs_module;
  802. $custom_reverse_arguments['module'] = $this->_relationship->lhs_module;
  803. $custom_reverse_arguments['related_module'] = $this->_relationship->rhs_module;
  804. }
  805. $this->_bean->call_custom_logic('after_relationship_delete', $custom_logic_arguments);
  806. //NOW THE REVERSE WAY SINCE A RELATIONSHIP TAKES TWO
  807. global $beanList;
  808. $class = $beanList[$custom_logic_arguments['related_module']];
  809. if ( !empty($class) ) {
  810. $rbean = new $class();
  811. $rbean->id = $id;
  812. $rbean->call_custom_logic('after_relationship_delete', $custom_reverse_arguments);
  813. }
  814. }
  815. function relationship_exists($table_name, $join_key_values) {
  816. //find the key values for the table.
  817. $dup_keys=$this->_get_alternate_key_fields($table_name);
  818. if (empty($dup_keys)) {
  819. $GLOBALS['log']->debug("No alternate key define, skipping duplicate check..");
  820. return false;
  821. }
  822. $delimiter='';
  823. $this->_duplicate_where=' WHERE ';
  824. foreach ($dup_keys as $field) {
  825. //look for key in $join_key_values, if found add to filter criteria else abort duplicate checking.
  826. if (isset($join_key_values[$field])) {
  827. $this->_duplicate_where .= $delimiter.' '.$field."='".$join_key_values[$field]."'";
  828. $delimiter='AND';
  829. } else {
  830. $GLOBALS['log']->error('Duplicate checking aborted, Please supply a value for this column '.$field);
  831. return false;
  832. }
  833. }
  834. //add deleted check.
  835. $this->_duplicate_where .= $delimiter.' deleted=0';
  836. $query='SELECT id FROM '.$table_name.$this->_duplicate_where;
  837. $GLOBALS['log']->debug("relationship_exists query(".$query.')');
  838. $result=$this->_db->query($query, true);
  839. $row = $this->_db->fetchByAssoc($result);
  840. if ($row == null) {
  841. return false;
  842. }
  843. else {
  844. $this->_duplicate_key=$row['id'];
  845. return true;
  846. }
  847. }
  848. /* returns array of keys for duplicate checking, first check for an index of type alternate_key, if not found searches for
  849. * primary key.
  850. *
  851. */
  852. function _get_alternate_key_fields($table_name) {
  853. $alternateKey=null;
  854. $indices=Link::_get_link_table_definition($table_name,'indices');
  855. if (!empty($indices)) {
  856. foreach ($indices as $index) {
  857. foreach ($index as $key=>$value) {
  858. if ($key=='type' && $value=='alternate_key') {
  859. return $index['fields'];
  860. }
  861. }
  862. }
  863. }
  864. $relationships=Link::_get_link_table_definition($table_name,'relationships');
  865. if (!empty($relationships)) {//bug 32623, when the relationship is built in old version, there is no alternate_key. we have to use join_key_lhs and join_key_lhs.
  866. if(!empty($relationships[$this->_relationship_name]) && !empty($relationships[$this->_relationship_name]['join_key_lhs']) && !empty($relationships[$this->_relationship_name]['join_key_rhs'])) {
  867. return array($relationships[$this->_relationship_name]['join_key_lhs'], $relationships[$this->_relationship_name]['join_key_rhs']);
  868. }
  869. }
  870. }
  871. /*
  872. */
  873. function _get_link_table_definition($table_name,$def_name) {
  874. global $dictionary;
  875. include_once('modules/TableDictionary.php');
  876. // first check to see if already loaded - assumes hasn't changed in the meantime
  877. if (isset($dictionary[$table_name][$def_name]))
  878. {
  879. return $dictionary[$table_name][$def_name];
  880. }
  881. else {
  882. if (isset($dictionary[$this->_relationship_name][$def_name])) {
  883. return ($dictionary[$this->_relationship_name][$def_name]);
  884. }
  885. // custom metadata is found in custom/metadata (naturally) and the naming follows the convention $relationship_name_c, and $relationship_name = $table_name$locations = array( 'metadata/' , 'custom/metadata/' ) ;
  886. $relationshipName = preg_replace( '/_c$/' , '' , $table_name ) ;
  887. $locations = array ( 'metadata/' , 'custom/metadata/' ) ;
  888. foreach ( $locations as $basepath )
  889. {
  890. $path = $basepath . $relationshipName . 'MetaData.php' ;
  891. if (file_exists($path))
  892. {
  893. include($path);
  894. if (isset($dictionary[$relationshipName][$def_name])) {
  895. return $dictionary[$relationshipName][$def_name];
  896. }
  897. }
  898. }
  899. // couldn't find the metadata for the table in either the standard or custom locations
  900. $GLOBALS['log']->debug('Error fetching field defs for join table '.$table_name);
  901. return null;
  902. }
  903. }
  904. /*
  905. * Return the name of the role field for the passed many to many table.
  906. * if there is no role filed : return false
  907. */
  908. function _get_link_table_role_field($table_name) {
  909. $varDefs = $this->_get_link_table_definition($table_name, 'fields');
  910. $role_field = false;
  911. if(!empty($varDefs)){
  912. $role_field = '';
  913. foreach($varDefs as $v){
  914. if(strpos($v['name'], '_role') !== false){
  915. $role_field = $v['name'];
  916. }
  917. }
  918. }
  919. return $role_field;
  920. }
  921. }
  922. ?>