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