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