Passed
Push — master ( 4be01e...967769 )
by Robbie
25:06 queued 13:45
created

DBEnum::flushCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\FieldType;
4
5
use SilverStripe\Core\Config\Config;
6
use SilverStripe\Forms\DropdownField;
7
use SilverStripe\ORM\ArrayLib;
8
use SilverStripe\ORM\DB;
9
10
/**
11
 * Class Enum represents an enumeration of a set of strings.
12
 *
13
 * See {@link DropdownField} for a {@link FormField} to select enum values.
14
 */
15
class DBEnum extends DBString
16
{
17
18
    /**
19
     * List of enum values
20
     *
21
     * @var array
22
     */
23
    protected $enum = [];
24
25
    /**
26
     * Default value
27
     *
28
     * @var string|null
29
     */
30
    protected $default = null;
31
32
    private static $default_search_filter_class = 'ExactMatchFilter';
0 ignored issues
show
introduced by
The private property $default_search_filter_class is not used, and could be removed.
Loading history...
33
34
    /**
35
     * Internal cache for obsolete enum values. The top level keys are the table, each of which contains
36
     * nested arrays with keys mapped to field names. The values of the lowest level array are the enum values
37
     *
38
     * @var array
39
     */
40
    protected static $enum_cache = [];
41
42
    /**
43
     * Clear all cached enum values.
44
     */
45
    public static function flushCache()
46
    {
47
        self::$enum_cache = [];
48
    }
49
50
    /**
51
     * Create a new Enum field, which is a value within a defined set, with an optional default.
52
     *
53
     * Example field specification strings:
54
     *
55
     * <code>
56
     *  "MyField" => "Enum('Val1, Val2, Val3')" // First item 'Val1' is default implicitly
57
     *  "MyField" => "Enum('Val1, Val2, Val3', 'Val2')" // 'Val2' is default explicitly
58
     *  "MyField" => "Enum('Val1, Val2, Val3', null)" // Force empty (no) default
59
     *  "MyField" => "Enum(array('Val1', 'Val2', 'Val3'), 'Val1')" // Supports array notation as well
60
     * </code>
61
     *
62
     * @param string $name
63
     * @param string|array $enum A string containing a comma separated list of options or an array of Vals.
64
     * @param string|int|null $default The default option, which is either NULL or one of the items in the enumeration.
65
     * If passing in an integer (non-string) it will default to the index of that item in the list.
66
     * Set to null or empty string to allow empty values
67
     * @param array  $options Optional parameters for this DB field
68
     */
69
    public function __construct($name = null, $enum = null, $default = 0, $options = [])
70
    {
71
        if ($enum) {
72
            $this->setEnum($enum);
73
            $enum = $this->getEnum();
74
75
            // If there's a default, then use this
76
            if ($default && !is_int($default)) {
77
                if (in_array($default, $enum)) {
78
                    $this->setDefault($default);
79
                } else {
80
                    user_error(
81
                        "Enum::__construct() The default value '$default' does not match any item in the enumeration",
82
                        E_USER_ERROR
83
                    );
84
                }
85
            } elseif (is_int($default) && $default < count($enum)) {
86
                // Set to specified index if given
87
                $this->setDefault($enum[$default]);
88
            } else {
89
                // Set to null if specified
90
                $this->setDefault(null);
91
            }
92
        }
93
94
        parent::__construct($name, $options);
95
    }
96
97
    /**
98
     * @return void
99
     */
100
    public function requireField()
101
    {
102
        $charset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'charset');
103
        $collation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'collation');
104
105
        $parts = array(
106
            'datatype' => 'enum',
107
            'enums' => $this->getEnumObsolete(),
108
            'character set' => $charset,
109
            'collate' => $collation,
110
            'default' => $this->getDefault(),
111
            'table' => $this->getTable(),
112
            'arrayValue' => $this->arrayValue
113
        );
114
115
        $values = array(
116
            'type' => 'enum',
117
            'parts' => $parts
118
        );
119
120
        DB::require_field($this->getTable(), $this->getName(), $values);
121
    }
122
123
    /**
124
     * Return a dropdown field suitable for editing this field.
125
     *
126
     * @param string $title
127
     * @param string $name
128
     * @param bool $hasEmpty
129
     * @param string $value
130
     * @param string $emptyString
131
     * @return DropdownField
132
     */
