Passed
Push — pulls/manymanylist-add-callbac... ( e094c2...9c86cd )
by Ingo
08:27
created

RelationList   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 137
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 26
c 1
b 0
f 0
dl 0
loc 137
rs 10
wmc 11

5 Methods

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