Passed
Push — pulls/manymanylist-add-callbac... ( aed064...34c05d )
by Ingo
13:26
created

RelationList::setAddCallback()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM;
4
5
use Exception;
6
use Sminnee\CallbackList\CallbackList;
7
8
/**
9
 * A DataList that represents a relation.
10
 *
11
 * Adds the notion of a foreign ID that can be optionally set.
12
 */
13
abstract class RelationList extends DataList implements Relation
14
{
15
    /**
16
     * @var CallbackList|null
17
     */
18
    protected $addCallbacks;
19
20
    /**
21
     * @var CallbackList|null
22
     */
23
    protected $removeCallbacks;
24
25
    /**
26
     * Manage callbacks which are called after the add() action is completed.
27
     * Each callback will be passed (RelationList $this, DataObject|int $item, array $extraFields).
28
     * If a relation methods is manually defined, this can be called to adjust the behaviour
29
     * when adding records to this list.
30
     *
31
     * Needs to be defined through an overloaded relationship getter
32
     * to ensure it is set consistently. These getters return a new object
33
     * every time they're called.
34
     *
35
     * Note that subclasses of RelationList must implement the callback for it to function
36
     *
37
     * @return this
38
     */
39
    public function addCallbacks(): CallbackList
40
    {
41
        if (!$this->addCallbacks) {
42
            $this->addCallbacks = new CallbackList();
43
        }
44
45
        return $this->addCallbacks;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->addCallbacks returns the type Sminnee\CallbackList\CallbackList which is incompatible with the documented return type SilverStripe\ORM\RelationList.
Loading history...
46
    }
47
48
    /**
49
     * Manage callbacks which are called after the remove() action is completed.
50
     * Each Callback will be passed (RelationList $this, [int] $removedIds).
51
     *
52
     * Needs to be defined through an overloaded relationship getter
53
     * to ensure it is set consistently. These getters return a new object
54
     * every time they're called. Example:
55
     *
56
     * ```php
57
     * class MyObject extends DataObject()
58
     * {
59
     *   private static $many_many = [
60
     *     'MyRelationship' => '...',
61
     *   ];
62
     *   public function MyRelationship()
63
     *   {
64
     *     $list = $this->getManyManyComponents('MyRelationship');
65
     *     $list->removeCallbacks()->add(function ($removedIds) {
66
     *       // ...
67
     *     });
68
     *     return $list;
69
     *   }
70
     * }
71
     * ```
72
     *
73
     * If a relation methods is manually defined, this can be called to adjust the behaviour
74
     * when adding records to this list.
75
     *
76
     * Subclasses of RelationList must implement the callback for it to function
77
     *
78
     * @return this
79
     */
80
    public function removeCallbacks(): CallbackList
81
    {
82
        if (!$this->removeCallbacks) {
83
            $this->removeCallbacks = new CallbackList();
84
        }
85
86
        return $this->removeCallbacks;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->removeCallbacks returns the type Sminnee\CallbackList\CallbackList which is incompatible with the documented return type SilverStripe\ORM\RelationList.
Loading history...
87
    }
88
89
    /**
90
     * Any number of foreign keys to apply to this list
91
     *
92
     * @return string|array|null
93
     */
94
    public function getForeignID()
95
    {
96
        return $this->dataQuery->getQueryParam('Foreign.ID');
97
    }
98
99
    public function getQueryParams()
100
    {
101
        $params = parent::getQueryParams();
102
103
        // Remove `Foreign.` query parameters for created objects,
104
        // as this would interfere with relations on those objects.
105
        foreach (array_keys($params) as $key) {
106
            if (stripos($key, 'Foreign.') === 0) {
107
                unset($params[$key]);
108
            }
109
        }
110
111
        return $params;
112
    }
113
114
    /**
115
     * Returns a copy of this list with the ManyMany relationship linked to
116
     * the given foreign ID.
117
     *
118
     * @param int|array $id An ID or an array of IDs.
119
     *
120
     * @return static
121
     */
122
    public function forForeignID($id)
123
    {
124
        // Turn a 1-element array into a simple value
125
        if (is_array($id) && sizeof($id) == 1) {
126
            $id = reset($id);
127
        }
128
129
        // Calculate the new filter
130
        $filter = $this->foreignIDFilter($id);
131
132
        $list = $this->alterDataQuery(function (DataQuery $query) use ($id, $filter) {
133
            // Check if there is an existing filter, remove if there is
134
            $currentFilter = $query->getQueryParam('Foreign.Filter');
135
            if ($currentFilter) {
136
                try {
137
                    $query->removeFilterOn($currentFilter);
138
                } catch (Exception $e) {
139
                    /* NOP */
140
                }
141
            }
142
143
            // Add the new filter
144
            $query->setQueryParam('Foreign.ID', $id);
145
            $query->setQueryParam('Foreign.Filter', $filter);
146
            $query->where($filter);
147
        });
148
149
        return $list;
150
    }
151
152
    /**
153
     * Returns a where clause that filters the members of this relationship to
154
     * just the related items.
155
     *
156
     *
157
     * @param array|integer $id (optional) An ID or an array of IDs - if not provided, will use the current ids as
158
     * per getForeignID
159
     * @return array Condition In array(SQL => parameters format)
160
     */
161
    abstract protected function foreignIDFilter($id = null);
162
}
163