Passed
Push — 4.1 ( dd3fbf...7ec5fa )
by Daniel
11:01
created

Member_GroupSet::removeAll()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 0
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Security;
4
5
use SilverStripe\ORM\DataObject;
6
use SilverStripe\ORM\DB;
7
use SilverStripe\ORM\ManyManyList;
8
use SilverStripe\ORM\Queries\SQLDelete;
9
use SilverStripe\ORM\Queries\SQLSelect;
10
11
/**
12
 * Represents a set of Groups attached to a member.
13
 * Handles the hierarchy logic.
14
 */
15
class Member_GroupSet extends ManyManyList
16
{
17
18
    protected function linkJoinTable()
19
    {
20
        // Do not join the table directly
21
        if ($this->extraFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->extraFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
22
            user_error('Member_GroupSet does not support many_many_extraFields', E_USER_ERROR);
23
        }
24
    }
25
26
    /**
27
     * Link this group set to a specific member.
28
     *
29
     * Recursively selects all groups applied to this member, as well as any
30
     * parent groups of any applied groups
31
     *
32
     * @param array|integer $id (optional) An ID or an array of IDs - if not provided, will use the current
33
     * ids as per getForeignID
34
     * @return array Condition In array(SQL => parameters format)
35
     */
36
    public function foreignIDFilter($id = null)
37
    {
38
        if ($id === null) {
39
            $id = $this->getForeignID();
40
        }
41
42
        // Find directly applied groups
43
        $manyManyFilter = parent::foreignIDFilter($id);
44
        $query = new SQLSelect('"Group_Members"."GroupID"', '"Group_Members"', $manyManyFilter);
45
        $groupIDs = $query->execute()->column();
46
47
        // Get all ancestors, iteratively merging these into the master set
48
        $allGroupIDs = array();
49
        while ($groupIDs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $groupIDs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
50
            $allGroupIDs = array_merge($allGroupIDs, $groupIDs);
51
            $groupIDs = DataObject::get("SilverStripe\\Security\\Group")->byIDs($groupIDs)->column("ParentID");
52
            $groupIDs = array_filter($groupIDs);
53
        }
54
55
        // Add a filter to this DataList
56
        if (!empty($allGroupIDs)) {
57
            $allGroupIDsPlaceholders = DB::placeholders($allGroupIDs);
58
            return array("\"Group\".\"ID\" IN ($allGroupIDsPlaceholders)" => $allGroupIDs);
59
        } else {
60
            return array('"Group"."ID"' => 0);
61
        }
62
    }
63
64
    public function foreignIDWriteFilter($id = null)
65
    {
66
        // Use the ManyManyList::foreignIDFilter rather than the one
67
        // in this class, otherwise we end up selecting all inherited groups
68
        return parent::foreignIDFilter($id);
69
    }
70
71
    public function add($item, $extraFields = null)
72
    {
73
        // Get Group.ID
74
        $itemID = null;
75
        if (is_numeric($item)) {
76
            $itemID = $item;
77
        } else {
78
            if ($item instanceof Group) {
79
                $itemID = $item->ID;
80
            }
81
        }
82
83
        // Check if this group is allowed to be added
84
        if ($this->canAddGroups(array($itemID))) {
85
            parent::add($item, $extraFields);
86
        }
87
    }
88
89
    public function removeAll()
90
    {
91
        // Remove the join to the join table to avoid MySQL row locking issues.
92
        $query = $this->dataQuery();
93
        $foreignFilter = $query->getQueryParam('Foreign.Filter');
94
        $query->removeFilterOn($foreignFilter);
95
96
        // Select ID column
97
        $selectQuery = $query->query();
98
        $dataClassIDColumn = DataObject::getSchema()->sqlColumnForField($this->dataClass(), 'ID');
99
        $selectQuery->setSelect($dataClassIDColumn);
100
101
        $from = $selectQuery->getFrom();
102
        unset($from[$this->joinTable]);
103
        $selectQuery->setFrom($from);
104
        $selectQuery->setOrderBy(); // ORDER BY in subselects breaks MS SQL Server and is not necessary here
105
        $selectQuery->setDistinct(false);
106
107
        // Use a sub-query as SQLite does not support setting delete targets in
108
        // joined queries.
109
        $delete = new SQLDelete();
110
        $delete->setFrom("\"{$this->joinTable}\"");
111
        $delete->addWhere(parent::foreignIDFilter());
112
        $subSelect = $selectQuery->sql($parameters);
113
        $delete->addWhere(array(
114
            "\"{$this->joinTable}\".\"{$this->localKey}\" IN ($subSelect)" => $parameters
115
        ));
116
        $delete->execute();
117
    }
118
119
    /**
120
     * Determine if the following groups IDs can be added
121
     *
122
     * @param array $itemIDs
123
     * @return boolean
124
     */
125
    protected function canAddGroups($itemIDs)
126
    {
127
        if (empty($itemIDs)) {
128
            return true;
129
        }
130
        $member = $this->getMember();
131
        return empty($member) || $member->onChangeGroups($itemIDs);
132
    }
133
134
    /**
135
     * Get foreign member record for this relation
136
     *
137
     * @return Member
138
     */
139
    protected function getMember()
140
    {
141
        $id = $this->getForeignID();
142
        if ($id) {
143
            return DataObject::get_by_id(Member::class, $id);
0 ignored issues
show
Bug introduced by
$id of type string is incompatible with the type integer expected by parameter $id of SilverStripe\ORM\DataObject::get_by_id(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
            return DataObject::get_by_id(Member::class, /** @scrutinizer ignore-type */ $id);
Loading history...
144
        }
145
    }
146
}
147