Attribute.php
7.79 KB
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
<?php
/**
* SearchWP Attribute.
*
* @package SearchWP
* @author Jon Christopher
*/
namespace SearchWP;
use SearchWP\Utils;
use SearchWP\Option;
/**
* Class Attribute defines individual attributes of a Source.
*
* @since 4.0
*/
final class Attribute {
/**
* A unique (per Source) name for this Attribute.
*
* @since 4.0
* @var string
*/
private $name;
/**
* A (more) readable version of the name.
*
* @since 4.0
* @var string
*/
private $label;
/**
* Notes for this Attribute when displayed in the UI.
*
* @since 4.0
* @var array
*/
private $notes;
/**
* Tooltip for this Attribute when displayed in the UI.
*
* @since 4.0
* @var string
*/
private $tooltip;
/**
* If this Attribute is repeatable, the options define the choices for this Attribute.
* Typically an Array of Options but can be a Callable (that returns an Array of Options) as well.
*
* @since 4.0
* @var mixed
*/
private $options = false;
/**
* Whether custom (user-defined) Options can be added.
*
* @since 4.0
* @var boolean
*/
public $allow_custom = false;
/**
* Attribute settings.
*
* @since 4.0
* @var mixed
*/
private $settings = null;
/**
* The value for this Attribute. Can be a Number, String, or Callable function.
* If Callable:
* - 1st parameter is the entry ID as per the ID column from the Source.
* - 2nd parameter is the option value as per the 'options' property of this Attribute.
*
* @since 4.0
* @var mixed
*/
private $data;
/**
* Whether Phrase logic should consider this Attribute. Can be a string that represents
* the column name of the Source's db_table to be used. Can also be an array of arrays with:
* `table` Table name.
* `column` Column to consider for source matches.
* `id` Column containing Entry ID.
*
* @since 4.0
* @var bool
*/
private $phrases = false;
/**
* Whether this Attribute should have a default weight (i.e. is automatically added to the Source).
*
* @since 4.0
* @var bool|Int
*/
private $default = false;
/**
* AJAX tag to retrieve Options.
*
* @since 4.0
* @var string
*/
public $options_ajax_tag = '';
/**
* Constructor.
*
* @since 4.0
*/
public function __construct( array $args ) {
if ( empty( trim( (string) $args['name'] ) ) ) {
wp_die(
__( 'Missing data property for SearchWP Attribute:', 'searchwp' ) . ' <code>' . esc_html( $this->name ) . '</code>',
__( 'SearchWP Attribute Error', 'searchwp' )
);
}
// The data for this attribute is retrieved via callback, which must be defined.
if ( empty( $args['data'] ) ) {
wp_die(
__( 'Missing data property for SearchWP Attribute:', 'searchwp' ) . ' <code>' . esc_html( $this->name ) . '</code>',
__( 'SearchWP Attribute Error', 'searchwp' )
);
}
$this->name = trim( sanitize_key( (string) $args['name'] ) );
$this->data = $args['data'];
$this->label = empty( $args['label'] ) ? ucfirst( $this->name ) : $args['label'];
$this->notes = empty( $args['notes'] ) ? [] : $args['notes'];
$this->tooltip = empty( $args['tooltip'] ) ? false : $args['tooltip'];
$this->options = empty( $args['options'] ) ? false : $args['options'];
$this->allow_custom = empty( $args['allow_custom'] ) ? false : true;
$this->phrases = empty( $args['phrases'] ) ? false : $args['phrases'];
$this->default = empty( $args['default'] ) ? false : (int) $args['default'];
if ( strlen( $this->name ) > 80 ) {
do_action( 'searchwp\debug\log', 'Name too long (max 80 chars) : ' . $this->name, 'attribute' );
return;
}
}
/**
* Getter for name.
*
* @since 4.0
* @return string
*/
public function get_name() {
return $this->name;
}
/**
* Getter for label.
*
* @since 4.0
* @return string
*/
public function get_label( $source = null ) {
$label = $this->label;
if ( $source instanceof Source ) {
$label = (string) apply_filters(
'searchwp\source\attribute\label',
$label, [
'source' => $source->get_name(),
'attribute' => $this->get_name(),
] );
}
return $label;
}
/**
* Getter for notes.
*
* @since 4.0
* @return string[]
*/
public function get_notes() {
return array_map( function( $note ) {
return sanitize_text_field( (string) $note );
}, (array) $this->notes );
}
/**
* Getter for tooltip.
*
* @since 4.0
* @return string
*/
public function get_tooltip() {
return sanitize_text_field( (string) $this->tooltip );
}
/**
* Phrases support details.
*
* @since 4.0
* @return bool
*/
public function get_phrases() {
return $this->phrases;
}
/**
* Returns whether options are statically defined.
*
* @since 4.0
* @return bool
*/
public function options_static() {
return ! is_callable( $this->options );
}
/**
* Getter for Attribute options.
*
* @since 4.0
* @param bool|string $search Search string to consider when finding options.
* @param array $include Option values to use as a limiter when retrieving Options.
* @return Options[]|mixed[] Array of Options or property values.
*/
public function get_options( $search = false, array $include = [] ) {
$options = is_callable( $this->options ) ? call_user_func( $this->options, $search, $include ) : $this->options;
if ( empty( $options ) ) {
return $options;
} else {
$options = (array) $options;
if ( ! empty( $include ) ) {
$options = array_filter( $options, function( $option ) use ( $include ) {
return in_array( $option->get_value(), $include );
} );
}
}
// Options must be Options.
return array_filter( $options, function( $option ) {
return $option instanceof Option;
} );
}
/**
* Getter for settings.
*
* @since 4.0
* @return mixed
*/
public function get_settings() {
return $this->settings;
}
/**
* Getter for default.
*
* @since 4.0
* @return bool|int
*/
public function get_default() {
return $this->default;
}
/**
* Setter for settings.
*
* @param mixed $settings Settings to set.
* @return void
*/
public function set_settings( $settings ) {
// Omit any invalid options.
if ( is_array( $settings ) && ! empty( $settings ) ) {
// Validate Options by using the existing settings as the include parameter.
$options = array_map( function( $option ) {
return $option->get_value();
}, $this->get_options( false, array_keys( $settings ) ) );
// Validate Option values.
// TODO: If a Taxonomy is registered outside `init` we get
// Warning: array_merge() expects at least 1 parameter, 0 given
// for this array_merge call.
$settings = call_user_func_array( 'array_merge',
array_filter( array_map( function( $key, $value ) use ( $options ) {
if ( ! in_array( $key, $options ) ) {
return false;
}
// The value here is the chosen weight.
return [ $key => absint( $value ) ];
}, array_keys( $settings ), array_values( $settings ) ) )
);
} else if ( is_numeric( $settings ) ) {
// The value here is the chosen weight.
$settings = absint( $settings );
}
$this->settings = $settings;
}
/**
* Retrieves the data for this Attribute.
*
* @since 4.0
* @param string|int $id The Entry ID.
* @param string $option The chosen option.
* @param bool $raw Whether to return the raw data, default is to tokenize.
* @return mixed
*/
public function get_data( $id = 0, $option = '', $raw = false ) {
if ( ! is_callable( $this->data ) ) {
$data = $this->data;
} else {
if ( false === $this->options ) {
$data = call_user_func( $this->data, $id );
} else {
$data = empty( $option ) ? false : call_user_func( $this->data, $id, $option );
}
}
if ( ! $raw ) {
$data = Utils::tokenize( $data );
}
return $data;
}
}