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