]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - data/Relationships/SugarRelationship.php
Release 6.5.0
[Github/sugarcrm.git] / data / Relationships / SugarRelationship.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-2012 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38
39 global $dictionary;
40 //Load all relationship metadata
41 include_once("modules/TableDictionary.php");
42 require_once("data/BeanFactory.php");
43
44
45 define('REL_LHS','LHS');
46 define('REL_RHS','RHS');
47 define('REL_BOTH','BOTH_SIDES');
48 define('REL_MANY_MANY', 'many-to-many');
49 define('REL_ONE_MANY', 'one-to-many');
50 define('REL_ONE_ONE', 'one-to-one');
51 /**
52  * A relationship is between two modules.
53  * It contains at least two links.
54  * Each link represents a connection from one record to the records linked in this relationship.
55  * Links have a context(focus) bean while relationships do not.
56  * @api
57  */
58 abstract class SugarRelationship
59 {
60     protected $def;
61     protected $lhsLink;
62     protected $rhsLink;
63     protected $ignore_role_filter = false;
64     protected $self_referencing = false; //A relationship is self referencing when LHS module = RHS Module
65
66     protected static $beansToResave = array();
67
68     public abstract function add($lhs, $rhs, $additionalFields = array());
69
70     /**
71      * @abstract
72      * @param  $lhs SugarBean
73      * @param  $rhs SugarBean
74      * @return void
75      */
76     public abstract function remove($lhs, $rhs);
77
78     /**
79      * @abstract
80      * @param $link Link2 loads the rows for this relationship that match the given link
81      * @return void
82      */
83     public abstract function load($link, $params = array());
84
85     /**
86      * Gets the query to load a link.
87      * This is currently public, but should prob be made protected later.
88      * See Link2->getQuery
89      * @abstract
90      * @param  $link Link Object to get query for.
91      * @return string|array query used to load this relationship
92      */
93     public abstract function getQuery($link, $params = array());
94
95     /**
96      * @abstract
97      * @param Link2 $link
98      * @return string|array the query to join against the related modules table for the given link.
99      */
100     public abstract function getJoin($link);
101
102     /**
103      * @abstract
104      * @param SugarBean $lhs
105      * @param SugarBean $rhs
106      * @return bool
107      */
108     public abstract function relationship_exists($lhs, $rhs);
109
110     /**
111      * @abstract
112      * @return string name of the table for this relationship
113      */
114     public abstract function getRelationshipTable();
115
116     /**
117      * @param  $link Link2 removes all the beans associated with this link from the relationship
118      * @return void
119      */
120     public function removeAll($link)
121     {
122         $focus = $link->getFocus();
123         $related = $link->getBeans();
124         foreach($related as $relBean)
125         {
126             if (empty($relBean->id)) {
127                 continue;
128             }
129
130             if ($link->getSide() == REL_LHS)
131                 $this->remove($focus, $relBean);
132             else
133                 $this->remove($relBean, $focus);
134         }
135     }
136
137     /**
138      * @param $rowID id of SugarBean to remove from the relationship
139      * @return void
140      */
141     public function removeById($rowID){
142         $this->removeRow(array("id" => $rowID));
143     }
144
145     /**
146      * @return string name of right hand side module.
147      */
148     public function getRHSModule()
149     {
150         return $this->def['rhs_module'];
151     }
152
153     /**
154      * @return string name of left hand side module.
155      */
156     public function getLHSModule()
157     {
158         return $this->def['lhs_module'];
159     }
160
161     /**
162      * @return String left link in relationship.
163      */
164     public function getLHSLink()
165     {
166         return $this->lhsLink;
167     }
168
169     /**
170      * @return String right link in relationship.
171      */
172     public function getRHSLink()
173     {
174         return $this->rhsLink;
175     }
176
177     /**
178      * @return array names of fields stored on the relationship
179      */
180     public function getFields()
181     {
182         return isset($this->def['fields']) ? $this->def['fields'] : array();
183     }
184
185     /**
186      * @param array $row values to be inserted into the relationship
187      * @return bool|void null if new row was inserted and true if an exesting row was updated
188      */
189     protected function addRow($row)
190     {
191         $existing = $this->checkExisting($row);
192         if (!empty($existing)) //Update the existing row, overriding the values with those passed in
193             return $this->updateRow($existing['id'], array_merge($existing, $row));
194
195         $values = array();
196         foreach($this->getFields() as  $def)
197         {
198             $field = $def['name'];
199             if (isset($row[$field]))
200             {
201                 $values[$field] = "'{$row[$field]}'";
202             }
203         }
204         $columns = implode(',', array_keys($values));
205         $values = implode(',', $values);
206         if (!empty($values))
207         {
208             $query = "INSERT INTO {$this->getRelationshipTable()} ($columns) VALUES ($values)";
209             DBManagerFactory::getInstance()->query($query);
210         }
211     }
212
213     /**
214      * @param $id id of row to update
215      * @param $values values to insert into row
216      * @return resource result of update satatement
217      */
218     protected function updateRow($id, $values)
219     {
220         $newVals = array();
221         //Unset the ID since we are using it to update the row
222         if (isset($values['id'])) unset($values['id']);
223         foreach($values as $field => $val)
224         {
225             $newVals[] = "$field='$val'";
226         }
227
228         $newVals = implode(",",$newVals);
229
230         $query = "UPDATE {$this->getRelationshipTable()} set $newVals WHERE id='$id'";
231
232         return DBManagerFactory::getInstance()->query($query);
233     }
234
235     /**
236      * Removes one or more rows from the relationship table
237      * @param $where array of field=>value pairs to match
238      * @return bool|resource
239      */
240     protected function removeRow($where)
241     {
242         if (empty($where))
243             return false;
244
245         $date_modified = TimeDate::getInstance()->getNow()->asDb();
246         $stringSets = array();
247         foreach ($where as $field => $val)
248         {
249             $stringSets[] = "$field = '$val'";
250         }
251         $whereString = "WHERE " . implode(" AND ", $stringSets);
252
253         $query = "UPDATE {$this->getRelationshipTable()} set deleted=1 , date_modified = '$date_modified' $whereString";
254
255         return DBManagerFactory::getInstance()->query($query);
256
257     }
258
259     /**
260      * Checks for an existing row who's keys match the one passed in.
261      * @param  $row
262      * @return array|bool returns false if now row is found, otherwise the row is returned
263      */
264     protected function checkExisting($row)
265     {
266         $leftIDName = $this->def['join_key_lhs'];
267         $rightIDName = $this->def['join_key_rhs'];
268         if (empty($row[$leftIDName]) ||  empty($row[$rightIDName]))
269             return false;
270
271         $leftID = $row[$leftIDName];
272         $rightID = $row[$rightIDName];
273         //Check the relationship role as well
274         $roleCheck = $this->getRoleWhere();
275
276         $query = "SELECT * FROM {$this->getRelationshipTable()} WHERE $leftIDName='$leftID' AND $rightIDName='$rightID' $roleCheck AND deleted=0";
277
278         $db = DBManagerFactory::getInstance();
279         $result = $db->query($query);
280         $row = $db->fetchByAssoc($result);
281         if (!empty($row))
282         {
283             return $row;
284         } else{
285             return false;
286         }
287     }
288
289     /**
290      * Gets the relationship role column check for the where clause
291      * @param string $table
292      * @return string
293      */
294     protected function getRoleWhere($table = "", $ignore_role_filter = false)
295     {
296         $ignore_role_filter = $ignore_role_filter || $this->ignore_role_filter;
297         $roleCheck = "";
298         if (empty ($table))
299             $table = $this->getRelationshipTable();
300         if (!empty($this->def['relationship_role_column']) && !empty($this->def["relationship_role_column_value"]) && !$ignore_role_filter )
301         {
302             if (empty($table))
303                 $roleCheck = " AND $this->relationship_role_column";
304             else
305                 $roleCheck = " AND $table.{$this->relationship_role_column}";
306             //role column value.
307             if (empty($this->def['relationship_role_column_value']))
308             {
309                 $roleCheck.=' IS NULL';
310             } else {
311                 $roleCheck.= " = '$this->relationship_role_column_value'";
312             }
313         }
314         return $roleCheck;
315     }
316
317     /**
318      * @param SugarBean $focus base bean the hooks is triggered from
319      * @param SugarBean $related bean being added/removed/updated from relationship
320      * @param string $link_name name of link being triggerd
321      * @return array base arguments to pass to relationship logic hooks
322      */
323     protected function getCustomLogicArguments($focus, $related, $link_name)
324     {
325         $custom_logic_arguments = array();
326         $custom_logic_arguments['id'] = $focus->id;
327         $custom_logic_arguments['related_id'] = $related->id;
328         $custom_logic_arguments['module'] = $focus->module_dir;
329         $custom_logic_arguments['related_module'] = $related->module_dir;
330         $custom_logic_arguments['link'] = $link_name;
331         $custom_logic_arguments['relationship'] = $this->name;
332
333         return $custom_logic_arguments;
334     }
335
336     /**
337      * Call the after add logic hook for a given link
338      * @param  SugarBean $focus base bean the hooks is triggered from
339      * @param  SugarBean $related bean being added/removed/updated from relationship
340      * @param string $link_name name of link being triggerd
341      * @return void
342      */
343     protected function callAfterAdd($focus, $related, $link_name="")
344     {
345         $custom_logic_arguments = $this->getCustomLogicArguments($focus, $related, $link_name);
346         $focus->call_custom_logic('after_relationship_add', $custom_logic_arguments);
347     }
348
349     /**
350      * @param  SugarBean $focus
351      * @param  SugarBean $related
352      * @param string $link_name
353      * @return void
354      */
355     protected function callAfterDelete($focus, $related, $link_name="")
356     {
357         $custom_logic_arguments = $this->getCustomLogicArguments($focus, $related, $link_name);
358         $focus->call_custom_logic('after_relationship_delete', $custom_logic_arguments);
359     }
360
361     /**
362      * @param $optional_array clause to add to the where query when populating this relationship. It should be in the
363      * @param string $add_and
364      * @param string $prefix
365      * @return string
366      */
367     protected function getOptionalWhereClause($optional_array) {
368         //lhs_field, operator, and rhs_value must be set in optional_array
369         foreach(array("lhs_field", "operator", "rhs_value") as $required){
370             if (empty($optional_array[$required]))
371                 return "";
372         }
373
374         return $optional_array['lhs_field']."".$optional_array['operator']."'".$optional_array['rhs_value']."'";
375     }
376
377     /**
378      * Adds a realted Bean to the list to be resaved along with the current bean.
379      * @static
380      * @param  SugarBean $bean
381      * @return void
382      */
383     public static function addToResaveList($bean)
384     {
385         if (!isset(self::$beansToResave[$bean->module_dir]))
386         {
387             self::$beansToResave[$bean->module_dir] = array();
388         }
389         self::$beansToResave[$bean->module_dir][$bean->id] = $bean;
390     }
391
392     /**
393      *
394      * @static
395      * @return void
396      */
397     public static function resaveRelatedBeans()
398     {
399         $GLOBALS['resavingRelatedBeans'] = true;
400
401         //Resave any bean not currently in the middle of a save operation
402         foreach(self::$beansToResave as $module => $beans)
403         {
404             foreach ($beans as $bean)
405             {
406                 if (empty($bean->deleted) && empty($bean->in_save))
407                 {
408                     $bean->save();
409                 }
410             }
411         }
412
413         $GLOBALS['resavingRelatedBeans'] = false;
414
415         //Reset the list of beans that will need to be resaved
416         self::$beansToResave = array();
417     }
418
419     /**
420      * @return bool true if the relationship is a flex / parent relationship
421      */
422     public function isParentRelationship()
423     {
424         //check role fields to see if this is a parent (flex relate) relationship
425         if(!empty($this->def["relationship_role_column"]) && !empty($this->def["relationship_role_column_value"])
426            && $this->def["relationship_role_column"] == "parent_type" && $this->def['rhs_key'] == "parent_id")
427         {
428             return true;
429         }
430         return false;
431     }
432
433     public function __get($name)
434     {
435         if (isset($this->def[$name]))
436             return $this->def[$name];
437
438         switch($name)
439         {
440             case "relationship_type":
441                 return $this->type;
442             case 'relationship_name':
443                 return $this->name;
444             case "lhs_module":
445                 return $this->getLHSModule();
446             case "rhs_module":
447                 return $this->getRHSModule();
448             case "lhs_table" :
449                 isset($this->def['lhs_table']) ? $this->def['lhs_table'] : "";
450             case "rhs_table" :
451                 isset($this->def['rhs_table']) ? $this->def['rhs_table'] : "";
452             case "list_fields":
453                 return array('lhs_table', 'lhs_key', 'rhs_module', 'rhs_table', 'rhs_key', 'relationship_type');
454         }
455
456         if (isset($this->$name))
457             return $this->$name;
458
459         return null;
460     }
461 }