Field   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 3
Metric Value
eloc 39
c 3
b 0
f 3
dl 0
loc 185
ccs 50
cts 50
cp 1
rs 10
wmc 17

11 Methods

Rating   Name   Duplication   Size   Complexity  
A initGridField() 0 14 2
A getValueHolderField() 0 11 2
A enableLinkable() 0 11 2
A getGridField() 0 3 1
A createList() 0 3 1
A enableAddable() 0 11 2
A __construct() 0 14 1
A __call() 0 3 1
A FieldHolder() 0 7 1
A removeAddable() 0 10 2
A removeLinkable() 0 10 2
1
<?php
2
3
namespace Moo\HasOneSelector\Form;
4
5
use Exception;
6
use Moo\HasOneSelector\ORM\DataList;
7
use SilverStripe\Forms\CompositeField;
8
use SilverStripe\Forms\FormField;
9
use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter;
10
use SilverStripe\Forms\GridField\GridFieldAddNewButton;
11
use SilverStripe\Forms\GridField\GridFieldComponent;
12
use SilverStripe\Forms\HiddenField;
13
use SilverStripe\ORM\DataObject;
14
use SilverStripe\ORM\FieldType\DBHTMLText;
15
use SilverStripe\ORM\SS_List;
16
17
/**
18
 * Class Field provides CMS field to manage selecting/adding/editing object within
19
 * has_one relation of the current object being edited.
20
 */
