One2MBeanRelationship   D
last analyzed

Complexity

Total Complexity 89

Size/Duplication

Total Lines 354
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 48.31%
Metric Value
dl 0
loc 354
ccs 86
cts 178
cp 0.4831
rs 4.8717
wmc 89
lcom 1
cbo 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C add() 0 55 14
A updateLinks() 0 8 3
A updateFields() 0 16 4
C remove() 0 30 8
B load() 0 34 6
F getQuery() 0 58 15
F getJoin() 0 37 11
F getSubpanelQuery() 0 63 22
A relationship_exists() 0 7 3
A getRelationshipTable() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like One2MBeanRelationship often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use One2MBeanRelationship, and based on these observations, apply Extract Interface, too.

1
<?php
2 1
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-2013 SugarCRM Inc.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
42 1
require_once("data/Relationships/One2MRelationship.php");
43
44
/**
45
 * Represents a one to many relationship that is table based.
46
 * @api
47
 */
48
class One2MBeanRelationship extends One2MRelationship
49
{
50
    //Type is read in sugarbean to determine query construction
51
    var $type = "one-to-many";
52
53 109
    public function __construct($def)
54
    {
55 109
        parent::__construct($def);
56 109
    }
57
58
    /**
59
     * @param  $lhs SugarBean left side bean to add to the relationship.
60
     * @param  $rhs SugarBean right side bean to add to the relationship.
61
     * @param  $additionalFields key=>value pairs of fields to save on the relationship
62
     * @return boolean true if successful
63
     */
64
    public function add($lhs, $rhs, $additionalFields = array())
65
    {
66
        // test to see if the relationship exist if the relationship between the two beans
67
        // exist then we just fail out with false as we don't want to re-trigger this
68
        // the save and such as it causes problems with the related() in sugarlogic
69
        if($this->relationship_exists($lhs, $rhs) && !empty($GLOBALS['resavingRelatedBeans'])) return false;
70
71
        $lhsLinkName = $this->lhsLink;
72
        $rhsLinkName = $this->rhsLink;
73
74
        //Since this is bean based, we know updating the RHS's field will overwrite any old value,
75
        //But we need to use delete to make sure custom logic is called correctly
76
        if ($rhs->load_relationship($rhsLinkName))
77
        {
78
            $oldLink = $rhs->$rhsLinkName;
79
            $prevRelated = $oldLink->getBeans(null);
80
            foreach($prevRelated as $oldLHS)
81
            {
82
                if ($oldLHS->id != $lhs->id)
83
                    $this->remove($oldLHS, $rhs, false);
84
            }
85
        }
86
87
        //Make sure we load the current relationship state to the LHS link
88
        if ((isset($lhs->$lhsLinkName) && is_a($lhs->$lhsLinkName, "Link2")) || $lhs->load_relationship($lhsLinkName)) {
89
            $lhs->$lhsLinkName->load();
90
        }
91
92
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
93
        {
94
            $this->callBeforeAdd($lhs, $rhs);
95
            $this->callBeforeAdd($rhs, $lhs);
96
        }
97
98
        $this->updateFields($lhs, $rhs, $additionalFields);
99
100
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
101
        {
102
            //Need to call save to update the bean as the relationship is saved on the main table
103
            //We don't want to create a save loop though, so make sure we aren't already in the middle of saving this bean
104
            SugarRelationship::addToResaveList($rhs);
105
106
            $this->updateLinks($lhs, $lhsLinkName, $rhs, $rhsLinkName);
107
108
            $this->callAfterAdd($lhs, $rhs);
109
            $this->callAfterAdd($rhs, $lhs);
110
        }
111
112
        //One2MBean relationships require that the RHS bean be saved or else the relationship will not be saved.
113
        //If we aren't already in a relationship save, intitiate a save now.
114
        if (empty($GLOBALS['resavingRelatedBeans']))
115
            SugarRelationship::resaveRelatedBeans();
116
        
117
        return true;
118
    }
119
120
    protected function updateLinks($lhs, $lhsLinkName, $rhs, $rhsLinkName)
121
    {
122
        if (isset($lhs->$lhsLinkName))
123
            $lhs->$lhsLinkName->addBean($rhs);
124
        //RHS only has one bean ever, so we don't need to preload the relationship
125
        if (isset($rhs->$rhsLinkName))
126
            $rhs->$rhsLinkName->beans = array($lhs->id => $lhs);
127
    }
128
129
    protected function updateFields($lhs, $rhs, $additionalFields)
130
    {
131
        //Now update the RHS bean's ID field
132
        $rhsID = $this->def['rhs_key'];
133
        $rhs->$rhsID = $lhs->id;
134
        foreach($additionalFields as $field => $val)
135
        {
136
            $rhs->$field = $val;
137
        }
138
        //Update role fields
139
        if(!empty($this->def["relationship_role_column"]) && !empty($this->def["relationship_role_column_value"]))
140
        {
141
            $roleField = $this->def["relationship_role_column"];
142
            $rhs->$roleField = $this->def["relationship_role_column_value"];
143
        }
144
    }
145
146 7
    public function remove($lhs, $rhs, $save = true)
147
    {
148 7
        $rhsID = $this->def['rhs_key'];
149
150
        //If this relationship has already been removed, we can just return
151 7
        if ($rhs->$rhsID != $lhs->id)
152
            return false;
153
154 7
        $rhs->$rhsID = '';
155
156 7
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
157
        {
158 7
            $this->callBeforeDelete($lhs, $rhs);
159 7
            $this->callBeforeDelete($rhs, $lhs);
160
        }
161
162 7
        if ($save && !$rhs->deleted)
163
        {
164 4
            $rhs->in_relationship_update = TRUE;
165 4
            $rhs->save();
166
        }
167
168 7
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
169
        {
170 7
            $this->callAfterDelete($lhs, $rhs);
171 7
            $this->callAfterDelete($rhs, $lhs);
172
        }
173
174 7
        return true;
175
    }
176
177
    /**
178
     * @param  $link Link2 loads the relationship for this link.
179
     * @return void
180
     */
181 89
    public function load($link, $params = array())
182
    {
183 89
        $relatedModule = $link->getSide() == REL_LHS ? $this->def['rhs_module'] : $this->def['lhs_module'];
184 89
        $rows = array();
185
        //The related bean ID is stored on the RHS table.
186
        //If the link is RHS, just grab it from the focus.
187 89
        if ($link->getSide() == REL_RHS)
188
        {
189 83
            $rhsID = $this->def['rhs_key'];
190 83
            $id = $link->getFocus()->$rhsID;
191 83
            if (!empty($id))
192
            {
193 83
                $rows[$id] = array('id' => $id);
194
            }
195
        }
196
        else //If the link is LHS, we need to query to get the full list and load all the beans.
197
        {
198 31
            $db = DBManagerFactory::getInstance();
199 31
            $query = $this->getQuery($link, $params);
200 31
            if (empty($query))
201
            {
202
                $GLOBALS['log']->fatal("query for {$this->name} was empty when loading from   {$this->lhsLink}\n");
0 ignored issues
show
Documentation introduced by
The property name does not exist on object<One2MBeanRelationship>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
203
                return array("rows" => array());
204
            }
205 31
            $result = $db->query($query);
206 31
            while ($row = $db->fetchByAssoc($result, FALSE))
207
            {
208 10
                $id = $row['id'];
209 10
                $rows[$id] = $row;
210
            }
211
        }
212
213 89
        return array("rows" => $rows);
214
    }
215
216 34
    public function getQuery($link, $params = array())
217
    {
218
        //There was an old signature with $return_as_array as the second parameter. We should respect this if $params is true
219 34
        if ($params === true) {
220 3
            $params = array("return_as_array" => true);
221
        }
222
223 34
        if ($link->getSide() == REL_RHS) {
224
            return false;
225
        } else {
226 34
            $lhsKey = $this->def['lhs_key'];
227 34
            $rhsTable = $this->def['rhs_table'];
228 34
            $rhsTableKey = "{$rhsTable}.{$this->def['rhs_key']}";
229 34
            $relatedSeed = BeanFactory::getBean($this->getRHSModule());
230 34
            $deleted = !empty($params['deleted']) ? 1 : 0;
231 34
            $where = "WHERE $rhsTableKey = '{$link->getFocus()->$lhsKey}' AND {$rhsTable}.deleted=$deleted";
232 34
            $order_by = '';
233
234
            //Check for role column
235 34
            if (!empty($this->def["relationship_role_column"]) && !empty($this->def["relationship_role_column_value"])) {
236 11
                $roleField = $this->def["relationship_role_column"];
237 11
                $roleValue = $this->def["relationship_role_column_value"];
238 11
                $where .= " AND $rhsTable.$roleField = '$roleValue'";
239
            }
240
241
            //Add any optional where clause
242 34
            if (!empty($params['where'])) {
243
                $add_where = is_string($params['where']) ? $params['where'] : "$rhsTable." . $this->getOptionalWhereClause($params['where']);
244
                if (!empty($add_where))
245
                    $where .= " AND $add_where";
246
            }
247
248
            //Add any optional order clauses
249 34
            if (!empty($params['order_by'])) {
250
                $order_by = $relatedSeed->process_order_by($params['order_by']);
251
            }
252
253 34
            $from = $this->def['rhs_table'];
254
255 34
            if (empty($params['return_as_array'])) {
256
                //Limit is not compatible with return_as_array
257 31
                $query = "SELECT id FROM $from $where";
258 31
                if (!empty($order_by)) $query .= ' ORDER BY '.$order_by;
259 31
                if (!empty($params['limit']) && $params['limit'] > 0) {
260
                    $offset = isset($params['offset']) ? $params['offset'] : 0;
261
                    $query = DBManagerFactory::getInstance()->limitQuery($query, $offset, $params['limit'], false, "", false);
262
                }
263 31
                return $query;
264
            } else {
265
                return array(
266 3
                    'select' => "SELECT {$this->def['rhs_table']}.id",
267 3
                    'from' => "FROM {$this->def['rhs_table']}",
268 3
                    'where' => $where,
269 3
                    'order_by' => $order_by
270
                );
271
            }
272
        }
273
    }
274
275 35
    public function getJoin($link, $params = array(), $return_array = false)
276
    {
277 35
        $linkIsLHS = $link->getSide() == REL_LHS;
278 35
        $startingTable = (empty($params['left_join_table_alias']) ? $this->def['lhs_table'] : $params['left_join_table_alias']);
279 35
        if (!$linkIsLHS)
280 34
            $startingTable = (empty($params['right_join_table_alias']) ? $this->def['rhs_table'] : $params['right_join_table_alias']);
281 35
        $startingKey = $linkIsLHS ? $this->def['lhs_key'] : $this->def['rhs_key'];
282 35
        $targetTable = $linkIsLHS ? $this->def['rhs_table'] : $this->def['lhs_table'];
283 35
        $targetTableWithAlias = $targetTable;
284 35
        $targetKey = $linkIsLHS ? $this->def['rhs_key'] : $this->def['lhs_key'];
285 35
        $join_type= isset($params['join_type']) ? $params['join_type'] : ' INNER JOIN ';
286 35
        $join = '';
287
288
        //Set up any table aliases required
289 35
        if ( ! empty($params['join_table_alias']))
290
        {
291 35
            $targetTableWithAlias = $targetTable. " ".$params['join_table_alias'];
292 35
            $targetTable = $params['join_table_alias'];
293
        }
294
295
        //First join the relationship table
296 35
        $join .= "$join_type $targetTableWithAlias ON $startingTable.$startingKey=$targetTable.$targetKey AND $targetTable.deleted=0\n"
297
        //Next add any role filters
298 35
               . $this->getRoleWhere(($linkIsLHS) ? $targetTable : $startingTable) . "\n";
299
300 35
        if($return_array){
301
            return array(
302 35
                'join' => $join,
303 35
                'type' => $this->type,
304 35
                'rel_key' => $targetKey,
305 35
                'join_tables' => array($targetTable),
306 35
                'where' => "",
307 35
                'select' => "$targetTable.id",
308
            );
309
        }
310
        return $join;
311
    }
312
313
    public function getSubpanelQuery($link, $params = array(), $return_array = false)
314
    {
315
316
        $linkIsLHS = $link->getSide() == REL_RHS;
317
        $startingTable = (empty($params['left_join_table_alias']) ? $this->def['lhs_table'] : $params['left_join_table_alias']);
318
        if (!$linkIsLHS)
319
            $startingTable = (empty($params['right_join_table_alias']) ? $this->def['rhs_table'] : $params['right_join_table_alias']);
320
        $startingKey = $linkIsLHS ? $this->def['lhs_key'] : $this->def['rhs_key'];
321
        $targetTable = $linkIsLHS ? $this->def['rhs_table'] : $this->def['lhs_table'];
322
        $targetKey = $linkIsLHS ? $this->def['rhs_key'] : $this->def['lhs_key'];
323
        $join_type= isset($params['join_type']) ? $params['join_type'] : ' INNER JOIN ';
324
        $query = '';
325
326
        $alias = empty($params['join_table_alias']) ? "{$link->name}_rel": $params['join_table_alias'];
327
        $alias = $GLOBALS['db']->getValidDBName($alias, false, 'alias');
328
329
        $tableInRoleFilter = "";
330
        if (
331
            (
332
                $startingTable == "meetings"
333
                || $startingTable == "notes"
334
                || $startingTable == "tasks"
335
                || $startingTable == "calls"
336
                || $startingTable == "emails"
337
            )
338
            &&
339
            (
340
                $targetTable == "meetings"
341
                || $targetTable == "notes"
342
                || $targetTable == "tasks"
343
                || $targetTable == "calls"
344
            )
345
            && substr($alias, 0, 12 + strlen($targetTable)) == $targetTable . "_activities_"
346
        )
347
        {
348
            $tableInRoleFilter = $linkIsLHS ? $alias : $startingTable;
349
        }
350
        
351
        //Set up any table aliases required
352
        $targetTableWithAlias = "$targetTable $alias";
353
        $targetTable = $alias;
354
355
        $query .= "$join_type $targetTableWithAlias ON $startingTable.$startingKey=$targetTable.$targetKey AND $targetTable.deleted=0\n"
356
        //Next add any role filters
357
               . $this->getRoleWhere($tableInRoleFilter) . "\n";
358
359
        if (!empty($params['return_as_array'])) {
360
            $return_array = true;
361
        }
362
363
        if($return_array){
364
            return array(
365
                'join' => $query,
366
                'type' => $this->type,
367
                'rel_key' => $targetKey,
368
                'join_tables' => array($targetTable),
369
                'where' => "WHERE $startingTable.$startingKey='{$link->focus->id}'",
370
                'select' => " ",
371
            );
372
        }
373
        return $query;
374
375
    }
376
377
    /**
378
     * Check to see if the relationship already exist.
379
     *
380
     * If it does return true otherwise return false
381
     *
382
     * @param SugarBean $lhs        Left hand side of the relationship
383
     * @param SugarBean $rhs        Right hand side of the relationship
384
     * @return boolean
385
     */
386
    public function relationship_exists($lhs, $rhs)
387
    {
388
        // we need the key that is stored on the rhs to compare tok
389
        $lhsIDName = $this->def['rhs_key'];
390
391
        return (isset($rhs->fetched_row[$lhsIDName]) && $rhs->$lhsIDName == $rhs->fetched_row[$lhsIDName] && $rhs->$lhsIDName == $lhs->id);
392
    }
393
394 3
    public function getRelationshipTable()
395
    {
396 3
        if (isset($this->def['table']))
397
            return $this->def['table'];
398
        else
399 3
            return $this->def['rhs_table'];
400
    }
401
}
402