PageRenderTime 65ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/SugarCE-6.5.4/SugarCE-Full-6.5.4/data/Link.php

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