GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#2835)
by
unknown
06:01
created

Field   F

Complexity

Total Complexity 190

Size/Duplication

Total Lines 2119
Duplicated Lines 0 %

Importance

Changes 10
Bugs 0 Features 0
Metric Value
c 10
b 0
f 0
dl 0
loc 2119
rs 0.6314
wmc 190

79 Methods

Rating   Name   Duplication   Size   Complexity  
A offsetUnset() 0 3 1
A createCheckboxSetting() 0 9 1
A isFilterSQL() 0 10 3
A fetchSuggestionTypes() 0 3 1
A isFilterRegex() 0 10 4
A prepareTextValue() 0 3 1
B prepareReadableValue() 0 16 6
A getExampleFormMarkup() 0 6 1
A tableExists() 0 6 2
D checkFields() 0 38 13
A requiresSQLGrouping() 0 3 1
A checkPostFieldData() 0 13 4
A canToggle() 0 3 1
A canPrePopulate() 0 3 1
B buildSummaryBlock() 0 36 3
A appendStatusFooter() 0 10 1
B fetchFilterableOperators() 0 30 1
A displayDatasourceFilterPanel() 0 15 4
A cleanValue() 0 3 1
B displayFilteringOptions() 0 25 3
B setFromPOST() 0 7 6
A offsetSet() 0 3 1
A processRawFieldData() 0 6 1
A setArray() 0 8 3
B commit() 0 24 5
A findRelatedEntries() 0 15 2
A prepareAssociationsDrawerXMLElement() 0 17 2
A get() 0 11 3
B buildFilterSQL() 0 38 6
A __construct() 0 3 2
A fetchAssociatedEntrySearchValue() 0 3 1
A handle() 0 3 1
A appendFormattedElement() 0 5 2
A remove() 0 3 1
A canShowAssociationColumn() 0 3 1
A findParentRelatedEntries() 0 15 2
B getAssociationContext() 0 23 5
C appendAssociationInterfaceSelect() 0 50 9
A appendRequiredCheckbox() 0 7 2
A set() 0 3 1
A canShowTableColumn() 0 3 1
A buildSortingSQL() 0 13 3
A isRandomOrder() 0 9 2
A buildLocationSelect() 0 16 2
A fetchIncludableElements() 0 3 1
A appendShowAssociationCheckbox() 0 8 2
A getToggleStates() 0 3 1
A name() 0 3 2
A displayPublishPanel() 0 2 1
A canPublishFilter() 0 3 1
A createTable() 0 23 1
A fetchAssociatedEntryCount() 0 2 1
A tearDown() 0 3 1
A allowDatasourceParamOutput() 0 3 1
A createAssociationsDrawerXMLElement() 0 7 1
A allowDatasourceOutputGrouping() 0 3 1
A offsetGet() 0 3 1
A mustBeUnique() 0 3 1
B buildDSRetrievalSQL() 0 55 7
B buildValidationSelect() 0 25 4
A __clone() 0 5 2
A requiresTable() 0 3 1
A prepareTableValue() 0 11 2
A getParameterPoolValue() 0 3 1
A toggleFieldData() 0 3 1
A offsetExists() 0 3 1
A canFilter() 0 3 1
A groupRecords() 0 4 1
B buildFormatterSelect() 0 24 5
A getEntryQueryFieldAdapter() 0 3 1
A appendShowColumnCheckbox() 0 7 2
A isSortable() 0 3 1
A findDefaults() 0 2 1
A setAssociationContext() 0 12 2
A entryDataCleanup() 0 10 2
C exists() 0 44 7
A displaySettingsPanel() 0 21 4
C buildRegexSQL() 0 45 7
A buildSortingSelectSQL() 0 16 4

How to fix   Complexity   

Complex Class

Complex classes like Field often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Field, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package toolkit
4
 */
5
/**
6
 * The Field class represents a Symphony Field object. Fields are the building
7
 * blocks for Sections. All fields instances are unique and can only be used once
8
 * in a Symphony install. Fields have their own field table which records where
9
 * instances of this field type have been used in other sections and their settings.
10
 * They also spinoff other `tbl_entry_data_{id}` tables that actually store data for
11
 * entries particular to this field.
12
 *
13
 * @since Symphony 3.0.0 it implements the ArrayAccess interface.
14
 */