21
class Field extends CompositeField
22
{
23
    /**
24
     * Instance of form field that find and display selected record.
25
     */
26
    protected ?GridField $gridField = null;
27
28
    /**
29
     * Instance of form field that holds the value.
30
     */
31
    protected ?FormField $valueField = null;
32
33
    /**
34
     * HasOneSelector Field constructor.
35
     *
36
     * @param string $name
37
     * @param string $title
38
     * @param string $dataClass
39
     */
40 8
    public function __construct($name, $title, DataObject $owner, $dataClass = DataObject::class)
41
    {
42
        // Create grid field
43 8
        $this->initGridField($name, $title, $owner, $dataClass);
44
45 8
        $this->addExtraClass('b-hasoneselector-field');
46
47
        // Ensure there is a left label to allow for field to be aligned with others
48 8
        $this->leftTitle = ' ';
49
50
        // Create composite field with hidden field holds the value and grid field to find and select has one relation
51 8
        parent::__construct([
52 8
            $this->getValueHolderField(),
53 8
            $this->getGridField(),
54 8
        ]);
55
    }
56
57
    /**
58
     * Returns a "field holder" for this field.
59
     *
60
     * Forms are constructed by concatenating a number of these field holders.
61
     *
62
     * The default field holder is a label and a form field inside a div.
63
     *
64
     * @see FieldHolder.ss
65
     *
66
     * @param array $properties
67
     *
68
     * @return DBHTMLText
69
     */
70 2
    public function FieldHolder($properties = [])
71
    {
72
        // Set title based on left title property
73 2
        $properties['Title'] = $this->leftTitle;
74
75
        // Render field holder
76 2
        return parent::FieldHolder($properties);
77
    }
78
79
    /**
80
     * Get instance of value holder field that hold the value of has one.
81
     */
82 8
    protected function getValueHolderField(): FormField
83
    {
84 8
        if (is_null($this->valueField)) {
85
            // Name of the has one relation
86 8
            $recordName = $this->getGridField()->getName().'ID';
87
88
            // Field to hold the value
89 8
            $this->valueField = HiddenField::create($recordName, '', '');
90
        }
91
92 8
        return $this->valueField;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->valueField could return the type null which is incompatible with the type-hinted return SilverStripe\Forms\FormField. Consider adding an additional type-check to rule them out.
Loading history...
93
    }
94
95
    /**
96
     * Get instance of grid field embed in wrapper field.
97
     */
98 8
    public function getGridField(): GridField
99
    {
100 8
        return $this->gridField;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->gridField could return the type null which is incompatible with the type-hinted return Moo\HasOneSelector\Form\GridField. Consider adding an additional type-check to rule them out.
Loading history...
101
    }
102
103
    /**
104
     * Initiate instance of grid field. This is a subclass of GridField.
105
     */
106 8
    protected function initGridField(
107
        string $name,
108
        string $title,
109
        DataObject $owner,
110
        string $dataClass = DataObject::class
111
    ): GridField {
112 8
        if (is_null($this->gridField)) {
113 8
            $this->gridField = GridField::create($name, $title, $owner, $dataClass);
114
            // Instance of data list that manages the grid field data
115 8
            $this->gridField->setList($this->createList());
0 ignored issues
show
Bug introduced by
The method setList() does not exist on null. ( Ignorable by Annotation )

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

115
            $this->gridField->/** @scrutinizer ignore-call */ 
116
                              setList($this->createList());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
116
        }
117 8
        $this->gridField->setValueHolderField($this->getValueHolderField());
118
119 8
        return $this->gridField;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->gridField could return the type null which is incompatible with the type-hinted return Moo\HasOneSelector\Form\GridField. Consider adding an additional type-check to rule them out.
Loading history...
120
    }
121
122
    /**
123
     * Create data list for grid field.
124
     */
125 8
    protected function createList(): SS_List
126
    {
127 8
        return DataList::create($this->getGridField());
128
    }
129
130
    /**
131
     * Remove the linkable grid field component.
132
     */
133 1
    public function removeLinkable(): self
134
    {
135
        // Remove grid field linkable component
136 1
        $this->getGridField()->getConfig()->getComponents()->each(function ($component) {
137 1
            if ($component instanceof GridFieldAddExistingAutocompleter) {
138 1
                $this->getGridField()->getConfig()->removeComponentsByType(GridFieldAddExistingAutocompleter::class);
139
            }
140 1
        });
141
142 1
        return $this;
143
    }
144
145
    /**
146
     * Add linkable grid field component.
147
     */
148 1
    public function enableLinkable(GridFieldComponent $component = null): self
149
    {
150
        // Use default linkable grid field component
151 1
        if (is_null($component)) {
152 1
            $component = new GridFieldAddExistingAutocompleter('buttons-before-right');
153
        }
154
155
        // Add grid field component
156 1
        $this->getGridField()->getConfig()->addComponent($component);
157
158 1
        return $this;
159
    }
160
161
    /**
162
     * Remove the addable grid field component.
163
     */
164 1
    public function removeAddable(): self
165
    {
166
        // Remove grid field addable component
167 1
        $this->getGridField()->getConfig()->getComponents()->each(function ($component) {
168 1
            if ($component instanceof GridFieldAddNewButton) {
169 1
                $this->getGridField()->getConfig()->removeComponentsByType(GridFieldAddNewButton::class);
170
            }
171 1
        });
172
173 1
        return $this;
174
    }
175
176
    /**
177
     * Add addable grid field component.
178
     */
179 1
    public function enableAddable(GridFieldComponent $component = null): self
180
    {
181
        // Use default addable grid field component
182 1
        if (is_null($component)) {
183 1
            $component = new GridFieldAddNewButton('buttons-before-left');
184
        }
185
186
        // Add grid field component
187 1
        $this->getGridField()->getConfig()->addComponent($component);
188
189 1
        return $this;
190
    }
191
192
    /**
193
     * Proxy any undefined methods to the grid field as this is the main field and the composite is wrapper to manage
194
     * the field and value of has one.
195
     *
196
     * @param string $method
197
     * @param array  $arguments
198
     *
199
     * @throws Exception
200
     *
201
     * @return mixed
202
     */
203 8
    public function __call($method, $arguments = [])
204
    {
205 8
        return $this->getGridField()->{$method}(...$arguments);
206
    }
207
}
208