133
    public function formField($title = null, $name = null, $hasEmpty = false, $value = "", $emptyString = null)
134
    {
135
136
        if (!$title) {
137
            $title = $this->getName();
138
        }
139
        if (!$name) {
140
            $name = $this->getName();
141
        }
142
143
        $field = new DropdownField($name, $title, $this->enumValues(false), $value);
144
        if ($hasEmpty) {
145
            $field->setEmptyString($emptyString);
146
        }
147
148
        return $field;
149
    }
150
151
    public function scaffoldFormField($title = null, $params = null)
152
    {
153
        return $this->formField($title);
154
    }
155
156
    /**
157
     * @param string
158
     *
159
     * @return DropdownField
160
     */
161
    public function scaffoldSearchField($title = null)
162
    {
163
        $anyText = _t('SilverStripe\\ORM\\FieldType\\DBEnum.ANY', 'Any');
164
        return $this->formField($title, null, true, '', "($anyText)");
165
    }
166
167
    /**
168
     * Returns the values of this enum as an array, suitable for insertion into
169
     * a {@link DropdownField}
170
     *
171
     * @param boolean
172
     *
173
     * @return array
174
     */
175
    public function enumValues($hasEmpty = false)
176
    {
177
        return ($hasEmpty)
178
            ? array_merge(array('' => ''), ArrayLib::valuekey($this->getEnum()))
179
            : ArrayLib::valuekey($this->getEnum());
180
    }
181
182
    /**
183
     * Get list of enum values
184
     *
185
     * @return array
186
     */
187
    public function getEnum()
188
    {
189
        return $this->enum;
190
    }
191
192
193
    /**
194
     * Get the list of enum values, including obsolete values still present in the database
195
     *
196
     * If table or name are not set, or if it is not a valid field on the given table,
197
     * then only known enum values are returned.
198
     *
199
     * Values cached in this method can be cleared via `DBEnum::flushCache();`
200
     *
201
     * @return array
202
     */
203
    public function getEnumObsolete()
204
    {
205
        // Without a table or field specified, we can only retrieve known enum values
206
        $table = $this->getTable();
207
        $name = $this->getName();
208
        if (empty($table) || empty($name)) {
209
            return $this->getEnum();
210
        }
211
212
        // Ensure the table level cache exists
213
        if (empty(self::$enum_cache[$table])) {
214
            self::$enum_cache[$table] = array();
215
        }
216
217
        // Check existing cache
218
        if (!empty(self::$enum_cache[$table][$name])) {
219
            return self::$enum_cache[$table][$name];
220
        }
221
222
        // Get all enum values
223
        $enumValues = $this->getEnum();
224
        if (DB::get_schema()->hasField($table, $name)) {
225
            $existing = DB::query("SELECT DISTINCT \"{$name}\" FROM \"{$table}\"")->column();
226
            $enumValues = array_unique(array_merge($enumValues, $existing));
227
        }
228
229
        // Cache and return
230
        self::$enum_cache[$table][$name] = $enumValues;
231
        return $enumValues;
232
    }
233
234
    /**
235
     * Set enum options
236
     *
237
     * @param string|array $enum
238
     * @return $this
239
     */
240
    public function setEnum($enum)
241
    {
242
        if (!is_array($enum)) {
243
            $enum = preg_split(
244
                '/\s*,\s*/',
245
                // trim commas only if they are on the right with a newline following it
246
                ltrim(preg_replace('/,\s*\n\s*$/', '', $enum))
247
            );
248
        }
249
        $this->enum = array_values($enum);
0 ignored issues
show
Bug introduced by
It seems like $enum can also be of type false; however, parameter $input of array_values() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

249
        $this->enum = array_values(/** @scrutinizer ignore-type */ $enum);
Loading history...
250
        return $this;
251
    }
252
253
    /**
254
     * Get default vwalue
255
     *
256
     * @return string|null
257
     */
258
    public function getDefault()
259
    {
260
        return $this->default;
261
    }
262
263
    /**
264
     * Set default value
265
     *
266
     * @param string $default
267
     * @return $this
268
     */
269
    public function setDefault($default)
270
    {
271
        $this->default = $default;
272
        $this->setDefaultValue($default);
273
        return $this;
274
    }
275
}
276