M2MRelationship   D
last analyzed

Complexity

Total Complexity 134

Size/Duplication

Total Lines 621
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Test Coverage

Coverage 89.86%
Metric Value
dl 0
loc 621
ccs 124
cts 138
cp 0.8986
rs 4.7875
wmc 134
lcom 2
cbo 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
A getLinkedDefForModuleByRelationship() 0 19 3
B getMostAppropriateLinkedDefinition() 0 22 6
C add() 0 67 12
D getRowToInsert() 0 33 10
A addSelfReferencing() 0 8 2
D remove() 0 118 32
A removeSelfReferencing() 0 11 2
B load() 0 16 6
A linkIsLHS() 0 3 1
F getQuery() 0 63 22
F getJoin() 0 57 13
F getSubpanelQuery() 0 47 10
A getRoleFilterForJoin() 0 17 4
A relationship_exists() 0 9 1
A getAlternateKeyFields() 0 12 3
A getRelationshipTable() 0 9 3
A getFields() 0 20 3

How to fix   Complexity   

Complex Class

Complex classes like M2MRelationship 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 M2MRelationship, 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/SugarRelationship.php");
43
44
/**
45
 * Represents a many to many relationship that is table based.
46
 * @api
47
 */
48
class M2MRelationship extends SugarRelationship
49
{
50
    var $type = "many-to-many";
51
52 47
    public function __construct($def)
53
    {
54 47
        $this->def = $def;
55 47
        $this->name = $def['name'];
56
57 47
        $lhsModule = $def['lhs_module'];
58 47
        $this->lhsLinkDef = $this->getLinkedDefForModuleByRelationship($lhsModule);
59 47
        $this->lhsLink = $this->lhsLinkDef['name'];
60
61 47
        $rhsModule = $def['rhs_module'];
62 47
        $this->rhsLinkDef = $this->getLinkedDefForModuleByRelationship($rhsModule);
63 47
        $this->rhsLink = $this->rhsLinkDef['name'];
64
65 47
        $this->self_referencing = $lhsModule == $rhsModule;
66 47
    }
67
68
    /**
69
     * Find the link entry for a particular relationship and module.
70
     *
71
     * @param $module
72
     * @return array|bool
73
     */
74 47
    public function getLinkedDefForModuleByRelationship($module)
75
    {
76 47
        $results = VardefManager::getLinkFieldForRelationship( $module, BeanFactory::getObjectName($module), $this->name);
77
        //Only a single link was found
78 47
        if( isset($results['name']) )
79
        {
80 47
            return $results;
81
        }
82
        //Multiple links with same relationship name
83 23
        else if( is_array($results) )
84
        {
85
            $GLOBALS['log']->error("Warning: Multiple links found for relationship {$this->name} within module {$module}");
86
            return $this->getMostAppropriateLinkedDefinition($results);
87
        }
88
        else
89
        {
90 23
            return FALSE;
91
        }
92
    }
93
94
    /**
95
     * Find the most 'appropriate' link entry for a relationship/module in which there are multiple link entries with the
96
     * same relationship name.
97
     *
98
     * @param $links
99
     * @return bool
100
     */
101
    protected function getMostAppropriateLinkedDefinition($links)
102
    {
103
        //First priority is to find a link name that matches the relationship name
104
        foreach($links as $link)
105
        {
106
            if( isset($link['name']) && $link['name'] == $this->name )
107
            {
108
                return $link;
109
            }
110
        }
111
        //Next would be a relationship that has a side defined
112
        foreach($links as $link)
113
        {
114
            if( isset($link['id_name']))
115
            {
116
                return $link;
117
            }
118
        }
119
        //Unable to find an appropriate link, guess and use the first one
120
        $GLOBALS['log']->error("Unable to determine best appropriate link for relationship {$this->name}");
121
        return $links[0];
122
    }
123
    /**
124
     * @param  $lhs SugarBean left side bean to add to the relationship.
125
     * @param  $rhs SugarBean right side bean to add to the relationship.
126
     * @param  $additionalFields key=>value pairs of fields to save on the relationship
127
     * @return boolean true if successful
128
     */
129
    public function add($lhs, $rhs, $additionalFields = array())
130
    {
131
        $lhsLinkName = $this->lhsLink;
132
        $rhsLinkName = $this->rhsLink;
133
        
134
    	/* BEGIN - SECURITY GROUPS */
135
    	//Need to hijack this as security groups will not contain a link on the module side
136
    	//due to the way the module works. Plus it would remove the relative ease of adding custom module support
137
    	
138
    	if(get_class($rhs) != 'User' && get_class($rhs) != 'ACLRole' && get_class($lhs) == 'SecurityGroup') {
139
			$rhs->$rhsLinkName->addBean($lhs);			
140
			$this->callBeforeAdd($rhs, $lhs, $rhsLinkName);
141
142
			$dataToInsert = $this->getRowToInsert($lhs, $rhs, $additionalFields);
143
			$this->addRow($dataToInsert);
144
    		$rhs->$rhsLinkName->addBean($lhs);
145
    		$this->callAfterAdd($lhs, $rhs, $lhsLinkName);
146
    	} else if(get_class($lhs) != 'User' && get_class($lhs) != 'ACLRole' && get_class($rhs) == 'SecurityGroup') {
147
			$lhs->$lhsLinkName->addBean($rhs);			
148
			$this->callBeforeAdd($lhs, $rhs, $lhsLinkName);
149
150
			$dataToInsert = $this->getRowToInsert($lhs, $rhs, $additionalFields);
151
			$this->addRow($dataToInsert);
152
    		$lhs->$lhsLinkName->addBean($rhs);
153
    		$this->callAfterAdd($rhs, $lhs, $rhsLinkName);
154
    	} else {
155
    	/* END - SECURITY GROUPS */
156
157
        if (empty($lhs->$lhsLinkName) && !$lhs->load_relationship($lhsLinkName))
158
        {
159
            $lhsClass = get_class($lhs);
160
            $GLOBALS['log']->fatal("could not load LHS $lhsLinkName in $lhsClass");
161
            return false;
162
        }
163
        if (empty($rhs->$rhsLinkName) && !$rhs->load_relationship($rhsLinkName))
164
        {
165
            $rhsClass = get_class($rhs);
166
            $GLOBALS['log']->fatal("could not load RHS $rhsLinkName in $rhsClass");
167
            return false;
168
        }
169
170
            $lhs->$lhsLinkName->addBean($rhs);
171
            $rhs->$rhsLinkName->addBean($lhs);
172
173
            $this->callBeforeAdd($lhs, $rhs, $lhsLinkName);
174
            $this->callBeforeAdd($rhs, $lhs, $rhsLinkName);
175
176
        //Many to many has no additional logic, so just add a new row to the table and notify the beans.
177
        $dataToInsert = $this->getRowToInsert($lhs, $rhs, $additionalFields);
178
179
        $this->addRow($dataToInsert);
180
181
        if ($this->self_referencing)
182
            $this->addSelfReferencing($lhs, $rhs, $additionalFields);
183
184
            $lhs->$lhsLinkName->addBean($rhs);
185
            $rhs->$rhsLinkName->addBean($lhs);
186
187
            $this->callAfterAdd($lhs, $rhs, $lhsLinkName);
188
            $this->callAfterAdd($rhs, $lhs, $rhsLinkName);
189
190
        /* BEGIN - SECURITY GROUPS */
191
        } //end normal 
192
        /* END - SECURITY GROUPS */
193
194
        return true;
195
    }
196
197
    protected function getRowToInsert($lhs, $rhs, $additionalFields = array())
198
    {
199
        $row = array(
200
            "id" => create_guid(),
201
            $this->def['join_key_lhs'] => $lhs->id,
202
            $this->def['join_key_rhs'] => $rhs->id,
203
            'date_modified' => TimeDate::getInstance()->nowDb(),
204
            'deleted' => 0,
205
        );
206
207
208
        if (!empty($this->def['relationship_role_column']) && !empty($this->def['relationship_role_column_value']) && !$this->ignore_role_filter )
209
        {
210
            $row[$this->relationship_role_column] = $this->relationship_role_column_value;
0 ignored issues
show
Documentation introduced by
The property relationship_role_column does not exist on object<M2MRelationship>. 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...
211
        }
212
213
        if (!empty($this->def['fields']))
214
        {
215
            foreach($this->def['fields'] as $fieldDef)
216
            {
217
                if (!empty($fieldDef['name']) && !isset($row[$fieldDef['name']]) && !empty($fieldDef['default']))
218
                {
219
                    $row[$fieldDef['name']] = $fieldDef['default'];
220
                }
221
            }
222
        }
223
        if (!empty($additionalFields))
224
        {
225
            $row = array_merge($row, $additionalFields);
226
        }
227
228
        return $row;
229
    }
230
231
    /**
232
     * Adds the reversed version of this relationship to the table so that it can be accessed from either side equally
233
     * @param $lhs
234
     * @param $rhs
235
     * @param array $additionalFields
236
     * @return void
237
     */
238
    protected function addSelfReferencing($lhs, $rhs, $additionalFields = array())
239
    {
240
        if ($rhs->id != $lhs->id)
241
        {
242
            $dataToInsert = $this->getRowToInsert($rhs, $lhs, $additionalFields);
243
            $this->addRow($dataToInsert);
244
        }
245
    }
246
247 1
    public function remove($lhs, $rhs)
248
    {
249 1
        if(!($lhs instanceof SugarBean) || !($rhs instanceof SugarBean)) {
250
            $GLOBALS['log']->fatal("LHS and RHS must be beans");
251
            return false;
252
        }
253 1
        $lhsLinkName = $this->lhsLink;
254 1
        $rhsLinkName = $this->rhsLink;
255
256 1
        if (!($lhs instanceof SugarBean)) {
257
            $GLOBALS['log']->fatal("LHS is not a SugarBean object");
258
            return false;
259
        }
260 1
        if (!($rhs instanceof SugarBean)) {
261
            $GLOBALS['log']->fatal("RHS is not a SugarBean object");
262
            return false;
263
        }
264
        
265
    	/* BEGIN - SECURITY GROUPS */
266
    	//Need to hijack this as security groups will not contain a link on the module side
267
    	//due to the way the module works. Plus it would remove the relative ease of adding custom module support
268
    	
269 1
    	if(get_class($lhs) == 'SecurityGroup' || get_class($rhs) == 'SecurityGroup') {
270
			$dataToRemove = array(
271
				$this->def['join_key_lhs'] => $lhs->id,
272
				$this->def['join_key_rhs'] => $rhs->id
273
			);
274
275
276
              if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
277
              {
278
                  if (get_class($lhs) != 'SecurityGroup' && $lhs->$lhsLinkName instanceof Link2)
279
                  {
280
                      $lhs->$lhsLinkName->load();
281
                      $this->callBeforeDelete($lhs, $rhs, $lhsLinkName);
282
                  }
283
284
                  if (get_class($rhs) != 'SecurityGroup' && $rhs->$rhsLinkName instanceof Link2)
285
                  {
286
                      $rhs->$rhsLinkName->load();
287
                      $this->callBeforeDelete($rhs, $lhs, $rhsLinkName);
288
                  }
289
              }
290
291
			$this->removeRow($dataToRemove);
292
			
293
			if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
294
			{
295
				if (get_class($lhs) != 'SecurityGroup' && $lhs->$lhsLinkName instanceof Link2)
296
				{
297
					$lhs->$lhsLinkName->load();
298
					$this->callAfterDelete($lhs, $rhs, $lhsLinkName);
299
				}
300
301
				if (get_class($rhs) != 'SecurityGroup' && $rhs->$rhsLinkName instanceof Link2)
302
				{
303
					$rhs->$rhsLinkName->load();
304
					$this->callAfterDelete($rhs, $lhs, $rhsLinkName);
305
				}
306
			}
307
		} else {
308
    	/* END - SECURITY GROUPS */        
309 1
        if (empty($lhs->$lhsLinkName) && !$lhs->load_relationship($lhsLinkName))
310
        {
311
            $GLOBALS['log']->fatal("could not load LHS $lhsLinkName");
312
            return false;
313
        }
314 1
        if (empty($rhs->$rhsLinkName) && !$rhs->load_relationship($rhsLinkName))
315
        {
316
            $GLOBALS['log']->fatal("could not load RHS $rhsLinkName");
317
            return false;
318
        }
319
320 1
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
321
        {
322 1
            if ($lhs->$lhsLinkName instanceof Link2)
323
            {
324 1
                $lhs->$lhsLinkName->load();
325 1
                $this->callBeforeDelete($lhs, $rhs, $lhsLinkName);
326
            }
327
328 1
            if ($rhs->$rhsLinkName instanceof Link2)
329
            {
330 1
                $rhs->$rhsLinkName->load();
331 1
                $this->callBeforeDelete($rhs, $lhs, $rhsLinkName);
332
            }
333
        }
334
335
        $dataToRemove = array(
336 1
            $this->def['join_key_lhs'] => $lhs->id,
337 1
            $this->def['join_key_rhs'] => $rhs->id
338
        );
339
340 1
        $this->removeRow($dataToRemove);
341
342 1
        if ($this->self_referencing)
343
            $this->removeSelfReferencing($lhs, $rhs);
344
345 1
        if (empty($_SESSION['disable_workflow']) || $_SESSION['disable_workflow'] != "Yes")
346
        {
347 1
            if ($lhs->$lhsLinkName instanceof Link2)
348
            {
349 1
                $lhs->$lhsLinkName->load();
350 1
                $this->callAfterDelete($lhs, $rhs, $lhsLinkName);
351
            }
352
353 1
            if ($rhs->$rhsLinkName instanceof Link2)
354
            {
355 1
                $rhs->$rhsLinkName->load();
356 1
                $this->callAfterDelete($rhs, $lhs, $rhsLinkName);
357
            }
358
        }
359
        /* BEGIN - SECURITY GROUPS */
360
        } //end normal 
361
        /* END - SECURITY GROUPS */
362
363 1
        return true;
364
    }
365
366
    /**
367
     * Removes the reversed version of this relationship
368
     * @param $lhs
369
     * @param $rhs
370
     * @param array $additionalFields
371
     * @return void
372
     */
373
    protected function removeSelfReferencing($lhs, $rhs, $additionalFields = array())
374
    {
375
        if ($rhs->id != $lhs->id)
376
        {
377
            $dataToRemove = array(
378
                $this->def['join_key_lhs'] => $rhs->id,
379
                $this->def['join_key_rhs'] => $lhs->id
380
            );
381
            $this->removeRow($dataToRemove);
382
        }
383
    }
384
385
    /**
386
     * @param  $link Link2 loads the relationship for this link.
387
     * @return void
388
     */
389 30
    public function load($link, $params = array())
390
    {
391 30
        $db = DBManagerFactory::getInstance();
392 30
        $query = $this->getQuery($link, $params);
393 30
        $result = $db->query($query);
394 30
        $rows = Array();
395 30
        $idField = $link->getSide() == REL_LHS ? $this->def['join_key_rhs'] : $this->def['join_key_lhs'];
396 30
        while ($row = $db->fetchByAssoc($result, FALSE))
397
        {
398 3
            if (empty($row['id']) && empty($row[$idField]))
399 1
                continue;
400 2
            $id = empty($row['id']) ? $row[$idField] : $row['id'];
401 2
            $rows[$id] = $row;
402
        }
403 30
        return array("rows" => $rows);
404
    }
405
406 32
    protected function linkIsLHS($link) {
407 32
        return $link->getSide() == REL_LHS;
408
    }
409
410 32
    public function getQuery($link, $params = array())
411
    {
412 32
        if ($this->linkIsLHS($link)) {
413 18
            $knownKey = $this->def['join_key_lhs'];
414 18
            $targetKey = $this->def['join_key_rhs'];
415 18
            $relatedSeed = BeanFactory::getBean($this->getRHSModule());
416 18
            $relatedSeedKey = $this->def['rhs_key'];
417 18
            if (!empty($params['where']) || !empty($params['order_by']))
418 18
                $whereTable = (empty($params['right_join_table_alias']) ? $relatedSeed->table_name : $params['right_join_table_alias']);
419
        } else {
420 26
            $knownKey = $this->def['join_key_rhs'];
421 26
            $targetKey = $this->def['join_key_lhs'];
422 26
            $relatedSeed = BeanFactory::getBean($this->getLHSModule());
423 26
            $relatedSeedKey = $this->def['lhs_key'];
424 26
            if (!empty($params['where']) || !empty($params['order_by']))
425
                $whereTable = (empty($params['left_join_table_alias']) ? $relatedSeed->table_name : $params['left_join_table_alias']);
426
        }
427 32
        $rel_table = $this->getRelationshipTable();
428
429 32
        $where = "$rel_table.$knownKey = '{$link->getFocus()->id}'" . $this->getRoleWhere();
430 32
        $order_by = '';
431
432
        //Add any optional where clause
433 32
        if (!empty($params['where'])) {
434
            $add_where = is_string($params['where']) ? $params['where'] : "$whereTable." . $this->getOptionalWhereClause($params['where']);
435
            if (!empty($add_where))
436
                $where .= " AND $add_where";
437
        }
438
439
        //Add any optional order clauses
440 32
        if (!empty($params['order_by'])) {
441
            $order_by = $relatedSeed->process_order_by($params['order_by']);
442
        }
443
444 32
        $deleted = !empty($params['deleted']) ? 1 : 0;
445 32
        $from = $rel_table . " ";
446 32
        if (!empty($params['where']) || !empty($params['order_by'])) {
447
            $from .= ", $whereTable";
448
            if (isset($relatedSeed->custom_fields)) {
449
                $customJoin = $relatedSeed->custom_fields->getJOIN();
450
                $from .= $customJoin ? $customJoin['join'] : '';
451
            }
452
            $where .= " AND $rel_table.$targetKey=$whereTable.id";
453
        }
454
455 32
        if (empty($params['return_as_array'])) {
456 32
            $query = "SELECT $targetKey id FROM $from WHERE $where AND $rel_table.deleted=$deleted";
457 32
            if(!empty($order_by)) $query .= ' ORDER BY '.$order_by;
458
            //Limit is not compatible with return_as_array
459 32
            if (!empty($params['limit']) && $params['limit'] > 0) {
460
                $offset = isset($params['offset']) ? $params['offset'] : 0;
461
                $query = DBManagerFactory::getInstance()->limitQuery($query, $offset, $params['limit'], false, "", false);
462
            }
463 32
            return $query;
464
        } else {
465
            return array(
466
                'select' => "SELECT $targetKey id",
467
                'from' => "FROM $from",
468
                'where' => "WHERE $where AND $rel_table.deleted=$deleted",
469
                'order_by' => $order_by
470
            );
471
        }
472
    }
473
474 7
    public function getJoin($link, $params = array(), $return_array = false)
475
    {
476 7
        $linkIsLHS = $link->getSide() == REL_LHS;
477 7
        if ($linkIsLHS) {
478 5
            $startingTable = (empty($params['left_join_table_alias']) ? $link->getFocus()->table_name : $params['left_join_table_alias']);
479
        } else {
480 2
            $startingTable = (empty($params['right_join_table_alias']) ? $link->getFocus()->table_name : $params['right_join_table_alias']);
481
        }
482
483 7
        $startingKey = $linkIsLHS ? $this->def['lhs_key'] : $this->def['rhs_key'];
484 7
        $startingJoinKey = $linkIsLHS ? $this->def['join_key_lhs'] : $this->def['join_key_rhs'];
485 7
        $joinTable = $this->getRelationshipTable();
486 7
        $joinTableWithAlias = $joinTable;
487 7
        $joinKey = $linkIsLHS ? $this->def['join_key_rhs'] : $this->def['join_key_lhs'];
488 7
        $targetTable = $linkIsLHS ? $this->def['rhs_table'] : $this->def['lhs_table'];
489 7
        $targetTableWithAlias = $targetTable;
490 7
        $targetKey = $linkIsLHS ? $this->def['rhs_key'] : $this->def['lhs_key'];
491 7
        $join_type= isset($params['join_type']) ? $params['join_type'] : ' INNER JOIN ';
492
493 7
        $join = '';
494
495
        //Set up any table aliases required
496 7
        if (!empty($params['join_table_link_alias']))
497
        {
498 7
            $joinTableWithAlias = $joinTable . " ". $params['join_table_link_alias'];
499 7
            $joinTable = $params['join_table_link_alias'];
500
        }
501 7
        if ( ! empty($params['join_table_alias']))
502
        {
503 7
            $targetTableWithAlias = $targetTable . " ". $params['join_table_alias'];
504 7
            $targetTable = $params['join_table_alias'];
505
        }
506
507 7
        $join1 = "$startingTable.$startingKey=$joinTable.$startingJoinKey";
508 7
        $join2 = "$targetTable.$targetKey=$joinTable.$joinKey";
509 7
        $where = "";
510
511
512
        //First join the relationship table
513 7
        $join .= "$join_type $joinTableWithAlias ON $join1 AND $joinTable.deleted=0\n"
514
        //Next add any role filters
515 7
               . $this->getRoleWhere($joinTable) . "\n"
516
        //Then finally join the related module's table
517 7
               . "$join_type $targetTableWithAlias ON $join2 AND $targetTable.deleted=0\n";
518
519 7
        if($return_array){
520
            return array(
521 7
                'join' => $join,
522 7
                'type' => $this->type,
523 7
                'rel_key' => $joinKey,
524 7
                'join_tables' => array($joinTable, $targetTable),
525 7
                'where' => $where,
526 7
                'select' => "$targetTable.id",
527
            );
528
        }
529
        return $join . $where;
530
    }
531
532
    /**
533
     * Similar to getQuery or Get join, except this time we are starting from the related table and
534
     * searching for items with id's matching the $link->focus->id
535
     * @param  $link
536
     * @param array $params
537
     * @param bool $return_array
538
     * @return String|Array
539
     */
540
    public function getSubpanelQuery($link, $params = array(), $return_array = false)
541
    {
542
        $targetIsLHS = $link->getSide() == REL_RHS;
543
        $startingTable = $targetIsLHS ? $this->def['lhs_table'] : $this->def['rhs_table'];;
544
        $startingKey = $targetIsLHS ? $this->def['lhs_key'] : $this->def['rhs_key'];
545
        $startingJoinKey = $targetIsLHS ? $this->def['join_key_lhs'] : $this->def['join_key_rhs'];
546
        $joinTable = $this->getRelationshipTable();
547
        $joinTableWithAlias = $joinTable;
548
        $joinKey = $targetIsLHS ? $this->def['join_key_rhs'] : $this->def['join_key_lhs'];
549
        $targetKey = $targetIsLHS ? $this->def['rhs_key'] : $this->def['lhs_key'];
550
        $join_type= isset($params['join_type']) ? $params['join_type'] : ' INNER JOIN ';
551
552
        $query = '';
553
554
        //Set up any table aliases required
555
        if (!empty($params['join_table_link_alias']))
556
        {
557
            $joinTableWithAlias = $joinTable . " ". $params['join_table_link_alias'];
558
            $joinTable = $params['join_table_link_alias'];
559
        }
560
561
        $where = "$startingTable.$startingKey=$joinTable.$startingJoinKey AND $joinTable.$joinKey='{$link->getFocus()->$targetKey}'";
562
563
        //Check if we should ignore the role filter.
564
        $ignoreRole = !empty($params['ignore_role']);
565
566
        //First join the relationship table
567
        $query .= "$join_type $joinTableWithAlias ON $where AND $joinTable.deleted=0\n"
568
        //Next add any role filters
569
               . $this->getRoleWhere($joinTable, $ignoreRole) . "\n";
570
571
        if (!empty($params['return_as_array'])) {
572
            $return_array = true;
573
        }
574
        if($return_array){
575
            return array(
576
                'join' => $query,
577
                'type' => $this->type,
578
                'rel_key' => $joinKey,
579
                'join_tables' => array($joinTable),
580
                'where' => "",
581
                'select' => " ",
582
            );
583
        }
584
        return $query;
585
586
    }
587
588
    protected function getRoleFilterForJoin()
589
    {
590
        $ret = "";
591
        if (!empty($this->relationship_role_column) && !$this->ignore_role_filter)
592
        {
593
            $ret .= " AND ".$this->getRelationshipTable().'.'.$this->relationship_role_column;
594
            //role column value.
595
            if (empty($this->relationship_role_column_value))
596
            {
597
                $ret.=' IS NULL';
598
            } else {
599
                $ret.= "='".$this->relationship_role_column_value."'";
600
            }
601
            $ret.= "\n";
602
        }
603
        return $ret;
604
    }
605
606
    /**
607
     * @param  $lhs
608
     * @param  $rhs
609
     * @return bool
610
     */
611
    public function relationship_exists($lhs, $rhs)
612
    {
613
        $query = "SELECT id FROM {$this->getRelationshipTable()} WHERE {$this->join_key_lhs} = '{$lhs->id}' AND {$this->join_key_rhs} = '{$rhs->id}'";
0 ignored issues
show
Documentation introduced by
The property join_key_lhs does not exist on object<M2MRelationship>. 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...
Documentation introduced by
The property join_key_rhs does not exist on object<M2MRelationship>. 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...
614
615
        //Roles can allow for multiple links between two records with different roles
616
        $query .= $this->getRoleWhere() . " and deleted = 0";
617
618
        return $GLOBALS['db']->getOne($query);
619
    }
620
621
    /**
622
     * @return Array - set of fields that uniquely identify an entry in this relationship
623
     */
624
    protected function getAlternateKeyFields()
625
    {
626
        $fields = array($this->join_key_lhs, $this->join_key_rhs);
0 ignored issues
show
Documentation introduced by
The property join_key_lhs does not exist on object<M2MRelationship>. 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...
Documentation introduced by
The property join_key_rhs does not exist on object<M2MRelationship>. 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...
627
628
        //Roles can allow for multiple links between two records with different roles
629
        if (!empty($this->def['relationship_role_column']) && !$this->ignore_role_filter)
630
        {
631
            $fields[] = $this->relationship_role_column;
632
        }
633
634
        return $fields;
635
    }
636
637 39
    public function getRelationshipTable()
638
    {
639 39
        if (!empty($this->def['table']))
640 30
            return $this->def['table'];
641 21
        else if(!empty($this->def['join_table']))
642 21
            return $this->def['join_table'];
643
644
        return false;
645
    }
646
647
    public function getFields()
648
    {
649
        if (!empty($this->def['fields']))
650
            return $this->def['fields'];
651
        $fields = array(
652
            "id" => array('name' => 'id'),
653
            'date_modified' => array('name' => 'date_modified'),
654
            'modified_user_id' => array('name' => 'modified_user_id'),
655
            'created_by' => array('name' => 'created_by'),
656
            $this->def['join_key_lhs'] => array('name' => $this->def['join_key_lhs']),
657
            $this->def['join_key_rhs'] => array('name' => $this->def['join_key_rhs'])
658
        );
659
        if (!empty($this->def['relationship_role_column']))
660
        {
661
            $fields[$this->def['relationship_role_column']] = array("name" => $this->def['relationship_role_column']);
662
        }
663
        $fields['deleted'] = array('name' => 'deleted');
664
665
        return $fields;
666
    }
667
668
}
669