15
class Field implements ArrayAccess
16
{
17
    /**
18
     * The desired result when creating a field in the section editor
19
     * @var integer
20
     */
21
    const __OK__ = 100;
22
23
    /**
24
     * If an error occurring when saving a section because of this field,
25
     * this will be returned
26
     * @var integer
27
     */
28
    const __ERROR__ = 150;
29
30
    /**
31
     * When saving a section, if a value that is required is missing,
32
     * this will be returned
33
     * @var integer
34
     */
35
    const __MISSING_FIELDS__ = 200;
36
37
    /**
38
     * If a value for a setting is invalid, this will be returned
39
     * @var integer
40
     */
41
    const __INVALID_FIELDS__ = 220;
42
43
    /**
44
     * If there already is an instance of this field in this section and
45
     * `mustBeUnique()` returns true, this will be returned
46
     * @var integer
47
     * @see mustBeUnique()
48
     */
49
    const __DUPLICATE__ = 300;
50
51
    /**
52
     * Fields can returned this is an error occurred when saving the
53
     * field's settings that doesn't fit another `Field` constant
54
     * @var integer
55
     */
56
    const __ERROR_CUSTOM__ = 400;
57
58
    /**
59
     * If the field name is not a valid QName, this error will be returned
60
     * @var integer
61
     */
62
    const __INVALID_QNAME__ = 500;
63
64
    /**
65
     * Used by the `FieldManager` to return fields that can be toggled
66
     * @var integer
67
     */
68
    const __TOGGLEABLE_ONLY__ = 600;
69
70
    /**
71
     * Used by the `FieldManager` to return fields that can't be toggled
72
     * @var integer
73
     */
74
    const __UNTOGGLEABLE_ONLY__ = 700;
75
76
    /**
77
     * Used by the `FieldManager` to return fields that can be filtered
78
     * @var integer
79
     */
80
    const __FILTERABLE_ONLY__ = 800;
81
82
    /**
83
     * Used by the `FieldManager` to return fields that can't be filtered
84
     * @var integer
85
     */
86
    const __UNFILTERABLE_ONLY__ = 900;
87
88
    /**
89
     * Used by the `FieldManager` to just return all fields
90
     * @var integer
91
     */
92
    const __FIELD_ALL__ = 1000;
93
94
    /**
95
     * Used to manage the joins when this field used in a datasource
96
     * @deprecated @since Symphony 3.0.0
97
     * @var integer
98
     */
99
    protected $_key = 0;
100
101
    /**
102
     * An associative array of the settings for this `Field` instance
103
     * @var array
104
     */
105
    protected $_settings = array();
106
107
    /**
108
     * Whether this field is required inherently, defaults to false.
109
     * @var boolean
110
     */
111
    protected $_required = false;
112
113
    /**
114
     * Whether this field can be viewed on the entries table. Note
115
     * that this is not the same variable as the one set when saving
116
     * a field in the section editor, rather just the if the field has
117
     * the ability to be shown. Defaults to true.
118
     * @var boolean
119
     */
120
    protected $_showcolumn = true;
121
122
    /**
123
     * Whether this field has an association that should be shown on
124
     * the Publish Index. This does not mean that it will be, but just
125
     * that this field has the ability too. Defaults to false.
126
     * @var boolean
127
     */
128
    protected $_showassociation = false;
129
130
    /**
131
     * The entry query field adapter object, responsible for filter and sort.
132
     * The default class does not set a default EntryQueryFieldAdapter object
133
     * to allow the compatibility layer to work. In later versions, this can change.
134
     * @since Symphony 3.0.0
135
     * @var EntryQueryFieldAdapter
136
     */
137
    protected $entryQueryFieldAdapter = null;
138
139
    /**
140
     * Construct a new instance of this field.
141
     */
142
    public function __construct()
143
    {
144
        $this->_handle = (strtolower(get_class($this)) == 'field' ? 'field' : strtolower(substr(get_class($this), 5)));
0 ignored issues
show
Bug Best Practice introduced by
The property _handle does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
145
    }
146
147
    /**
148
     * Adjust the newly cloned field in order to fix circular references.
149
     *
150
     * @return void
151
     */
152
    public function __clone()
153
    {
154
        if ($this->entryQueryFieldAdapter) {
155
            $eqfaClass = get_class($this->entryQueryFieldAdapter);
156
            $this->entryQueryFieldAdapter = new $eqfaClass($this);
157
        }
158
    }
159
160
    /**
161
     * Test whether this field can show the table column.
162
     *
163
     * @return boolean
164
     *  true if this can, false otherwise.
165
     */
166
    public function canShowTableColumn()
167
    {
168
        return $this->_showcolumn;
169
    }
170
171
    /**
172
     * Test whether this field can show the association column in
173
     * the Publish Index.
174
     *
175
     * @since Symphony 2.6.0
176
     * @return boolean
177
     *  true if this can, false otherwise.
178
     */
179
    public function canShowAssociationColumn()
180
    {
181
        return $this->_showassociation;
182
    }
183
184
    /**
185
     * Test whether this field can be toggled using the With Selected menu
186
     * on the Publish Index.
187
     *
188
     * @return boolean
189
     *  true if it can be toggled, false otherwise.
190
     */
191
    public function canToggle()
192
    {
193
        return false;
194
    }
195
196
    /**
197
     * Accessor to the toggle states. This default implementation returns
198
     * an empty array.
199
     *
200
     * @return array
201
     *  the array of toggle states.
202
     */
203
    public function getToggleStates()
204
    {
205
        return array();
206
    }
207
208
    /**
209
     * Toggle the field data. This default implementation always returns
210
     * the input data.
211
     *
212
     * @param array $data
213
     *   the data to toggle.
214
     * @param string $newState
215
     *   the new value to set
216
     * @param integer $entry_id (optional)
217
     *   an optional entry ID for more intelligent processing. defaults to null
218
     * @return array
219
     *   the toggled data.
220
     */
221
    public function toggleFieldData(array $data, $newState, $entry_id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $newState is not used and could be removed. ( Ignorable by Annotation )

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

221
    public function toggleFieldData(array $data, /** @scrutinizer ignore-unused */ $newState, $entry_id = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
222
    {
223
        return $data;
224
    }
225
226
    /**
227
     * Test whether this field can be filtered. This default implementation
228
     * prohibits filtering. Filtering allows the XML output results to be limited
229
     * according to an input parameter. Subclasses should override this if
230
     * filtering is supported.
231
     *
232
     * @return boolean
233
     *  true if this can be filtered, false otherwise.
234
     */
235
    public function canFilter()
236
    {
237
        return false;
238
    }
239
240
    /**
241
     * Test whether this field can be filtered in the publish index. This default
242
     * implementation prohibts filtering. Publish Filtering allows the index view
243
     * to filter results. Subclasses should override this if
244
     * filtering is supported.
245
     *
246
     * @return boolean
247
     *  true if this can be publish-filtered, false otherwise.
248
     */
249
    public function canPublishFilter()
250
    {
251
        return $this->canFilter();
252
    }
253
254
    /**
255
     * Test whether this field can be prepopulated with data. This default
256
     * implementation does not support pre-population and, thus, returns false.
257
     *
258
     * @return boolean
259
     *  true if this can be pre-populated, false otherwise.
260
     */
261
    public function canPrePopulate()
262
    {
263
        return false;
264
    }
265
266
    /**
267
     * Test whether this field can be sorted. This default implementation
268
     * returns false.
269
     *
270
     * @return boolean
271
     *  true if this field is sortable, false otherwise.
272
     */
273
    public function isSortable()
274
    {
275
        return false;
276
    }
277
278
    /**
279
     * Test whether this field must be unique in a section, that is, only one of
280
     * this field's type is allowed per section. This default implementation
281
     * always returns false.
282
     *
283
     * @return boolean
284
     *  true if the content of this field must be unique, false otherwise.
285
     */
286
    public function mustBeUnique()
287
    {
288
        return false;
289
    }
290
291
    /**
292
     * Test whether this field supports data source output grouping. This
293
     * default implementation prohibits grouping. Data-source grouping allows
294
     * clients of this field to group the XML output according to this field.
295
     * Subclasses should override this if grouping is supported.
296
     *
297
     * @return boolean
298
     *  true if this field does support data source grouping, false otherwise.
299
     */
300
    public function allowDatasourceOutputGrouping()
301
    {
302
        return false;
303
    }
304
305
    /**
306
     * Test whether this field requires grouping. If this function returns true
307
     * SQL statements generated in the `EntryManager` will include the `DISTINCT` keyword
308
     * to only return a single row for an entry regardless of how many 'matches' it
309
     * might have. This default implementation returns false.
310
     *
311
     * @return boolean
312
     *  true if this field requires grouping, false otherwise.
313
     */
314
    public function requiresSQLGrouping()
315
    {
316
        return false;
317
    }
318
319
    /**
320
     * Test whether this field supports data source parameter output. This
321
     * default implementation prohibits parameter output. Data-source
322
     * parameter output allows this field to be provided as a parameter
323
     * to other data sources or XSLT. Subclasses should override this if
324
     * parameter output is supported.
325
     *
326
     * @return boolean
327
     *  true if this supports data source parameter output, false otherwise.
328
     */
329
    public function allowDatasourceParamOutput()
330
    {
331
        return false;
332
    }
333
334
    /**
335
     * Accessor to the handle of this field object. The Symphony convention is
336
     * for field subclass names to be prefixed with field. Handle removes this prefix
337
     * so that the class handle can be used as the field type.
338
     *
339
     * @return string
340
     *  The field classname minus the field prefix.
341
     */
342
    public function handle()
343
    {
344
        return $this->_handle;
345
    }
346
347
    /**
348
     * Accessor to the name of this field object. The name may contain characters
349
     * that normally would be stripped in the handle while also allowing the field
350
     * name to be localized. If a name is not set, it will return the handle of the
351
     * the field
352
     *
353
     * @return string
354
     *  The field name
355
     */
356
    public function name()
357
    {
358
        return ($this->_name ? $this->_name : $this->_handle);
359
    }
360
361
    /**
362
     * Clean the input value using html entity encode.
363
     *
364
     * @param mixed $value
365
     *  the value to clean.
366
     * @return string
367
     *  the cleaned value.
368
     */
369
    public function cleanValue($value)
370
    {
371
        return html_entity_decode($value);
372
    }
373
374
    /**
375
     * Fields have settings that define how that field will act in a section, including
376
     * if it's required, any validators, if it can be shown on the entries table etc. This
377
     * function will set a setting to a value.  This function will set a setting to a value
378
     * overwriting any existing value for this setting
379
     *
380
     * @param string $setting
381
     *  the setting key.
382
     * @param mixed $value
383
     *  the value of the setting.
384
     */
385
    public function set($setting, $value)
386
    {
387
        $this->_settings[$setting] = $value;
388
    }
389
390
    /**
391
     * Add or overwrite the settings of this field by providing an associative array
392
     * of the settings. This will do nothing if the input array is empty. If a setting is
393
     * omitted from the input array, it will not be unset by this function
394
     *
395
     * @param array $array
396
     *  the associative array of settings for this field
397
     */
398
    public function setArray(array $array = array())
399
    {
400
        if (empty($array)) {
401
            return;
402
        }
403
404
        foreach ($array as $setting => $value) {
405
            $this->set($setting, $value);
406
        }
407
    }
408
409
    /**
410
     * Fill the input data array with default values for known keys provided
411
     * these settings are not already set. The input array is then used to set
412
     * the values of the corresponding settings for this field. This function
413
     * is called when a section is saved.
414
     *
415
     * @param array $settings
416
     *  the data array to initialize if necessary.
417
     */
418
    public function setFromPOST(array $settings = array())
419
    {
420
        $settings['location'] = (isset($settings['location']) ? $settings['location'] : 'main');
421
        $settings['required'] = (isset($settings['required']) && $settings['required'] === 'yes' ? 'yes' : 'no');
422
        $settings['show_column'] = (isset($settings['show_column']) && $settings['show_column'] === 'yes' ? 'yes' : 'no');
423
424
        $this->setArray($settings);
425
    }
426
427
    /**
428
     * Accessor to the a setting by name. If no setting is provided all the
429
     * settings of this `Field` instance are returned.
430
     *
431
     * @param string $setting (optional)
432
     *  the name of the setting to access the value for. This is optional and
433
     *  defaults to null in which case all settings are returned.
434
     * @return null|mixed|array
435
     *  the value of the setting if there is one, all settings if the input setting
436
     *  was omitted or null if the setting was supplied but there is no value
437
     *  for that setting.
438
     */
439
    public function get($setting = null)
440
    {
441
        if (is_null($setting)) {
442
            return $this->_settings;
443
        }
444
445
        if (!isset($this->_settings[$setting])) {
446
            return null;
447
        }
448
449
        return $this->_settings[$setting];
450
    }
451
452
    /**
453
     * Unset the value of a setting by the key
454
     *
455
     * @param string $setting
456
     *  the key of the setting to unset.
457
     */
458
    public function remove($setting)
459
    {
460
        unset($this->_settings[$setting]);
461
    }
462
463
464
    /**
465
     * Implementation of ArrayAccess::offsetExists()
466
     *
467
     * @param mixed $offset
468
     * @return bool
469
     */
470
    public function offsetExists($offset)
471
    {
472
        return isset($this->_settings[$offset]);
473
    }
474
475
    /**
476
     * Implementation of ArrayAccess::offsetGet()
477
     *
478
     * @param mixed $offset
479
     * @return mixed
480
     */
481
    public function offsetGet($offset)
482
    {
483
        return $this->_settings[$offset];
484
    }
485
486
    /**
487
     * Implementation of ArrayAccess::offsetSet()
488
     *
489
     * @param mixed $offset
490
     * @param mixed $value
491
     * @return void
492
     */
493
    public function offsetSet($offset, $value)
494
    {
495
        $this->_settings[$offset] = $value;
496
    }
497
498
    /**
499
     * Implementation of ArrayAccess::offsetUnset()
500
     *
501
     * @param mixed $offset
502
     * @return void
503
     */
504
    public function offsetUnset($offset)
505
    {
506
        unset($this->_settings[$offset]);
507
    }
508
509
    /**
510
     * Getter for this field's EntryQuery operations object.
511
     *
512
     * @since Symphony 3.0.0
513
     * @return EntryQueryFieldAdapter
514
     */
515
    public function getEntryQueryFieldAdapter()
516
    {
517
        return $this->entryQueryFieldAdapter;
518
    }
519
520
    /**
521
     * Just prior to the field being deleted, this function allows
522
     * Fields to cleanup any additional things before it is removed
523
     * from the section. This may be useful to remove data from any
524
     * custom field tables or the configuration.
525
     *
526
     * @since Symphony 2.2.1
527
     * @return boolean
528
     */
529
    public function tearDown()
530
    {
531
        return true;
532
    }
533
534
    /**
535
     * Allows a field to set default settings.
536
     *
537
     * @param array $settings
538
     *  the array of settings to populate with their defaults.
539
     */
540
    public function findDefaults(array &$settings)
541
    {
542
    }
543
544
    /**
545
     * Display the default settings panel, calls the `buildSummaryBlock`
546
     * function after basic field settings are added to the wrapper.
547
     *
548
     * @see buildSummaryBlock()
549
     * @param XMLElement $wrapper
550
     *    the input XMLElement to which the display of this will be appended.
551
     * @param mixed $errors
552
     *  the input error collection. this defaults to null.
553
     * @throws InvalidArgumentException
554
     */
555
    public function displaySettingsPanel(XMLElement &$wrapper, $errors = null)
556
    {
557
        // Create header
558
        $location = ($this->get('location') ? $this->get('location') : 'main');
559
        $header = new XMLElement('header', null, array(
560
            'class' => 'frame-header ' . $location,
561
            'data-name' => $this->name(),
562
            'title' => $this->get('id'),
563
        ));
564
        $label = (($this->get('label')) ? $this->get('label') : __('New Field'));
565
        $header->appendChild(new XMLElement('h4', '<strong>' . $label . '</strong> <span class="type">' . $this->name() . '</span>'));
566
        $wrapper->appendChild($header);
567
568
        // Create content
569
        $wrapper->appendChild(Widget::Input('fields['.$this->get('sortorder').'][type]', $this->handle(), 'hidden'));
0 ignored issues
show
Bug introduced by
Are you sure $this->get('sortorder') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

569
        $wrapper->appendChild(Widget::Input('fields['./** @scrutinizer ignore-type */ $this->get('sortorder').'][type]', $this->handle(), 'hidden'));
Loading history...
570
571
        if ($this->get('id')) {
572
            $wrapper->appendChild(Widget::Input('fields['.$this->get('sortorder').'][id]', $this->get('id'), 'hidden'));
0 ignored issues
show
Bug introduced by
It seems like $this->get('id') can also be of type array; however, parameter $value of Widget::Input() does only seem to accept string, 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

572
            $wrapper->appendChild(Widget::Input('fields['.$this->get('sortorder').'][id]', /** @scrutinizer ignore-type */ $this->get('id'), 'hidden'));
Loading history...
573
        }
574
575
        $wrapper->appendChild($this->buildSummaryBlock($errors));
576
    }
577
578
    /**
579
     * Construct the html block to display a summary of this field, which is the field
580
     * Label and it's location within the section. Any error messages generated are
581
     * appended to the optional input error array. This function calls
582
     * `buildLocationSelect` once it is completed
583
     *
584
     * @see buildLocationSelect()
585
     * @param array $errors (optional)
586
     *    an array to append html formatted error messages to. this defaults to null.
587
     * @throws InvalidArgumentException
588
     * @return XMLElement
589
     *    the root XML element of the html display of this.
590
     */
591
    public function buildSummaryBlock($errors = null)
592
    {
593
        $div = new XMLElement('div');
594
595
        // Publish label
596
        $label = Widget::Label(__('Label'));
597
        $label->appendChild(
598
            Widget::Input('fields['.$this->get('sortorder').'][label]', $this->get('label'))
0 ignored issues
show
Bug introduced by
Are you sure $this->get('sortorder') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

598
            Widget::Input('fields['./** @scrutinizer ignore-type */ $this->get('sortorder').'][label]', $this->get('label'))
Loading history...
Bug introduced by
It seems like $this->get('label') can also be of type array; however, parameter $value of Widget::Input() does only seem to accept string, 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

598
            Widget::Input('fields['.$this->get('sortorder').'][label]', /** @scrutinizer ignore-type */ $this->get('label'))
Loading history...
599
        );
600
        if (isset($errors['label'])) {
601
            $div->appendChild(Widget::Error($label, $errors['label']));
602
        } else {
603
            $div->appendChild($label);
604
        }
605
606
        // Handle + placement
607
        $group = new XMLElement('div');
608
        $group->setAttribute('class', 'two columns');
609
610
        $label = Widget::Label(__('Handle'));
611
        $label->setAttribute('class', 'column');
612
613
        $label->appendChild(Widget::Input('fields['.$this->get('sortorder').'][element_name]', $this->get('element_name')));
614
615
        if (isset($errors['element_name'])) {
616
            $group->appendChild(Widget::Error($label, $errors['element_name']));
617
        } else {
618
            $group->appendChild($label);
619
        }
620
621
        // Location
622
        $group->appendChild($this->buildLocationSelect($this->get('location'), 'fields['.$this->get('sortorder').'][location]'));
0 ignored issues
show
Bug introduced by
It seems like $this->get('location') can also be of type array; however, parameter $selected of Field::buildLocationSelect() does only seem to accept null|string, 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

622
        $group->appendChild($this->buildLocationSelect(/** @scrutinizer ignore-type */ $this->get('location'), 'fields['.$this->get('sortorder').'][location]'));
Loading history...
623
624
        $div->appendChild($group);
625
626
        return $div;
627
    }
628
629
    /**
630
     * Build the location select widget. This widget allows users to select
631
     * whether this field will appear in the main content column or in the sidebar
632
     * when creating a new entry.
633
     *
634
     * @param string|null $selected (optional)
635
     *    the currently selected location, if there is one. this defaults to null.
636
     * @param string $name (optional)
637
     *    the name of this field. this is optional and defaults to `fields[location]`.
638
     * @param string $label_value (optional)
639
     *    any predefined label for this widget. this is an optional argument that defaults
640
     *    to null.
641
     * @throws InvalidArgumentException
642
     * @return XMLElement
643
     *    An XMLElement representing a `<select>` field containing the options.
644
     */
645
    public function buildLocationSelect($selected = null, $name = 'fields[location]', $label_value = null)
646
    {
647
        if (!$label_value) {
648
            $label_value = __('Placement');
649
        }
650
651
        $label = Widget::Label($label_value);
652
        $label->setAttribute('class', 'column');
653
654
        $options = array(
655
            array('main', $selected == 'main', __('Main content')),
656
            array('sidebar', $selected == 'sidebar', __('Sidebar'))
657
        );
658
        $label->appendChild(Widget::Select($name, $options));
659
660
        return $label;
661
    }
662
663
    /**
664
     * Construct the html widget for selecting a text formatter for this field.
665
     *
666
     * @param string $selected (optional)
667
     *    the currently selected text formatter name if there is one. this defaults
668
     *    to null.
669
     * @param string $name (optional)
670
     *    the name of this field in the form. this is optional and defaults to
671
     *    "fields[format]".
672
     * @param string $label_value
673
     *    the default label for the widget to construct. if null is passed in then
674
     *    this defaults to the localization of "Formatting".
675
     * @throws InvalidArgumentException
676
     * @return XMLElement
677
     *    An XMLElement representing a `<select>` field containing the options.
678
     */
679
    public function buildFormatterSelect($selected = null, $name = 'fields[format]', $label_value)
680
    {
681
        $formatters = TextformatterManager::listAll();
682
683
        if (!$label_value) {
684
            $label_value = __('Formatting');
685
        }
686
687
        $label = Widget::Label($label_value);
688
        $label->setAttribute('class', 'column');
689
690
        $options = array();
691
692
        $options[] = array('none', false, __('None'));
693
694
        if (!empty($formatters) && is_array($formatters)) {
695
            foreach ($formatters as $handle => $about) {
696
                $options[] = array($handle, ($selected == $handle), $about['name']);
697
            }
698
        }
699
700
        $label->appendChild(Widget::Select($name, $options));
701
702
        return $label;
703
    }
704
705
    /**
706
     * Append a validator selector to a given `XMLElement`. Note that this
707
     * function differs from the other two similarly named build functions in
708
     * that it takes an `XMLElement` to append the Validator to as a parameter,
709
     * and does not return anything.
710
     *
711
     * @param XMLElement $wrapper
712
     *    the parent element to append the XMLElement of the Validation select to,
713
     *  passed by reference.
714
     * @param string $selected (optional)
715
     *    the current validator selection if there is one. defaults to null if there
716
     *    isn't.
717
     * @param string $name (optional)
718
     *    the form element name of this field. this defaults to "fields[validator]".
719
     * @param string $type (optional)
720
     *    the type of input for the validation to apply to. this defaults to 'input'
721
     *    but also accepts 'upload'.
722
     * @param array $errors (optional)
723
     *    an associative array of errors
724
     * @throws InvalidArgumentException
725
     */
726
    public function buildValidationSelect(XMLElement &$wrapper, $selected = null, $name = 'fields[validator]', $type = 'input', array $errors = null)
727
    {
728
        include TOOLKIT . '/util.validators.php';
0 ignored issues
show
Bug introduced by
The constant TOOLKIT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
729
730
        $rules = ($type == 'upload' ? $upload : $validators);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $upload seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $validators seems to be never defined.
Loading history...
731
732
        $label = Widget::Label(__('Validation Rule'));
733
        $label->setAttribute('class', 'column');
734
        $label->appendChild(new XMLElement('i', __('Optional')));
735
        $label->appendChild(Widget::Input($name, $selected));
736
737
        $ul = new XMLElement('ul', null, array('class' => 'tags singular', 'data-interactive' => 'data-interactive'));
738
        foreach ($rules as $name => $rule) {
739
            $ul->appendChild(new XMLElement('li', $name, array('class' => $rule)));
740
        }
741
742
        if (isset($errors['validator'])) {
743
            $div = new XMLElement('div');
744
            $div->appendChild($label);
745
            $div->appendChild($ul);
746
747
            $wrapper->appendChild(Widget::Error($div, $errors['validator']));
748
        } else {
749
            $wrapper->appendChild($label);
750
            $wrapper->appendChild($ul);
751
        }
752
    }
753
754
    /**
755
     * Append the html widget for selecting an association interface and editor
756
     * for this field.
757
     *
758
     * @param XMLElement $wrapper
759
     *    the parent XML element to append the association interface selection to,
760
     *    if either interfaces or editors are provided to the system.
761
     * @since Symphony 2.5.0
762
     */
763
    public function appendAssociationInterfaceSelect(XMLElement &$wrapper)
764
    {
765
        $wrapper->setAttribute('data-condition', 'associative');
766
767
        $interfaces = Symphony::ExtensionManager()->getProvidersOf(iProvider::ASSOCIATION_UI);
768
        $editors = Symphony::ExtensionManager()->getProvidersOf(iProvider::ASSOCIATION_EDITOR);
769
770
        if (!empty($interfaces) || !empty($editors)) {
771
            $association_context = $this->getAssociationContext();
772
773
            $group = new XMLElement('div');
774
            if (!empty($interfaces) && !empty($editors)) {
775
                $group->setAttribute('class', 'two columns');
776
            }
777
778
            // Create interface select
779
            if (!empty($interfaces)) {
780
                $label = Widget::Label(__('Association Interface'), null, 'column');
781
                $label->appendChild(new XMLElement('i', __('Optional')));
782
783
                $options = array(
784
                    array(null, false, __('None'))
785
                );
786
                foreach ($interfaces as $id => $name) {
787
                    $options[] = array($id, ($association_context['interface'] === $id), $name);
788
                }
789
790
                $select = Widget::Select('fields[' . $this->get('sortorder') . '][association_ui]', $options);
0 ignored issues
show
Bug introduced by
Are you sure $this->get('sortorder') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

790
                $select = Widget::Select('fields[' . /** @scrutinizer ignore-type */ $this->get('sortorder') . '][association_ui]', $options);
Loading history...
791
                $label->appendChild($select);
792
                $group->appendChild($label);
793
            }
794
795
            // Create editor select
796
            if (!empty($editors)) {
797
                $label = Widget::Label(__('Association Editor'), null, 'column');
798
                $label->appendChild(new XMLElement('i', __('Optional')));
799
800
                $options = array(
801
                    array(null, false, __('None'))
802
                );
803
                foreach ($editors as $id => $name) {
804
                    $options[] = array($id, ($association_context['editor'] === $id), $name);
805
                }
806
807
                $select = Widget::Select('fields[' . $this->get('sortorder') . '][association_editor]', $options);
808
                $label->appendChild($select);
809
                $group->appendChild($label);
810
            }
811
812
            $wrapper->appendChild($group);
813
        }
814
    }
815
816
    /**
817
     * Get association data of the current field from the page context.
818
     *
819
     * @since Symphony 2.5.0
820
     * @return array
821
     */
822
    public function getAssociationContext()
823
    {
824
        $context = Symphony::Engine()->Page->getContext();
0 ignored issues
show
Bug introduced by
The property Page does not seem to exist on Frontend.
Loading history...
Bug introduced by
The method getContext() does not exist on HTMLPage. It seems like you code against a sub-type of HTMLPage such as AdministrationPage. ( Ignorable by Annotation )

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

824
        /** @scrutinizer ignore-call */ 
825
        $context = Symphony::Engine()->Page->getContext();
Loading history...
825
        $associations = $context['associations']['parent'];
826
        $field_association = array();
827
        $count = 0;
828
829
        if (!empty($associations)) {
830
            $associationsCount = count($associations);
831
            for ($i = 0; $i < $associationsCount; $i++) {
832
                if ($associations[$i]['child_section_field_id'] == $this->get('id')) {
833
                    if ($count === 0) {
834
                        $field_association = $associations[$i];
835
                        $count++;
836
                    } else {
837
                        $field_association['parent_section_id'] .= '|' . $associations[$i]['parent_section_id'];
838
                        $field_association['parent_section_field_id'] .= '|' . $associations[$i]['parent_section_field_id'];
839
                    }
840
                }
841
            }
842
        }
843
844
        return $field_association;
845
    }
846
847
    /**
848
     * Set association data for the current field.
849
     *
850
     * @since Symphony 2.5.0
851
     * @param XMLElement $wrapper
852
     */
853
    public function setAssociationContext(XMLElement &$wrapper)
854
    {
855
        $association_context = $this->getAssociationContext();
856
857
        if (!empty($association_context)) {
858
            $wrapper->setAttributeArray(array(
859
                'data-parent-section-id' => $association_context['parent_section_id'],
860
                'data-parent-section-field-id' => $association_context['parent_section_field_id'],
861
                'data-child-section-id' => $association_context['child_section_id'],
862
                'data-child-section-field-id' => $association_context['child_section_field_id'],
863
                'data-interface' => $association_context['interface'],
864
                'data-editor' => $association_context['editor']
865
            ));
866
        }
867
    }
868
869
    /**
870
     * Append and set a labeled html checkbox to the input XML element if this
871
     * field is set as a required field.
872
     *
873
     * @param XMLElement $wrapper
874
     *    the parent XML element to append the constructed html checkbox to if
875
     *    necessary.
876
     * @throws InvalidArgumentException
877
     */
878
    public function appendRequiredCheckbox(XMLElement &$wrapper)
879
    {
880
        if (!$this->_required) {
881
            return;
882
        }
883
884
        $this->createCheckboxSetting($wrapper, 'required', __('Make this a required field'));
885
    }
886
887
    /**
888
     * Append the show column html widget to the input parent XML element. This
889
     * displays a column in the entries table or not.
890
     *
891
     * @param XMLElement $wrapper
892
     *    the parent XML element to append the checkbox to.
893
     * @throws InvalidArgumentException
894
     */
895
    public function appendShowColumnCheckbox(XMLElement &$wrapper)
896
    {
897
        if (!$this->_showcolumn) {
898
            return;
899
        }
900
901
        $this->createCheckboxSetting($wrapper, 'show_column', __('Display in entries table'));
902
    }
903
904
    /**
905
     * Append the show association html widget to the input parent XML element. This
906
     * widget allows fields that provide linking to hide or show the column in the linked
907
     * section, similar to how the Show Column functionality works, but for the linked
908
     * section.
909
     *
910
     * @param XMLElement $wrapper
911
     *    the parent XML element to append the checkbox to.
912
     * @param string $help (optional)
913
     *    a help message to show below the checkbox.
914
     * @throws InvalidArgumentException
915
     */
916
    public function appendShowAssociationCheckbox(XMLElement &$wrapper, $help = null)
917
    {
918
        if (!$this->_showassociation) {
919
            return;
920
        }
921
922
        $label = $this->createCheckboxSetting($wrapper, 'show_association', __('Display associations in entries table'), $help);
923
        $label->setAttribute('data-condition', 'associative');
924
    }
925
926
    /**
927
     * Given the setting name and the label, this helper method will add
928
     * the required markup for a checkbox to the given `$wrapper`.
929
     *
930
     * @since Symphony 2.5.2
931
     * @param XMLElement $wrapper
932
     *  Passed by reference, this will have the resulting markup appended to it
933
     * @param string $setting
934
     *  This will be used with $this->get() to get the existing value
935
     * @param string $label_description
936
     *  This will be localisable and displayed after the checkbox when
937
     *  generated.
938
     * @param string $help (optional)
939
     *    A help message to show below the checkbox.
940
     * @return XMLElement
941
     *  The Label and Checkbox that was just added to the `$wrapper`.
942
     */
943
    public function createCheckboxSetting(XMLElement &$wrapper, $setting, $label_description, $help = null)
944
    {
945
        $order = $this->get('sortorder');
946
        $name = "fields[$order][$setting]";
947
948
        $label = Widget::Checkbox($name, $this->get($setting), $label_description, $wrapper, $help);
0 ignored issues
show
Bug introduced by
It seems like $this->get($setting) can also be of type array; however, parameter $value of Widget::Checkbox() does only seem to accept string, 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

948
        $label = Widget::Checkbox($name, /** @scrutinizer ignore-type */ $this->get($setting), $label_description, $wrapper, $help);
Loading history...
949
        $label->addClass('column');
950
951
        return $label;
952
    }
953
954
    /**
955
     * Append the default status footer to the field settings panel.
956
     * Displays the required and show column checkboxes.
957
     *
958
     * @param XMLElement $wrapper
959
     *    the parent XML element to append the checkbox to.
960
     * @throws InvalidArgumentException
961
     */
962
    public function appendStatusFooter(XMLElement &$wrapper)
963
    {
964
        $fieldset = new XMLElement('fieldset');
965
        $div = new XMLElement('div', null, array('class' => 'two columns'));
966
967
        $this->appendRequiredCheckbox($div);
968
        $this->appendShowColumnCheckbox($div);
969
970
        $fieldset->appendChild($div);
971
        $wrapper->appendChild($fieldset);
972
    }
973
974
    /**
975
     * Check the field's settings to ensure they are valid on the section
976
     * editor
977
     *
978
     * @param array $errors
979
     *  the array to populate with the errors found.
980
     * @param boolean $checkForDuplicates (optional)
981
     *  if set to true, duplicate Field name's in the same section will be flagged
982
     *  as errors. Defaults to true.
983
     * @return integer
984
     *  returns the status of the checking. if errors has been populated with
985
     *  any errors `self::__ERROR__`, `self::__OK__` otherwise.
986
     */
987
    public function checkFields(array &$errors, $checkForDuplicates = true)
988
    {
989
        $parent_section = $this->get('parent_section');
990
        $label = $this->get('label');
991
        $element_name = $this->get('element_name');
992
993
        if (Lang::isUnicodeCompiled()) {
994
            $valid_name = preg_match('/^[\p{L}]([0-9\p{L}\.\-\_]+)?$/u', $element_name);
0 ignored issues
show
Bug introduced by
It seems like $element_name can also be of type array; however, parameter $subject of preg_match() does only seem to accept string, 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

994
            $valid_name = preg_match('/^[\p{L}]([0-9\p{L}\.\-\_]+)?$/u', /** @scrutinizer ignore-type */ $element_name);
Loading history...
995
        } else {
996
            $valid_name = preg_match('/^[A-z]([\w\d-_\.]+)?$/i', $element_name);
997
        }
998
999
        if ($label === '') {
1000
            $errors['label'] = __('This is a required field.');
1001
        } elseif (strtolower($label) === 'id') {
0 ignored issues
show
Bug introduced by
It seems like $label can also be of type array; however, parameter $str of strtolower() does only seem to accept string, 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

1001
        } elseif (strtolower(/** @scrutinizer ignore-type */ $label) === 'id') {
Loading history...
1002
            $errors['label'] = __('%s is a reserved name used by the system and is not allowed for a field handle. Try using %s instead.', array('<code>ID</code>', '<code>UID</code>'));
1003
        }
1004
1005
        if ($element_name === '') {
1006
            $errors['element_name'] = __('This is a required field.');
1007
        } elseif ($element_name === 'id') {
1008
            $errors['element_name'] = __('%s is a reserved name used by the system and is not allowed for a field handle. Try using %s instead.', array('<code>id</code>', '<code>uid</code>'));
1009
        } elseif (!$valid_name) {
1010
            $errors['element_name'] = __('Invalid element name. Must be valid %s.', array('<code>QName</code>'));
1011
        } elseif ($checkForDuplicates) {
1012
            if (FieldManager::fetchFieldIDFromElementName($element_name, $parent_section) !== $this->get('id')) {
0 ignored issues
show
Bug introduced by
It seems like $parent_section can also be of type array; however, parameter $section_id of FieldManager::fetchFieldIDFromElementName() does only seem to accept integer, 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

1012
            if (FieldManager::fetchFieldIDFromElementName($element_name, /** @scrutinizer ignore-type */ $parent_section) !== $this->get('id')) {
Loading history...
1013
                $errors['element_name'] = __('A field with that element name already exists. Please choose another.');
1014
            }
1015
        }
1016
1017
        // Check that if the validator is provided that it's a valid regular expression
1018
        if (!is_null($this->get('validator')) && $this->get('validator') !== '') {
1019
            if (@preg_match($this->get('validator'), 'teststring') === false) {
0 ignored issues
show
Bug introduced by
It seems like $this->get('validator') can also be of type array; however, parameter $pattern of preg_match() does only seem to accept string, 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

1019
            if (@preg_match(/** @scrutinizer ignore-type */ $this->get('validator'), 'teststring') === false) {
Loading history...
1020
                $errors['validator'] = __('Validation rule is not a valid regular expression');
1021
            }
1022
        }
1023
1024
        return (!empty($errors) ? self::__ERROR__ : self::__OK__);
1025
    }
1026
1027
    /**
1028
     * Format this field value for display in the publish index tables.
1029
     *
1030
     * Since Symphony 2.5.0, this function will call `Field::prepareReadableValue`
1031
     * in order to get the field's human readable value.
1032
     *
1033
     * @param array $data
1034
     *  an associative array of data for this string. At minimum this requires a
1035
     *  key of 'value'.
1036
     * @param XMLElement $link (optional)
1037
     *  an XML link structure to append the content of this to provided it is not
1038
     *  null. it defaults to null.
1039
     * @param integer $entry_id (optional)
1040
     *  An option entry ID for more intelligent processing. defaults to null
1041
     * @return string
1042
     *  the formatted string summary of the values of this field instance.
1043
     */
1044
    public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
1045
    {
1046
        $value = $this->prepareReadableValue($data, $entry_id, true, __('None'));
1047
1048
        if ($link) {
1049
            $link->setValue($value);
1050
1051
            return $link->generate();
1052
        }
1053
1054
        return $value;
1055
    }
1056
1057
    /**
1058
     * Format this field value for display as readable  text value. By default, it
1059
     * will call `Field::prepareTextValue` to get the raw text value of this field.
1060
     *
1061
     * If $truncate is set to true, Symphony will truncate the value to the
1062
     * configuration setting `cell_truncation_length`.
1063
     *
1064
     * @since Symphony 2.5.0
1065
     * @param array $data
1066
     *  an associative array of data for this string. At minimum this requires a
1067
     *  key of 'value'.
1068
     * @param integer $entry_id (optional)
1069
     *  An option entry ID for more intelligent processing. Defaults to null.
1070
     * @param string $defaultValue (optional)
1071
     *  The value to use when no plain text representation of the field's data
1072
     *  can be made. Defaults to null.
1073
     * @return string
1074
     *  the readable text summary of the values of this field instance.
1075
     */
1076
    public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = null)
1077
    {
1078
        $value = $this->prepareTextValue($data, $entry_id);
1079
1080
        if ($truncate) {
1081
            $max_length = Symphony::Configuration()->get('cell_truncation_length', 'symphony');
1082
            $max_length = ($max_length ? $max_length : 75);
1083
1084
            $value = (General::strlen($value) <= $max_length ? $value : General::substr($value, 0, $max_length) . '…');
1085
        }
1086
1087
        if (General::strlen($value) == 0 && $defaultValue != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $defaultValue of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
1088
            $value = $defaultValue;
1089
        }
1090
1091
        return $value;
1092
    }
1093
1094
    /**
1095
     * Format this field value for complete display as text (string). By default,
1096
     * it looks for the 'value' key in the $data array and strip tags from it.
1097
     *
1098
     * @since Symphony 2.5.0
1099
     * @param array $data
1100
     *  an associative array of data for this string. At minimum this requires a
1101
     *  key of 'value'.
1102
     * @param integer $entry_id (optional)
1103
     *  An option entry ID for more intelligent processing. defaults to null
1104
     * @return string
1105
     *  the complete text representation of the values of this field instance.
1106
     */
1107
    public function prepareTextValue($data, $entry_id = null)
1108
    {
1109
        return strip_tags($data['value']);
1110
    }
1111
1112
    /**
1113
     * This is general purpose factory method that makes it easier to create the
1114
     * markup needed in order to create an Associations Drawer XMLElement.
1115
     *
1116
     * @since Symphony 2.5.0
1117
     *
1118
     * @param string $value
1119
     *   The value to display in the link
1120
     * @param Entry $e
1121
     *   The associated entry
1122
     * @param array $parent_association
1123
     *   An array containing information about the association
1124
     * @param string $prepopulate
1125
     *   A string containing prepopulate parameter to append to the association url
1126
     *
1127
     * @return XMLElement
1128
     *   The XMLElement must be a li node, since it will be added an ul node.
1129
     */
1130
    public static function createAssociationsDrawerXMLElement($value, Entry $e, array $parent_association, $prepopulate = '')
1131
    {
1132
        $li = new XMLElement('li');
1133
        $a = new XMLElement('a', $value);
1134
        $a->setAttribute('href', SYMPHONY_URL . '/publish/' . $parent_association['handle'] . '/edit/' . $e->get('id') . '/' . $prepopulate);
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
Are you sure $e->get('id') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1134
        $a->setAttribute('href', SYMPHONY_URL . '/publish/' . $parent_association['handle'] . '/edit/' . /** @scrutinizer ignore-type */ $e->get('id') . '/' . $prepopulate);
Loading history...
1135
        $li->appendChild($a);
1136
        return $li;
1137
    }
1138
1139
    /**
1140
     * Format this field value for display in the Associations Drawer publish index.
1141
     * By default, Symphony will use the return value of the `prepareReadableValue` function.
1142
     *
1143
     * @since Symphony 2.4
1144
     * @since Symphony 2.5.0 The prepopulate parameter was added.
1145
     *
1146
     * @param Entry $e
1147
     *   The associated entry
1148
     * @param array $parent_association
1149
     *   An array containing information about the association
1150
     * @param string $prepopulate
1151
     *   A string containing prepopulate parameter to append to the association url
1152
     *
1153
     * @return XMLElement
1154
     *   The XMLElement must be a li node, since it will be added an ul node.
1155
     */
1156
    public function prepareAssociationsDrawerXMLElement(Entry $e, array $parent_association, $prepopulate = '')
1157
    {
1158
        $value = $this->prepareReadableValue($e->getData($this->get('id')), $e->get('id'));
0 ignored issues
show
Bug introduced by
It seems like $e->get('id') can also be of type array; however, parameter $entry_id of Field::prepareReadableValue() does only seem to accept integer, 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

1158
        $value = $this->prepareReadableValue($e->getData($this->get('id')), /** @scrutinizer ignore-type */ $e->get('id'));
Loading history...
Bug introduced by
It seems like $this->get('id') can also be of type array; however, parameter $field_id of Entry::getData() does only seem to accept integer, 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

1158
        $value = $this->prepareReadableValue($e->getData(/** @scrutinizer ignore-type */ $this->get('id')), $e->get('id'));
Loading history...
Bug introduced by
It seems like $e->getData($this->get('id')) can also be of type object; however, parameter $data of Field::prepareReadableValue() 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

1158
        $value = $this->prepareReadableValue(/** @scrutinizer ignore-type */ $e->getData($this->get('id')), $e->get('id'));
Loading history...
1159
1160
        // fallback for compatibility since the default
1161
        // `preparePlainTextValue` is not compatible with all fields
1162
        // this should be removed in Symphony 3.0
1163
        if (empty($value)) {
1164
            $value = strip_tags($this->prepareTableValue($e->getData($this->get('id')), null, $e->get('id')));
0 ignored issues
show
Bug introduced by
It seems like $e->getData($this->get('id')) can also be of type object; however, parameter $data of Field::prepareTableValue() 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

1164
            $value = strip_tags($this->prepareTableValue(/** @scrutinizer ignore-type */ $e->getData($this->get('id')), null, $e->get('id')));
Loading history...
Bug introduced by
It seems like $e->get('id') can also be of type array; however, parameter $entry_id of Field::prepareTableValue() does only seem to accept integer, 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

1164
            $value = strip_tags($this->prepareTableValue($e->getData($this->get('id')), null, /** @scrutinizer ignore-type */ $e->get('id')));
Loading history...
1165
        }
1166
1167
        // use our factory method to create the html
1168
        $li = self::createAssociationsDrawerXMLElement($value, $e, $parent_association, $prepopulate);
1169
1170
        $li->setAttribute('class', 'field-' . $this->get('type'));
0 ignored issues
show
Bug introduced by
Are you sure $this->get('type') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1170
        $li->setAttribute('class', 'field-' . /** @scrutinizer ignore-type */ $this->get('type'));
Loading history...
1171
1172
        return $li;
1173
    }
1174
1175
    /**
1176
     * Display the publish panel for this field. The display panel is the
1177
     * interface shown to Authors that allow them to input data into this
1178
     * field for an `Entry`.
1179
     *
1180
     * @param XMLElement $wrapper
1181
     *  the XML element to append the html defined user interface to this
1182
     *  field.
1183
     * @param array $data (optional)
1184
     *  any existing data that has been supplied for this field instance.
1185
     *  this is encoded as an array of columns, each column maps to an
1186
     *  array of row indexes to the contents of that column. this defaults
1187
     *  to null.
1188
     * @param mixed $flagWithError (optional)
1189
     *  flag with error defaults to null.
1190
     * @param string $fieldnamePrefix (optional)
1191
     *  the string to be prepended to the display of the name of this field.
1192
     *  this defaults to null.
1193
     * @param string $fieldnamePostfix (optional)
1194
     *  the string to be appended to the display of the name of this field.
1195
     *  this defaults to null.
1196
     * @param integer $entry_id (optional)
1197
     *  the entry id of this field. this defaults to null.
1198
     */
1199
    public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $flagWithError is not used and could be removed. ( Ignorable by Annotation )

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

1199
    public function displayPublishPanel(XMLElement &$wrapper, $data = null, /** @scrutinizer ignore-unused */ $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1200
    {
1201
    }
1202
1203
    /**
1204
     * Check the field data that has been posted from a form. This will set the
1205
     * input message to the error message or to null if there is none. Any existing
1206
     * message value will be overwritten.
1207
     *
1208
     * @param array $data
1209
     *  the input data to check.
1210
     * @param string $message
1211
     *  the place to set any generated error message. any previous value for
1212
     *  this variable will be overwritten.
1213
     * @param integer $entry_id (optional)
1214
     *  the optional id of this field entry instance. this defaults to null.
1215
     * @return integer
1216
     *  `self::__MISSING_FIELDS__` if there are any missing required fields,
1217
     *  `self::__OK__` otherwise.
1218
     */
1219
    public function checkPostFieldData($data, &$message, $entry_id = null)
1220
    {
1221
        $message = null;
1222
1223
        $has_no_value = is_array($data) ? empty($data) : strlen(trim($data)) == 0;
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
1224
1225
        if ($this->get('required') === 'yes' && $has_no_value) {
1226
            $message = __('‘%s’ is a required field.', array($this->get('label')));
1227
1228
            return self::__MISSING_FIELDS__;
1229
        }
1230
1231
        return self::__OK__;
1232
    }
1233
1234
    /**
1235
     * Process the raw field data.
1236
     *
1237
     * @param mixed $data
1238
     *  post data from the entry form
1239
     * @param integer $status
1240
     *  the status code resultant from processing the data.
1241
     * @param string $message
1242
     *  the place to set any generated error message. any previous value for
1243
     *  this variable will be overwritten.
1244
     * @param boolean $simulate (optional)
1245
     *  true if this will tell the CF's to simulate data creation, false
1246
     *  otherwise. this defaults to false. this is important if clients
1247
     *  will be deleting or adding data outside of the main entry object
1248
     *  commit function.
1249
     * @param mixed $entry_id (optional)
1250
     *  the current entry. defaults to null.
1251
     * @return array
1252
     *  the processed field data.
1253
     */
1254
    public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $simulate is not used and could be removed. ( Ignorable by Annotation )

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

1254
    public function processRawFieldData($data, &$status, &$message = null, /** @scrutinizer ignore-unused */ $simulate = false, $entry_id = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1255
    {
1256
        $status = self::__OK__;
1257
1258
        return array(
1259
            'value' => $data,
1260
        );
1261
    }
1262
1263
    /**
1264
     * Returns the keywords that this field supports for filtering. Note
1265
     * that no filter will do a simple 'straight' match on the value.
1266
     *
1267
     * @since Symphony 2.6.0
1268
     * @return array
1269
     */
1270
    public function fetchFilterableOperators()
1271
    {
1272
        return array(
1273
            array(
1274
                'title' => 'is',
1275
                'filter' => ' ',
1276
                'help' => __('Find values that are an exact match for the given string.')
1277
            ),
1278
            array(
1279
                'filter' => 'sql: NOT NULL',
1280
                'title' => 'is not empty',
1281
                'help' => __('Find entries with a non-empty value.')
1282
            ),
1283
            array(
1284
                'filter' => 'sql: NULL',
1285
                'title' => 'is empty',
1286
                'help' => __('Find entries with an empty value.')
1287
            ),
1288
            array(
1289
                'title' => 'contains',
1290
                'filter' => 'regexp: ',
1291
                'help' => __('Find values that match the given <a href="%s">MySQL regular expressions</a>.', array(
1292
                    'https://dev.mysql.com/doc/mysql/en/regexp.html'
1293
                ))
1294
            ),
1295
            array(
1296
                'title' => 'does not contain',
1297
                'filter' => 'not-regexp: ',
1298
                'help' => __('Find values that do not match the given <a href="%s">MySQL regular expressions</a>.', array(
1299
                    'https://dev.mysql.com/doc/mysql/en/regexp.html'
1300
                ))
1301
            ),
1302
        );
1303
    }
1304
1305
    /**
1306
     * Returns the types of filter suggestion this field supports. 
1307
     * The array may contain the following values:
1308
     *
1309
     * - `entry` for searching entries in the current section
1310
     * - `association` for searching entries in associated sections
1311
     * - `static` for searching static values
1312
     * - `date` for searching in a calendar
1313
     * - `parameters` for searching in parameters
1314
     *
1315
     * If the date type is set, only the calendar will be shown in the suggestion dropdown.
1316
     *
1317
     * @since Symphony 2.6.0
1318
     * @return array
1319
     */
1320
    public function fetchSuggestionTypes()
1321
    {
1322
        return array('entry');
1323
    }
1324
1325
    /**
1326
     * Display the default data source filter panel.
1327
     *
1328
     * @param XMLElement $wrapper
1329
     *    the input XMLElement to which the display of this will be appended.
1330
     * @param mixed $data (optional)
1331
     *    the input data. this defaults to null.
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $errors is correct as it would always require null to be passed?
Loading history...
1332
     * @param null $errors
1333
     *  the input error collection. this defaults to null.
1334
     * @param string $fieldnamePrefix
1335
     *  the prefix to apply to the display of this.
1336
     * @param string $fieldnamePostfix
1337
     *  the suffix to apply to the display of this.
1338
     * @throws InvalidArgumentException
1339
     */
1340
    public function displayDatasourceFilterPanel(XMLElement &$wrapper, $data = null, $errors = null, $fieldnamePrefix = null, $fieldnamePostfix = null)
0 ignored issues
show
Unused Code introduced by
The parameter $errors is not used and could be removed. ( Ignorable by Annotation )

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

1340
    public function displayDatasourceFilterPanel(XMLElement &$wrapper, $data = null, /** @scrutinizer ignore-unused */ $errors = null, $fieldnamePrefix = null, $fieldnamePostfix = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1341
    {
1342
        $wrapper->appendChild(new XMLElement('header', '<h4>' . $this->get('label') . '</h4> <span>' . $this->name() . '</span>', array(
0 ignored issues
show
Bug introduced by
Are you sure $this->get('label') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1342
        $wrapper->appendChild(new XMLElement('header', '<h4>' . /** @scrutinizer ignore-type */ $this->get('label') . '</h4> <span>' . $this->name() . '</span>', array(
Loading history...
1343
            'data-name' => $this->get('label') . ' (' . $this->name() . ')'
1344
        )));
1345
1346
        $label = Widget::Label(__('Value'));
1347
        $input = Widget::Input('fields[filter]'.($fieldnamePrefix ? '['.$fieldnamePrefix.']' : '').'['.$this->get('id').']'.($fieldnamePostfix ? '['.$fieldnamePostfix.']' : ''), ($data ? General::sanitize($data) : null));
0 ignored issues
show
Bug introduced by
Are you sure $this->get('id') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1347
        $input = Widget::Input('fields[filter]'.($fieldnamePrefix ? '['.$fieldnamePrefix.']' : '').'['./** @scrutinizer ignore-type */ $this->get('id').']'.($fieldnamePostfix ? '['.$fieldnamePostfix.']' : ''), ($data ? General::sanitize($data) : null));
Loading history...
1348
        $input->setAttribute('autocomplete', 'off');
1349
        $input->setAttribute('data-search-types', 'parameters');
1350
        $input->setAttribute('data-trigger', '{$');
1351
        $label->appendChild($input);
1352
        $wrapper->appendChild($label);
1353
1354
        $this->displayFilteringOptions($wrapper);
1355
    }
1356
1357
    /**
1358
     * Inserts tags at the bottom of the filter panel
1359
     *
1360
     * @since Symphony 2.6.0
1361
     * @param XMLElement $wrapper
1362
     */
1363
    public function displayFilteringOptions(XMLElement &$wrapper)
1364
    {
1365
        // Add filter tags
1366
        $filterTags = new XMLElement('ul');
1367
        $filterTags->setAttribute('class', 'tags singular');
1368
        $filterTags->setAttribute('data-interactive', 'data-interactive');
1369
1370
        $filters = $this->fetchFilterableOperators();
1371
        foreach ($filters as $value) {
1372
            $item = new XMLElement('li', $value['title']);
1373
            $item->setAttribute('data-value', $value['filter']);
1374
1375
            if (isset($value['help'])) {
1376
                $item->setAttribute('data-help', General::sanitize($value['help']));
1377
            }
1378
1379
            $filterTags->appendChild($item);
1380
        }
1381
        $wrapper->appendChild($filterTags);
1382
1383
        $help = new XMLElement('p');
1384
        $help->setAttribute('class', 'help');
1385
        $first = array_shift($filters);
1386
        $help->setValue($first['help']);
1387
        $wrapper->appendChild($help);
1388
    }
1389
1390
    /**
1391
     * Default accessor for the includable elements of this field. This array
1392
     * will populate the `Datasource` included elements. Fields that have
1393
     * different modes will override this and add new items to the array.
1394
     * The Symphony convention is element_name : mode. Modes allow Fields to
1395
     * output different XML in datasources.
1396
     *
1397
     * @return array
1398
     *  the array of includable elements from this field.
1399
     */
1400
    public function fetchIncludableElements()
1401
    {
1402
        return array($this->get('element_name'));
1403
    }
1404
1405
    /**
1406
     * Test whether the input string is a regular expression, by searching
1407
     * for the prefix of `regexp:` or `not-regexp:` in the given `$string`.
1408
     *
1409
     * @deprecated @since Symphony 3.0.0
1410
     *  Use EntryQueryFieldAdapter::isFilterRegex() instead
1411
     * @param string $string
1412
     *  The string to test.
1413
     * @return boolean
1414
     *  true if the string is prefixed with `regexp:` or `not-regexp:`, false otherwise.
1415
     */
1416
    protected static function isFilterRegex($string)
1417
    {
1418
        if (Symphony::Log()) {
1419
            Symphony::Log()->pushDeprecateWarningToLog(
1420
                get_called_class() . '::isFilterRegex()',
1421
                'EntryQueryFieldAdapter::isFilterRegex()'
1422
            );
1423
        }
1424
        if (preg_match('/^regexp:/i', $string) || preg_match('/^not-?regexp:/i', $string)) {
1425
            return true;
1426
        }
1427
    }
1428
1429
    /**
1430
     * Builds a basic REGEXP statement given a `$filter`. This function supports
1431
     * `regexp:` or `not-regexp:`. Users should keep in mind this function
1432
     * uses MySQL patterns, not the usual PHP patterns, the syntax between these
1433
     * flavours differs at times.
1434
     *
1435
     * @since Symphony 2.3
1436
     * @deprecated @since Symphony 3.0.0
1437
     *  Use EntryQueryFieldAdapter::createFilterRegexp() instead
1438
     * @link https://dev.mysql.com/doc/refman/en/regexp.html
1439
     * @param string $filter
1440
     *  The full filter, eg. `regexp: ^[a-d]`
1441
     * @param array $columns
1442
     *  The array of columns that need the given `$filter` applied to. The conditions
1443
     *  will be added using `OR` when using `regexp:` but they will be added using `AND`
1444
     *  when using `not-regexp:`
1445
     * @param string $joins
1446
     *  A string containing any table joins for the current SQL fragment. By default
1447
     *  Datasources will always join to the `tbl_entries` table, which has an alias of
1448
     *  `e`. This parameter is passed by reference.
1449
     * @param string $where
1450
     *  A string containing the WHERE conditions for the current SQL fragment. This
1451
     *  is passed by reference and is expected to be used to add additional conditions
1452
     *  specific to this field
1453
     */
1454
    public function buildRegexSQL($filter, array $columns, &$joins, &$where)
1455
    {
1456
        if (Symphony::Log()) {
1457
            Symphony::Log()->pushDeprecateWarningToLog(
1458
                get_called_class() . '::buildRegexSQL()',
1459
                'EntryQueryFieldAdapter::createFilterRegexp()'
1460
            );
1461
        }
1462
        $this->_key++;
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1462
        /** @scrutinizer ignore-deprecated */ $this->_key++;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1463
        $field_id = $this->get('id');
1464
        $filter = $this->cleanValue($filter);
1465
        $op = '';
1466
1467
        if (preg_match('/^regexp:\s*/i', $filter)) {
1468
            $pattern = preg_replace('/^regexp:\s*/i', null, $filter);
1469
            $regex = 'REGEXP';
1470
            $op = 'OR';
1471
        } elseif (preg_match('/^not-?regexp:\s*/i', $filter)) {
1472
            $pattern = preg_replace('/^not-?regexp:\s*/i', null, $filter);
1473
            $regex = 'NOT REGEXP';
1474
            $op = 'AND';
1475
        } else {
1476
            throw new Exception("Filter `$filter` is not a Regexp filter");
1477
        }
1478
1479
        if (strlen($pattern) == 0) {
1480
            return;
1481
        }
1482
1483
        $joins .= "
1484
            LEFT JOIN
1485
                `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1485
                `tbl_entries_data_{$field_id}` AS t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1486
                ON (e.id = t{$field_id}_{$this->_key}.entry_id)
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1486
                ON (e.id = t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.entry_id)

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1487
        ";
1488
1489
        $where .= "AND ( ";
1490
1491
        foreach ($columns as $key => $col) {
1492
            $modifier = ($key === 0) ? '' : $op;
1493
1494
            $where .= "
1495
                {$modifier} t{$field_id}_{$this->_key}.{$col} {$regex} '{$pattern}'
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1495
                {$modifier} t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.{$col} {$regex} '{$pattern}'

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1496
            ";
1497
        }
1498
        $where .= ")";
1499
    }
1500
1501
    /**
1502
     * Test whether the input string is a NULL/NOT NULL SQL clause, by searching
1503
     * for the prefix of `sql:` in the given `$string`, followed by `(NOT )? NULL`
1504
     *
1505
     * @since Symphony 2.7.0
1506
     * @deprecated @since Symphony 3.0.0
1507
     *  Use EntryQueryFieldAdapter::isFilterSQL() instead
1508
     * @param string $string
1509
     *  The string to test.
1510
     * @return boolean
1511
     *  true if the string is prefixed with `sql:`, false otherwise.
1512
     */
1513
    protected static function isFilterSQL($string)
1514
    {
1515
        if (Symphony::Log()) {
1516
            Symphony::Log()->pushDeprecateWarningToLog(
1517
                get_called_class() . '::isFilterSQL()',
1518
                'EntryQueryFieldAdapter::isFilterSQL()'
1519
            );
1520
        }
1521
        if (preg_match('/^sql:\s*(NOT )?NULL$/i', $string)) {
1522
            return true;
1523
        }
1524
    }
1525
1526
    /**
1527
     * Builds a basic NULL/NOT NULL SQL statement given a `$filter`.
1528
     *  This function supports `sql: NULL` or `sql: NOT NULL`.
1529
     *
1530
     * @since Symphony 2.7.0
1531
     * @deprecated @since Symphony 3.0.0
1532
     *  Use EntryQueryFieldAdapter::createFilterSQL() instead
1533
     * @link https://dev.mysql.com/doc/refman/en/regexp.html
1534
     * @param string $filter
1535
     *  The full filter, eg. `sql: NULL`
1536
     * @param array $columns
1537
     *  The array of columns that need the given `$filter` applied to. The conditions
1538
     *  will be added using `OR`.
1539
     * @param string $joins
1540
     *  A string containing any table joins for the current SQL fragment. By default
1541
     *  Datasources will always join to the `tbl_entries` table, which has an alias of
1542
     *  `e`. This parameter is passed by reference.
1543
     * @param string $where
1544
     *  A string containing the WHERE conditions for the current SQL fragment. This
1545
     *  is passed by reference and is expected to be used to add additional conditions
1546
     *  specific to this field
1547
     */
1548
    public function buildFilterSQL($filter, array $columns, &$joins, &$where)
1549
    {
1550
        if (Symphony::Log()) {
1551
            Symphony::Log()->pushDeprecateWarningToLog(
1552
                get_called_class() . '::buildFilterSQL()',
1553
                'EntryQueryFieldAdapter::createFilterSQL()'
1554
            );
1555
        }
1556
        $this->_key++;
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1556
        /** @scrutinizer ignore-deprecated */ $this->_key++;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1557
        $field_id = $this->get('id');
1558
        $filter = $this->cleanValue($filter);
1559
        $pattern = '';
1560
1561
        if (preg_match('/^sql:\s*NOT NULL$/i', $filter)) {
1562
            $pattern = 'NOT NULL';
1563
        } elseif (preg_match('/^sql:\s*NULL$/i', $filter)) {
1564
            $pattern = 'NULL';
1565
        } else {
1566
            // No match, return
1567
            return;
1568
        }
1569
1570
        $joins .= "
1571
            LEFT JOIN
1572
                `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1572
                `tbl_entries_data_{$field_id}` AS t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1573
                ON (e.id = t{$field_id}_{$this->_key}.entry_id)
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1573
                ON (e.id = t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.entry_id)

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1574
        ";
1575
1576
        $where .= "AND ( ";
1577
1578
        foreach ($columns as $key => $col) {
1579
            $modifier = ($key === 0) ? '' : 'OR';
1580
1581
            $where .= "
1582
                {$modifier} t{$field_id}_{$this->_key}.{$col} IS {$pattern}
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1582
                {$modifier} t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.{$col} IS {$pattern}

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1583
            ";
1584
        }
1585
        $where .= ")";
1586
    }
1587
1588
    /**
1589
     * Construct the SQL statement fragments to use to retrieve the data of this
1590
     * field when utilized as a data source.
1591
     *
1592
     * @deprecated @since Symphony 3.0.0
1593
     *  Use EntryQueryFieldAdapter::filter() instead
1594
     * @param array $data
1595
     *  An array of the data that contains the values for the filter as specified
1596
     *  in the datasource editor. The value that is entered in the datasource editor
1597
     *  is made into an array by using + or , to separate the filter.
1598
     * @param string $joins
1599
     *  A string containing any table joins for the current SQL fragment. By default
1600
     *  Datasources will always join to the `tbl_entries` table, which has an alias of
1601
     *  `e`. This parameter is passed by reference.
1602
     * @param string $where
1603
     *  A string containing the WHERE conditions for the current SQL fragment. This
1604
     *  is passed by reference and is expected to be used to add additional conditions
1605
     *  specific to this field
1606
     * @param boolean $andOperation (optional)
1607
     *  This parameter defines whether the `$data` provided should be treated as
1608
     *  AND or OR conditions. This parameter will be set to true if $data used a
1609
     *  + to separate the values, otherwise it will be false. It is false by default.
1610
     * @return boolean
1611
     *  true if the construction of the SQL was successful, false otherwise.
1612
     */
1613
    public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false)
1614
    {
1615
        if (Symphony::Log()) {
1616
            Symphony::Log()->pushDeprecateWarningToLog(
1617
                get_called_class() . '::buildDSRetrievalSQL()',
1618
                'EntryQueryFieldAdapter::filter()'
1619
            );
1620
        }
1621
        $field_id = $this->get('id');
1622
1623
        // REGEX filtering is a special case, and will only work on the first item
1624
        // in the array. You cannot specify multiple filters when REGEX is involved.
1625
        if (self::isFilterRegex($data[0])) {
0 ignored issues
show
Deprecated Code introduced by
The function Field::isFilterRegex() has been deprecated: @since Symphony 3.0.0 Use EntryQueryFieldAdapter::isFilterRegex() instead ( Ignorable by Annotation )

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

1625
        if (/** @scrutinizer ignore-deprecated */ self::isFilterRegex($data[0])) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1626
            $this->buildRegexSQL($data[0], array('value'), $joins, $where);
0 ignored issues
show
Deprecated Code introduced by
The function Field::buildRegexSQL() has been deprecated: @since Symphony 3.0.0 Use EntryQueryFieldAdapter::createFilterRegexp() instead ( Ignorable by Annotation )

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

1626
            /** @scrutinizer ignore-deprecated */ $this->buildRegexSQL($data[0], array('value'), $joins, $where);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1627
1628
            // SQL filtering: allows for NULL/NOT NULL statements
1629
        } elseif (self::isFilterSQL($data[0])) {
0 ignored issues
show
Deprecated Code introduced by
The function Field::isFilterSQL() has been deprecated: @since Symphony 3.0.0 Use EntryQueryFieldAdapter::isFilterSQL() instead ( Ignorable by Annotation )

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

1629
        } elseif (/** @scrutinizer ignore-deprecated */ self::isFilterSQL($data[0])) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1630
            $this->buildFilterSQL($data[0], array('value'), $joins, $where);
0 ignored issues
show
Deprecated Code introduced by
The function Field::buildFilterSQL() has been deprecated: @since Symphony 3.0.0 Use EntryQueryFieldAdapter::createFilterSQL() instead ( Ignorable by Annotation )

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

1630
            /** @scrutinizer ignore-deprecated */ $this->buildFilterSQL($data[0], array('value'), $joins, $where);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1631
1632
            // AND operation, iterates over `$data` and uses a new JOIN for
1633
            // every item.
1634
        } elseif ($andOperation) {
1635
            foreach ($data as $value) {
1636
                $this->_key++;
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1636
                /** @scrutinizer ignore-deprecated */ $this->_key++;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1637
                $value = $this->cleanValue($value);
1638
                $joins .= "
1639
                    LEFT JOIN
1640
                        `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1640
                        `tbl_entries_data_{$field_id}` AS t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1641
                        ON (e.id = t{$field_id}_{$this->_key}.entry_id)
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1641
                        ON (e.id = t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.entry_id)

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1642
                ";
1643
                $where .= "
1644
                    AND t{$field_id}_{$this->_key}.value = '{$value}'
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1644
                    AND t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.value = '{$value}'

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1645
                ";
1646
            }
1647
1648
            // Default logic, this will use a single JOIN statement and collapse
1649
            // `$data` into a string to be used in conjunction with IN
1650
        } else {
1651
            foreach ($data as &$value) {
1652
                $value = $this->cleanValue($value);
1653
            }
1654
1655
            $this->_key++;
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1655
            /** @scrutinizer ignore-deprecated */ $this->_key++;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1656
            $data = implode("', '", $data);
1657
            $joins .= "
1658
                LEFT JOIN
1659
                    `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1659
                    `tbl_entries_data_{$field_id}` AS t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1660
                    ON (e.id = t{$field_id}_{$this->_key}.entry_id)
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1660
                    ON (e.id = t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.entry_id)

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1661
            ";
1662
            $where .= "
1663
                AND t{$field_id}_{$this->_key}.value IN ('{$data}')
0 ignored issues
show
Deprecated Code introduced by
The property Field::$_key has been deprecated: @since Symphony 3.0.0 ( Ignorable by Annotation )

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

1663
                AND t{$field_id}_{/** @scrutinizer ignore-deprecated */ $this->_key}.value IN ('{$data}')

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1664
            ";
1665
        }
1666
1667
        return true;
1668
    }
1669
1670
    /**
1671
     * Determine if the requested $order is random or not.
1672
     *
1673
     * @since Symphony 2.7.0
1674
     * @deprecated @since Symphony 3.0.0
1675
     *  Use EntryQueryFieldAdapter::isRandomOrder() instead
1676
     * @param string $order
1677
     *  the sorting direction.
1678
     * @return boolean
1679
     *  true if the $order is either 'rand' or 'random'
1680
     */
1681
    protected function isRandomOrder($order)
1682
    {
1683
        if (Symphony::Log()) {
1684
            Symphony::Log()->pushDeprecateWarningToLog(
1685
                get_called_class() . '::isRandomOrder()',
1686
                'EntryQueryFieldAdapter::isRandomOrder()'
1687
            );
1688
        }
1689
        return in_array(strtolower($order), array('random', 'rand'));
1690
    }
1691
1692
    /**
1693
     * Build the SQL command to append to the default query to enable
1694
     * sorting of this field. By default this will sort the results by
1695
     * the entry id in ascending order.
1696
     *
1697
     * Extension developers should always implement both `buildSortingSQL()`
1698
     * and `buildSortingSelectSQL()`.
1699
     *
1700
     * @deprecated @since Symphony 3.0.0
1701
     *  Use EntryQueryFieldAdapter::sort() instead
1702
     * @uses Field::isRandomOrder()
1703
     * @see Field::buildSortingSelectSQL()
1704
     * @param string $joins
1705
     *  the join element of the query to append the custom join sql to.
1706
     * @param string $where
1707
     *  the where condition of the query to append to the existing where clause.
1708
     * @param string $sort
1709
     *  the existing sort component of the sql query to append the custom
1710
     *  sort sql code to.
1711
     * @param string $order (optional)
1712
     *  an optional sorting direction. this defaults to ascending. if this
1713
     *  is declared either 'random' or 'rand' then a random sort is applied.
1714
     */
1715
    public function buildSortingSQL(&$joins, &$where, &$sort, $order = 'ASC')
1716
    {
1717
        if (Symphony::Log()) {
1718
            Symphony::Log()->pushDeprecateWarningToLog(
1719
                get_called_class() . '::buildSortingSQL()',
1720
                'EntryQueryFieldAdapter::sort()'
1721
            );
1722
        }
1723
        if ($this->isRandomOrder($order)) {
0 ignored issues
show
Deprecated Code introduced by
The function Field::isRandomOrder() has been deprecated: @since Symphony 3.0.0 Use EntryQueryFieldAdapter::isRandomOrder() instead ( Ignorable by Annotation )

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

1723
        if (/** @scrutinizer ignore-deprecated */ $this->isRandomOrder($order)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1724
            $sort = 'ORDER BY RAND()';
1725
        } else {
1726
            $joins .= "LEFT OUTER JOIN `tbl_entries_data_".$this->get('id')."` AS `ed` ON (`e`.`id` = `ed`.`entry_id`) ";
0 ignored issues
show
Bug introduced by
Are you sure $this->get('id') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1726
            $joins .= "LEFT OUTER JOIN `tbl_entries_data_"./** @scrutinizer ignore-type */ $this->get('id')."` AS `ed` ON (`e`.`id` = `ed`.`entry_id`) ";
Loading history...
1727
            $sort = sprintf('ORDER BY `ed`.`value` %s', $order);
1728
        }
1729
    }
1730
1731
    /**
1732
     * Build the needed SQL clause command to make `buildSortingSQL()` work on
1733
     * MySQL 5.7 in strict mode, which requires all columns in the ORDER BY
1734
     * clause to be included in the SELECT's projection.
1735
     *
1736
     * If no new projection is needed (like if the order is made via a sub-query),
1737
     * simply return null.
1738
     *
1739
     * For backward compatibility, this method checks if the sort expression
1740
     * contains `ed`.`value`. This check will be removed in Symphony 3.0.0.
1741
     *
1742
     * Extension developers should make their Fields implement
1743
     * `buildSortingSelectSQL()` when overriding `buildSortingSQL()`.
1744
     *
1745
     * @deprecated @since Symphony 3.0.0
1746
     *  Use EntryQueryFieldAdapter::sort() instead
1747
     * @since Symphony 2.7.0
1748
     * @uses Field::isRandomOrder()
1749
     * @see Field::buildSortingSQL()
1750
     * @param string $sort
1751
     *  the existing sort component of the sql query, after it has been passed
1752
     *  to `buildSortingSQL()`
1753
     * @param string $order (optional)
1754
     *  an optional sorting direction. this defaults to ascending. Should be the
1755
     *  same value that was passed to `buildSortingSQL()`
1756
     * @return string
1757
     *  an optional select clause to append to the generated SQL query.
1758
     *  This is needed when sorting on a column that is not part of the projection.
1759
     */
1760
    public function buildSortingSelectSQL($sort, $order = 'ASC')
1761
    {
1762
        if (Symphony::Log()) {
1763
            Symphony::Log()->pushDeprecateWarningToLog(
1764
                get_called_class() . '::buildSortingSelectSQL()',
1765
                'EntryQueryFieldAdapter::sort()'
1766
            );
1767
        }
1768
        if ($this->isRandomOrder($order)) {
0 ignored issues
show
Deprecated Code introduced by
The function Field::isRandomOrder() has been deprecated: @since Symphony 3.0.0 Use EntryQueryFieldAdapter::isRandomOrder() instead ( Ignorable by Annotation )

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

1768
        if (/** @scrutinizer ignore-deprecated */ $this->isRandomOrder($order)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1769
            return null;
1770
        }
1771
        // @deprecated This check should be removed in Symphony 3.0.0
1772
        if (strpos($sort, '`ed`.`value`') === false) {
1773
            return null;
1774
        }
1775
        return '`ed`.`value`';
1776
    }
1777
1778
    /**
1779
     * Default implementation of record grouping. This default implementation
1780
     * will throw an `Exception`. Thus, clients must overload this method
1781
     * for grouping to be successful.
1782
     *
1783
     * @throws Exception
1784
     * @param array $records
1785
     *  the records to group.
1786
     */
1787
    public function groupRecords($records)
0 ignored issues
show
Unused Code introduced by
The parameter $records is not used and could be removed. ( Ignorable by Annotation )

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

1787
    public function groupRecords(/** @scrutinizer ignore-unused */ $records)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1788
    {
1789
        throw new Exception(
1790
            __('Data source output grouping is not supported by the %s field', array('<code>' . $this->get('label') . '</code>'))
0 ignored issues
show
Bug introduced by
Are you sure $this->get('label') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1790
            __('Data source output grouping is not supported by the %s field', array('<code>' . /** @scrutinizer ignore-type */ $this->get('label') . '</code>'))
Loading history...
1791
        );
1792
    }
1793
1794
    /**
1795
     * Function to format this field if it chosen in a data source to be
1796
     * output as a parameter in the XML.
1797
     *
1798
     * Since Symphony 2.5.0, it will defaults to `prepareReadableValue` return value.
1799
     *
1800
     * @param array $data
1801
     *  The data for this field from it's `tbl_entry_data_{id}` table
1802
     * @param integer $entry_id
1803
     *  The optional id of this field entry instance
1804
     * @return string|array
1805
     *  The formatted value to be used as the parameter. Note that this can be
1806
     *  an array or a string. When returning multiple values use array, otherwise
1807
     *  use string.
1808
     */
1809
    public function getParameterPoolValue(array $data, $entry_id = null)
1810
    {
1811
        return $this->prepareReadableValue($data, $entry_id);
1812
    }
1813
1814
    /**
1815
     * Append the formatted XML output of this field as utilized as a data source.
1816
     *
1817
     * Since Symphony 2.5.0, it will defaults to `prepareReadableValue` return value.
1818
     *
1819
     * @param XMLElement $wrapper
1820
     *  the XML element to append the XML representation of this to.
1821
     * @param array $data
1822
     *  the current set of values for this field. the values are structured as
1823
     *  for displayPublishPanel.
1824
     * @param boolean $encode (optional)
1825
     *  flag as to whether this should be html encoded prior to output. this
1826
     *  defaults to false.
1827
     * @param string $mode
1828
     *   A field can provide ways to output this field's data. For instance a mode
1829
     *  could be 'items' or 'full' and then the function would display the data
1830
     *  in a different way depending on what was selected in the datasource
1831
     *  included elements.
1832
     * @param integer $entry_id (optional)
1833
     *  the identifier of this field entry instance. defaults to null.
1834
     */
1835
    public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $mode is not used and could be removed. ( Ignorable by Annotation )

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

1835
    public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, /** @scrutinizer ignore-unused */ $mode = null, $entry_id = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1836
    {
1837
        $wrapper->appendChild(new XMLElement($this->get('element_name'), ($encode ?
0 ignored issues
show
Bug introduced by
It seems like $this->get('element_name') can also be of type array; however, parameter $name of XMLElement::__construct() does only seem to accept string, 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

1837
        $wrapper->appendChild(new XMLElement(/** @scrutinizer ignore-type */ $this->get('element_name'), ($encode ?
Loading history...
1838
                              General::sanitize($this->prepareReadableValue($data, $entry_id)) :
1839
                              $this->prepareReadableValue($data, $entry_id))));
1840
    }
1841
1842
    /**
1843
     * The default method for constructing the example form markup containing this
1844
     * field when utilized as part of an event. This displays in the event documentation
1845
     * and serves as a basic guide for how markup should be constructed on the
1846
     * `Frontend` to save this field
1847
     *
1848
     * @throws InvalidArgumentException
1849
     * @return XMLElement
1850
     *  a label widget containing the formatted field element name of this.
1851
     */
1852
    public function getExampleFormMarkup()
1853
    {
1854
        $label = Widget::Label($this->get('label'));
0 ignored issues
show
Bug introduced by
It seems like $this->get('label') can also be of type array; however, parameter $name of Widget::Label() does only seem to accept string, 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

1854
        $label = Widget::Label(/** @scrutinizer ignore-type */ $this->get('label'));
Loading history...
1855
        $label->appendChild(Widget::Input('fields['.$this->get('element_name').']'));
0 ignored issues
show
Bug introduced by
Are you sure $this->get('element_name') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1855
        $label->appendChild(Widget::Input('fields['./** @scrutinizer ignore-type */ $this->get('element_name').']'));
Loading history...
1856
1857
        return $label;
1858
    }
1859
1860
    /**
1861
     * Commit the settings of this field from the section editor to
1862
     * create an instance of this field in a section.
1863
     *
1864
     * @return boolean
1865
     *  true if the commit was successful, false otherwise.
1866
     */
1867
    public function commit()
1868
    {
1869
        $fields = array();
1870
1871
        $fields['label'] = General::sanitize($this->get('label'));
0 ignored issues
show
Bug introduced by
It seems like $this->get('label') can also be of type array; however, parameter $source of General::sanitize() does only seem to accept string, 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

1871
        $fields['label'] = General::sanitize(/** @scrutinizer ignore-type */ $this->get('label'));
Loading history...
1872
        $fields['element_name'] = ($this->get('element_name') ? $this->get('element_name') : Lang::createHandle($this->get('label')));
0 ignored issues
show
Bug introduced by
It seems like $this->get('label') can also be of type array; however, parameter $string of Lang::createHandle() does only seem to accept string, 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

1872
        $fields['element_name'] = ($this->get('element_name') ? $this->get('element_name') : Lang::createHandle(/** @scrutinizer ignore-type */ $this->get('label')));
Loading history...
1873
        $fields['parent_section'] = $this->get('parent_section');
1874
        $fields['location'] = $this->get('location');
1875
        $fields['required'] = $this->get('required');
1876
        $fields['type'] = $this->_handle;
1877
        $fields['show_column'] = $this->get('show_column');
1878
        $fields['sortorder'] = (string)$this->get('sortorder');
1879
1880
        if ($id = $this->get('id')) {
1881
            return FieldManager::edit($id, $fields);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type array; however, parameter $id of FieldManager::edit() does only seem to accept integer, 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

1881
            return FieldManager::edit(/** @scrutinizer ignore-type */ $id, $fields);
Loading history...
1882
        } elseif ($id = FieldManager::add($fields)) {
1883
            $this->set('id', $id);
1884
            if ($this->requiresTable()) {
1885
                return $this->createTable();
1886
            }
1887
            return true;
1888
        }
1889
1890
        return false;
1891
    }
1892
1893
    /**
1894
     * The default field table construction method. This constructs the bare
1895
     * minimum set of columns for a valid field table. Subclasses are expected
1896
     * to overload this method to create a table structure that contains
1897
     * additional columns to store the specific data created by the field.
1898
     *
1899
     * @throws DatabaseException
1900
     * @see Field::requiresTable()
1901
     * @return boolean
1902
     */
1903
    public function createTable()
1904
    {
1905
        return Symphony::Database()
1906
            ->create('tbl_entries_data_' . General::intval($this->get('id')))
1907
            ->ifNotExists()
1908
            ->fields([
1909
                'id' => [
1910
                    'type' => 'int(11)',
1911
                    'auto' => true,
1912
                ],
1913
                'entry_id' => 'int(11)',
1914
                'value' => [
1915
                    'type' => 'varchar(255)',
1916
                    'null' => true,
1917
                ],
1918
            ])
1919
            ->keys([
1920
                'id' => 'primary',
1921
                'entry_id' => 'key',
1922
                'value' => 'key',
1923
            ])
1924
            ->execute()
1925
            ->success();
1926
    }
1927
1928
    /**
1929
     * Tells Symphony that this field needs a table in order to store
1930
     * data for each of its entries. Used when adding/deleting this field in a section
1931
     * or entries are edited/added, data as a performance optimization.
1932
     * It defaults to true, which force table creation.
1933
     *
1934
     * Developers are encouraged to update their null create table implementation
1935
     * with this method.
1936
     *
1937
     * @since Symphony 2.7.0
1938
     * @see Field::createTable()
1939
     * @return boolean
1940
     *  true if Symphony should call `createTable()`
1941
     */
1942
    public function requiresTable()
1943
    {
1944
        return true;
1945
    }
1946
1947
    /**
1948
     * Checks that we are working with a valid field handle and
1949
     * that the setting table exists.
1950
     *
1951
     * @since Symphony 2.7.0
1952
     * @return boolean
1953
     *   true if the table exists, false otherwise
1954
     */
1955
    public function tableExists()
1956
    {
1957
        if (!$this->_handle) {
1958
            return false;
1959
        }
1960
        return Symphony::Database()->tableExists('tbl_fields_' . $this->_handle);
1961
    }
1962
1963
    /**
1964
     * Checks that we are working with a valid field handle and field id, and
1965
     * checks that the field record exists in the settings table.
1966
     *
1967
     * @since Symphony 2.7.1 It does check if the settings table only contains
1968
     *   default columns and assume those fields do not require a record in the settings table.
1969
     *   When this situation is detected the field is considered as valid even if no records were
1970
     *   found in the settings table.
1971
     *
1972
     * @since Symphony 2.7.0
1973
     * @see Field::tableExists()
1974
     * @return boolean
1975
     *   true if the field id exists in the table, false otherwise
1976
     */
1977
    public function exists()
1978
    {
1979
        if (!$this->get('id') || !$this->_handle) {
1980
            return false;
1981
        }
1982
        $row = Symphony::Database()
1983
            ->select(['id'])
1984
            ->from('tbl_fields_' . $this->_handle)
1985
            ->where(['field_id' => $this->get('id')])
1986
            ->execute()
1987
            ->rows();
1988
1989
        if (empty($row)) {
1990
            // Some fields do not create any records in their settings table because they do not
1991
            // implement a proper `Field::commit()` method.
1992
            // The base implementation of the commit function only deals with the "core"
1993
            // `tbl_fields` table.
1994
            // The problem with this approach is that it can lead to data corruption when
1995
            // saving a field that got deleted by another user.
1996
            // The only way a field can live without a commit method is if it does not store any
1997
            // settings at all.
1998
            // But current version of Symphony assume that the `tbl_fields_$handle` table exists
1999
            // with at least a `id` and `field_id` column, so field are required to at least create
2000
            // the table to make their field work without SQL errors from the core.
2001
            $columns = Symphony::Database()
2002
                ->describe('tbl_fields_' . $this->_handle)
2003
                ->execute()
2004
                ->column('Field');
2005
2006
            // The table only has the two required columns, tolerate the missing record
2007
            $isDefault = count($columns) === 2 &&
2008
                in_array('id', $columns) &&
2009
                in_array('field_id', $columns);
2010
            if ($isDefault) {
2011
                Symphony::Log()->pushDeprecateWarningToLog($this->_handle, get_class($this), array(
2012
                    'message-format' => __('The field `%1$s` does not create settings records in the `tbl_fields_%1$s`.'),
2013
                    'alternative-format' => __('Please implement the commit function in class `%s`.'),
2014
                    'removal-format' => __('The compatibility check will will be removed in Symphony %s.'),
2015
                    'removal-version' => '4.0.0',
2016
                ));
2017
            }
2018
            return $isDefault;
2019
        }
2020
        return true;
2021
    }
2022
2023
    /**
2024
     * Remove the entry data of this field from the database.
2025
     *
2026
     * @param integer|array $entry_id
2027
     *    the ID of the entry, or an array of entry ID's to delete.
2028
     * @param array $data (optional)
2029
     *    The entry data provided for fields to do additional cleanup
2030
     *  This is an optional argument and defaults to null.
2031
     * @throws DatabaseException
2032
     * @return boolean
2033
     *    Returns true after the cleanup has been completed
2034
     */
2035
    public function entryDataCleanup($entry_id, $data = null)
2036
    {
2037
        if (!is_array($entry_id)) {
2038
            $entry_id = [$entry_id];
2039
        }
2040
        return Symphony::Database()
0 ignored issues
show
Deprecated Code introduced by
The function Database::delete() has been deprecated: Symphony 3.0.0 This parameter is deprecated and will be removed. Use DatabaseDelete::where() ( Ignorable by Annotation )

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

2040
        return /** @scrutinizer ignore-deprecated */ Symphony::Database()

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2041
            ->delete('tbl_entries_data_' . $this->get('id'))
0 ignored issues
show
Bug introduced by
Are you sure $this->get('id') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

2041
            ->delete('tbl_entries_data_' . /** @scrutinizer ignore-type */ $this->get('id'))
Loading history...
2042
            ->where(['entry_id' => ['in' => $entry_id]])
2043
            ->execute()
2044
            ->success();
2045
    }
2046
2047
    /**
2048
     * Accessor to the associated entry search value for this field
2049
     * instance. This default implementation simply returns `$data`
2050
     *
2051
     * @param array $data
2052
     *  the data from which to construct the associated search entry value, this is usually
2053
     *  Entry with the `$parent_entry_id` value's data.
2054
     * @param integer $field_id (optional)
2055
     *  the ID of the field that is the parent in the relationship
2056
     * @param integer $parent_entry_id (optional)
2057
     *  the ID of the entry from the parent section in the relationship
2058
     * @return array|string
2059
     *  Defaults to returning `$data`, but overriding implementation should return
2060
     *  a string
2061
     */
2062
    public function fetchAssociatedEntrySearchValue($data, $field_id = null, $parent_entry_id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $parent_entry_id is not used and could be removed. ( Ignorable by Annotation )

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

2062
    public function fetchAssociatedEntrySearchValue($data, $field_id = null, /** @scrutinizer ignore-unused */ $parent_entry_id = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $field_id is not used and could be removed. ( Ignorable by Annotation )

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

2062
    public function fetchAssociatedEntrySearchValue($data, /** @scrutinizer ignore-unused */ $field_id = null, $parent_entry_id = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2063
    {
2064
        return $data;
2065
    }
2066
2067
    /**
2068
     * Fetch the count of the associated entries given a `$value`.
2069
     *
2070
     * @see toolkit.Field#fetchAssociatedEntrySearchValue()
2071
     * @param mixed $value
2072
     *  the value to find the associated entry count for, this usually comes from
2073
     *  the `fetchAssociatedEntrySearchValue` function.
2074
     * @return void|integer
2075
     *  this default implementation returns void. overriding implementations should
2076
     *  return an integer.
2077
     */
2078
    public function fetchAssociatedEntryCount($value)
2079
    {
2080
    }
2081
2082
    /**
2083
     * Find related entries from a linking field's data table. Default implementation uses
2084
     * column names `entry_id` and `relation_id` as with the Select Box Link
2085
     *
2086
     * @since Symphony 2.5.0
2087
     *
2088
     * @param  integer $entry_id
2089
     * @param  integer $parent_field_id
2090
     * @return array
2091
     */
2092
    public function findRelatedEntries($entry_id, $parent_field_id)
0 ignored issues
show
Unused Code introduced by
The parameter $parent_field_id is not used and could be removed. ( Ignorable by Annotation )

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

2092
    public function findRelatedEntries($entry_id, /** @scrutinizer ignore-unused */ $parent_field_id)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2093
    {
2094
        try {
2095
            $ids = Symphony::Database()
2096
                ->select(['entry_id'])
2097
                ->from('tbl_entries_data_' . $this->get('id'))
0 ignored issues
show
Bug introduced by
Are you sure $this->get('id') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

2097
                ->from('tbl_entries_data_' . /** @scrutinizer ignore-type */ $this->get('id'))
Loading history...
2098
                ->where(['relation_id' => General::intval($entry_id)])
2099
                ->where(['entry_id' => ['!=' => null]])
2100
                ->execute()
2101
                ->column('entry_id');
2102
        } catch (Exception $e) {
2103
            return [];
2104
        }
2105
2106
        return $ids;
2107
    }
2108
2109
    /**
2110
     * Find related entries for the current field. Default implementation uses
2111
     * column names `entry_id` and `relation_id` as with the Select Box Link
2112
     *
2113
     * @since Symphony 2.5.0
2114
     *
2115
     * @param  integer $parent_field_id
2116
     * @param  integer $entry_id
2117
     * @return array
2118
     */
2119
    public function findParentRelatedEntries($parent_field_id, $entry_id)
0 ignored issues
show
Unused Code introduced by
The parameter $parent_field_id is not used and could be removed. ( Ignorable by Annotation )

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

2119
    public function findParentRelatedEntries(/** @scrutinizer ignore-unused */ $parent_field_id, $entry_id)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2120
    {
2121
        try {
2122
            $ids = Symphony::Database()
2123
                ->select(['relation_id'])
2124
                ->from('tbl_entries_data_' . $this->get('id'))
0 ignored issues
show
Bug introduced by
Are you sure $this->get('id') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

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

2124
                ->from('tbl_entries_data_' . /** @scrutinizer ignore-type */ $this->get('id'))
Loading history...
2125
                ->where(['entry_id' => General::intval($entry_id)])
2126
                ->where(['relation_id' => ['!=' => null]])
2127
                ->execute()
2128
                ->column('relation_id');
2129
        } catch (Exception $e) {
2130
            return [];
2131
        }
2132
2133
        return $ids;
2134
    }
2135
}
2136