Completed
Pull Request — dev (#67)
by
unknown
01:21
created

FieldReverse_Relationship::displayPublishPanel()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 8.9368
c 0
b 0
f 0
cc 5
nc 9
nop 6
1
<?php
2
/**
3
 * Copyright: Deux Huit Huit 2017
4
 * LICENCE: MIT https://deuxhuithuit.mit-license.org
5
 */
6
7
if (!defined('__IN_SYMPHONY__')) die('<h2>Symphony Error</h2><p>You cannot directly access this file</p>');
8
9
require_once(EXTENSIONS . '/entry_relationship_field/lib/class.field.relationship.php');
10
require_once(EXTENSIONS . '/entry_relationship_field/lib/class.erfxsltutilities.php');
11
12
/**
13
 *
14
 * Field class that will represent a reverse relationship between entries
15
 * @author Deux Huit Huit
16
 *
17
 */
18
class FieldReverse_Relationship extends FieldRelationship
19
{
20
    /**
21
     *
22
     * Name of the field table
23
     *  @var string
24
     */
25
    const FIELD_TBL_NAME = 'tbl_fields_reverse_relationship';
26
27
    /**
28
     *
29
     * Constructor for the Reverse_Relationship Field object
30
     */
31
    public function __construct()
32
    {
33
        // call the parent constructor
34
        parent::__construct();
35
        // set the name of the field
36
        $this->_name = __('Reverse Relationship');
37
        // allowed to make it required
38
        $this->_required = true;
39
        // allowed to show in the table columns
40
        $this->_showcolumn = true;
41
        // forbid association
42
        $this->_showassociation = false;
43
        // set as not required by default
44
        $this->set('required', 'no');
45
        // show header by default
46
        $this->set('show_header', 'yes');
47
        // allow link by default
48
        $this->set('allow_unlink', 'yes');
49
        // allow go to by default
50
        $this->set('allow_goto', 'yes');
51
        // no modes
52
        $this->set('mode', null);
53
        $this->set('mode_table', null);
54
        $this->set('mode_header', null);
55
        $this->set('mode_footer', null);
56
        // no links
57
        $this->set('linked_section_id', null);
58
        $this->set('linked_field_id', null);
59
        // cache managers
60
        $this->sectionManager = new SectionManager;
61
        $this->entryManager = new EntryManager;
62
        $this->fieldManager = new FieldManager;
63
    }
64
65
    public function isSortable()
66
    {
67
        return true;
68
    }
69
70
    public function canFilter()
71
    {
72
        return false;
73
    }
74
75
    public function canPublishFilter()
76
    {
77
        return false;
78
    }
79
80
    public function canImport()
81
    {
82
        return false;
83
    }
84
85
    public function canPrePopulate()
86
    {
87
        return false;
88
    }
89
90
    public function mustBeUnique()
91
    {
92
        return false;
93
    }
94
95
    public function allowDatasourceOutputGrouping()
96
    {
97
        return false;
98
    }
99
100
    public function requiresSQLGrouping()
101
    {
102
        return false;
103
    }
104
105
    public function allowDatasourceParamOutput()
106
    {
107
        return false;
108
    }
109
110
    public function requiresTable()
111
    {
112
        return false;
113
    }
114
115
    public function createTable()
116
    {
117
        return false;
118
    }
119
120
    public function fetchIncludableElements()
121
    {
122
    }
123
124
    /* ********** INPUT AND FIELD *********** */
125
126
    /**
127
     *
128
     * Validates input
129
     * Called before <code>processRawFieldData</code>
130
     * @param $data
131
     * @param $message
132
     * @param $entry_id
133
     */
134
    public function checkPostFieldData($data, &$message, $entry_id=null)
135
    {
136
        return self::__OK__;
137
    }
138
139
140
    /**
141
     *
142
     * Process data before saving into database.
143
     *
144
     * @param array $data
145
     * @param int $status
146
     * @param boolean $simulate
147
     * @param int $entry_id
148
     *
149
     * @return Array - data to be inserted into DB
150
     */
151
    public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
152
    {
153
        $status = self::__OK__;
154
        return null;
155
    }
156
157
158
    /**
159
     *
160
     * Validates the field settings before saving it into the field's table
161
     */
162
    public function checkFields(Array &$errors, $checkForDuplicates = true)
163
    {
164
        $parent = parent::checkFields($errors, $checkForDuplicates);
165
        if ($parent != self::__OK__) {
166
            return $parent;
167
        }
168
169
        $sections = $this->get('linked_section_id');
170
        if (empty($sections)) {
171
            $errors['sections'] = __('A section must be chosen');
172
        }
173
        $field = $this->get('linked_field_id');
174
        if (empty($field)) {
175
            $errors['field'] = __('A field must be chosen');
176
        }
177
178
        return (!empty($errors) ? self::__ERROR__ : self::__OK__);
179
    }
180
181
    /**
182
     *
183
     * Save field settings into the field's table
184
     */
185
    public function commit()
186
    {
187
        // if the default implementation works...
188
        if(!parent::commit()) return false;
189
190
        $id = $this->get('id');
191
192
        // exit if there is no id
193
        if ($id == false) return false;
194
195
        // declare an array contains the field's settings
196
        $settings = array(
197
            'linked_section_id' => $this->get('linked_section_id'),
198
            'linked_field_id' => $this->get('linked_field_id'),
199
            'mode' => $this->get('mode'),
200
            'mode_table' => $this->get('mode_table'),
201
            'mode_header' => $this->get('mode_header'),
202
            'mode_footer' => $this->get('mode_footer'),
203
        );
204
205
        return FieldManager::saveSettings($id, $settings);
206
    }
207
208
    /**
209
     * Appends data into the XML tree of a Data Source
210
     * @param $wrapper
211
     * @param $data
212
     */
213
    public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
214
    {
215
        // nothing to do
216
    }
217
218
    /* ********* UI *********** */
219
220
    /**
221
     *
222
     * Builds the UI for the field's settings when creating/editing a section
223
     * @param XMLElement $wrapper
224
     * @param array $errors
225
     */
226
    public function displaySettingsPanel(XMLElement &$wrapper, $errors = null)
227
    {
228
        /* first line, label and such */
229
        parent::displaySettingsPanel($wrapper, $errors);
230
231
        // fieldset
232
        $fieldset = new XMLElement('fieldset', null);
233
234
        // group
235
        $group = new XMLElement('div', null, array('class' => 'two columns'));
236
        $fieldset->appendChild($group);
237
238
        // sections
239
        $sections = new XMLElement('div', null, array('class' => 'column'));
240
        $this->appendSelectionSelect($sections);
241 View Code Duplication
        if (is_array($errors) && isset($errors['sections'])) {
242
            $sections = Widget::Error($sections, $errors['sections']);
243
        }
244
        $group->appendChild($sections);
245
246
        // field
247
        $field = new XMLElement('div', null, array('class' => 'column'));
248
        $this->appendFieldSelect($field);
249 View Code Duplication
        if (is_array($errors) && isset($errors['field'])) {
250
            $field = Widget::Error($field, $errors['field']);
251
        }
252
        $group->appendChild($field);
253
254
        $wrapper->appendChild($fieldset);
255
256
        // xsl
257
        $xsl = new XMLElement('fieldset');
258
        $xsl->appendChild(new XMLElement('legend', __('Backend XSL templates options')));
259
        $xsl_cols = new XMLElement('div');
260
        $xsl_cols->setAttribute('class', 'four columns');
261
262
        // xsl mode
263
        $xslmode = Widget::Label();
264
        $xslmode->setValue(__('XSL mode for entries content template'));
265
        $xslmode->setAttribute('class', 'column');
266
        $xslmode->appendChild(Widget::Input($this->createSettingsFieldName('mode'), $this->get('mode'), 'text'));
267
        $xsl_cols->appendChild($xslmode);
268
269
        // xsl header mode
270
        $xslmodetable = Widget::Label();
271
        $xslmodetable->setValue(__('XSL mode for entries header template'));
272
        $xslmodetable->setAttribute('class', 'column');
273
        $xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_header'), $this->get('mode_header'), 'text'));
274
        $xsl_cols->appendChild($xslmodetable);
275
276
        // xsl table mode
277
        $xslmodetable = Widget::Label();
278
        $xslmodetable->setValue(__('XSL mode for publish table value'));
279
        $xslmodetable->setAttribute('class', 'column');
280
        $xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_table'), $this->get('mode_table'), 'text'));
281
        $xsl_cols->appendChild($xslmodetable);
282
283
        // xsl action bar mode
284
        $xslmodetable = Widget::Label();
285
        $xslmodetable->setValue(__('XSL mode for publish action bar'));
286
        $xslmodetable->setAttribute('class', 'column');
287
        $xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_footer'), $this->get('mode_footer'), 'text'));
288
        $xsl_cols->appendChild($xslmodetable);
289
290
        $xsl->appendChild($xsl_cols);
291
        $wrapper->appendChild($xsl);
292
293
        // Footer
294
        $this->appendStatusFooter($wrapper);
295
    }
296
297
    /**
298
     *
299
     * Builds the UI for the publish page
300
     * @param XMLElement $wrapper
301
     * @param mixed $data
302
     * @param mixed $flagWithError
303
     * @param string $fieldnamePrefix
304
     * @param string $fieldnamePostfix
305
     */
306
    public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
307
    {
308
        if (!$entry_id) {
309
            return;
310
        }
311
        $field = $this->fieldManager
312
            ->select()
313
            ->field($this->get('linked_field_id'))
314
            ->execute()
315
            ->next();
316
        $section = $this->sectionManager
317
            ->select()
318
            ->section($this->get('linked_section_id'))
319
            ->execute()
320
            ->next();
321
        if (!($field instanceof FieldRelationship)) {
322
            $flagWithError = __('Linked field is not valid. Please edit this field to set it to a valid ER field.');
323
        }
324
325
        $label = Widget::Label($this->get('label'));
326
        // label error management
327
        if ($flagWithError != null) {
328
            $wrapper->appendChild(Widget::Error($label, $flagWithError));
329
        } else {
330
            $wrapper->appendChild($label);
331
            $wrapper->appendChild($this->createEntriesList(array()));
332
            $wrapper->appendChild($this->createActionBarMenu($field));
333
        }
334
335
        $wrapper->setAttribute('data-field-id', $this->get('id'));
336
        $wrapper->setAttribute('data-linked-field-id', $this->get('linked_field_id'));
337
        $wrapper->setAttribute('data-linked-section-id', $this->get('linked_section_id'));
338
        $wrapper->setAttribute('data-linked-section', $section->get('handle'));
339
        $wrapper->setAttribute('data-field-label', $field->get('label'));
340
        $wrapper->setAttribute(
341
            'data-entries',
342
            implode(self::SEPARATOR, $field->findRelatedEntries($entry_id, $field->get('id')))
343
        );
344
        if (isset($_REQUEST['debug'])) {
345
            $wrapper->setAttribute('data-debug', true);
346
        }
347
    }
348
349
    private function createActionBarMenu($field)
350
    {
351
        $section = $this->sectionManager
352
            ->select()
353
            ->section($this->get('linked_section_id'))
354
            ->execute()
355
            ->next();
356
        $wrap = new XMLElement('div');
357
358
        $fieldset = new XMLElement('fieldset');
359
        $fieldset->setAttribute('class', 'single');
360
        $div = new XMLElement('div');
361
362
        $div->appendChild(new XMLElement(
363
            'span',
364
            __('Related section: '),
365
            array('class' => 'reverse-selection')
366
        ));
367
        $div->appendChild(new XMLElement(
368
            'label',
369
            General::sanitize($section->get('name') . ': ' . $field->get('label')),
370
            array('class' => 'reverse-selection')
371
        ));
372
        $div->appendChild(new XMLElement('button', __('Add to entry'), array(
373
            'type' => 'button',
374
            'class' => 'add',
375
            'data-add' => $section->get('handle'),
376
        )));
377
378
        $fieldset->appendChild($div);
379
        $wrap->appendChild($fieldset);
380
381
        return $wrap;
382
    }
383
384
    private static $erFields = array();
385
    private function getERFields()
386
    {
387
        if (empty(self::$erFields)) {
388
            self::$erFields = $this->fieldManager
389
                ->select()
390
                ->sort('id', 'asc')
391
                ->type('entry_relationship')
392
                ->execute()
393
                ->rows();
394
        }
395
        return self::$erFields;
396
    }
397
398
    private static $erSections = array();
399
    private function getERSections()
400
    {
401
        if (empty(self::$erSections) && !empty(self::$erFields)) {
402
            $erFields = self::getERFields();
403
            $sectionIds = array_map(function ($erField) {
404
                return $erField->get('parent_section');
405
            }, $erFields);
406
            self::$erSections = $this->sectionManager
407
                ->select()
408
                ->sections($sectionIds)
409
                ->execute()
410
                ->rows();
411
        }
412
        return self::$erSections;
413
    }
414
415
    private function buildSectionSelect($name)
416
    {
417
        $sections = static::getERSections();
0 ignored issues
show
Bug introduced by
Since getERSections() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getERSections() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
418
        $options = array();
419
420 View Code Duplication
        foreach ($sections as $section) {
421
            $driver = $section->get('id');
422
            $selected = $driver === $this->get('linked_section_id');
423
            $options[] = array($driver, $selected, General::sanitize($section->get('name')));
424
        }
425
426
        return Widget::Select($name, $options);
427
    }
428
429 View Code Duplication
    private function appendSelectionSelect(&$wrapper)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
430
    {
431
        $name = $this->createSettingsFieldName('linked_section_id', false);
432
433
        $input = $this->buildSectionSelect($name);
434
        $input->setAttribute('class', 'reverse_relationship-sections');
435
436
        $label = Widget::Label();
437
438
        $label->setValue(__('Available sections %s', array($input->generate())));
439
440
        $wrapper->appendChild($label);
441
    }
442
443
    private function buildFieldSelect($name)
444
    {
445
        if ($this->get('linked_section_id')) {
446
            $section = $this->sectionManager
447
                ->select()
448
                ->section($this->get('linked_section_id'))
449
                ->execute()
450
                ->next();
451
        } else {
452
            $section = null;
453
        }
454
        $fields = static::getERFields();
0 ignored issues
show
Bug introduced by
Since getERFields() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getERFields() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
455
        $options = array();
456
457
        foreach ($fields as $field) {
458
            if ($section && $section->get('id') !== $field->get('parent_section')) {
459
                continue;
460
            }
461
            $driver = $field->get('id');
462
            $selected = $driver === $this->get('linked_field_id');
463
            $options[] = array($driver, $selected, General::sanitize($field->get('label')));
464
        }
465
466
        return Widget::Select($name, $options);
467
    }
468
469 View Code Duplication
    protected function appendFieldSelect(&$wrapper)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
470
    {
471
        $name = $this->createSettingsFieldName('linked_field_id', false);
472
473
        $input = $this->buildFieldSelect($name);
474
        $input->setAttribute('class', 'reverse_relationship-field');
475
476
        $label = Widget::Label();
477
478
        $label->setValue(__('Available Fields %s', array($input->generate())));
479
480
        $wrapper->appendChild($label);
481
    }
482
483
    /**
484
     *
485
     * Return a plain text representation of the field's data
486
     * @param array $data
487
     * @param int $entry_id
488
     */
489
    public function prepareTextValue($data, $entry_id = null)
490
    {
491
        $field = $this->fieldManager
492
            ->select()
493
            ->field($this->get('linked_field_id'))
494
            ->execute()
495
            ->next();
496
        $section = $this->sectionManager
497
            ->select()
498
            ->section($this->get('linked_section_id'))
499
            ->execute()
500
            ->next();
501
        if ($entry_id == null || !$field || !$section) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $entry_id of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
502
            return null;
503
        }
504
        $fieldId = $field->get('id');
505
        $where = $field->generateWhereFilter($entry_id);
506
        $data = Symphony::Database()
507
            ->select(['entries'])
508
            ->from('tbl_entries_data_' . $fieldId, 'd')
509
            ->where($where)
510
            ->execute()
511
            ->rows();
512
513
        // if (!is_array($data) || !($data = current($data))) {
514
        //     return null;
515
        // }
516
        return $data['entries'];
517
    }
518
519
    /**
520
     * Format this field value for display as readable text value.
521
     *
522
     * @param array $data
523
     *  an associative array of data for this string. At minimum this requires a
524
     *  key of 'value'.
525
     * @param integer $entry_id (optional)
526
     *  An option entry ID for more intelligent processing. Defaults to null.
527
     * @param string $defaultValue (optional)
528
     *  The value to use when no plain text representation of the field's data
529
     *  can be made. Defaults to null.
530
     * @return string
531
     *  the readable text summary of the values of this field instance.
532
     */
533
    public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = 'None')
534
    {
535
        $field = $this->fieldManager
536
            ->select()
537
            ->field($this->get('linked_field_id'))
538
            ->execute()
539
            ->next();
540
        $section = $this->sectionManager
541
            ->select()
542
            ->section($this->get('linked_section_id'))
543
            ->execute()
544
            ->next();
545
        if ($entry_id == null || !$field || !$section) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $entry_id of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
546
            return __($defaultValue);
547
        }
548
549
        $fieldId = $field->get('id');
550
        $where = $field->generateWhereFilter($entry_id);
551
        $entries = Symphony::Database()
552
            ->select(['*'])
553
            ->distinct()
554
            ->from('tbl_entries_data_' . $fieldId, 'd')
555
            ->where($where)
556
            ->execute()
557
            ->rows();
558
559
        $output = array();
560
        foreach ($entries as $e) {
561
            $e['entries'] = $entry_id;
562
            $output[] = $field->prepareReadableValue($e, $e['entry_id'], false, $defaultValue);
563
        }
564
        return implode('', $output);
565
    }
566
567
    /**
568
     * Format this field value for display in the publish index tables.
569
     *
570
     * @param array $data
571
     *  an associative array of data for this string. At minimum this requires a
572
     *  key of 'value'.
573
     * @param XMLElement $link (optional)
574
     *  an XML link structure to append the content of this to provided it is not
575
     *  null. it defaults to null.
576
     * @param integer $entry_id (optional)
577
     *  An option entry ID for more intelligent processing. defaults to null
578
     * @return string
579
     *  the formatted string summary of the values of this field instance.
580
     */
581
    public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
582
    {
583
        // $field = FieldManager::fetch($this->get('linked_field_id'));
584
        $field = $this->fieldManager
585
            ->select()
586
            ->field($this->get('linked_field_id'))
587
            ->execute()
588
            ->next();
589
        $section = $this->sectionManager
590
            ->select()
591
            ->section($this->get('linked_section_id'))
592
            ->execute()
593
            ->next();
594
        if ($entry_id == null || !$field || !$section) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $entry_id of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
595
            return __($defaultValue);
0 ignored issues
show
Bug introduced by
The variable $defaultValue does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
596
        }
597
598
        $fieldId = $field->get('id');
599
        $where = $field->generateWhereFilter($entry_id);
600
        $entries = Symphony::Database()
601
            ->select(['*'])
602
            ->distinct()
603
            ->from('tbl_entries_data_' . $fieldId, 'd')
604
            ->where($where)
605
            ->execute()
606
            ->rows();
607
        $output = array();
608
        $this->set('elements', '*');
609
        $this->set('sections', $this->get('linked_section_id'));
610
        foreach ($entries as $position => $e) {
611
            $e['entries'] = $entry_id;
612
            $value = $field->prepareTableValue($e, $link, $e['entry_id']);
613
614
            if ($this->get('mode_table')) {
615
                $entry = current($this->entryManager
616
                    ->select()
617
                    ->entry($e['entry_id'])
618
                    ->execute()
619
                    ->row());
620
                $cellcontent = ERFXSLTUTilities::processXSLT($this, $entry, $section->get('handle'), $section->fetchFields(), 'mode_table', isset($_REQUEST['debug']), 'entry', $position + 1);
621
622
                $cellcontent = trim($cellcontent);
623
                if (General::strlen($cellcontent)) {
624
                    if ($link) {
625
                        $link->setValue($cellcontent);
626
                        $value = $link->generate();
627
                    } else {
628
                        $value = $cellcontent;
629
                    }
630
                }
631
            } else if ($link) {
632
                $link->setValue($value);
633
                $value = $link->generate();
634
            }
635
636
            $output[] = $value;
637
        }
638
        return implode('', $output);
639
    }
640
641
    /**
642
     * Build the SQL command to append to the default query to enable
643
     * sorting of this field. By default this will sort the results by
644
     * the entry id in ascending order.
645
     *
646
     * Extension developers should always implement both `buildSortingSQL()`
647
     * and `buildSortingSelectSQL()`.
648
     *
649
     * @uses Field::isRandomOrder()
650
     * @see Field::buildSortingSelectSQL()
651
     * @param string $joins
652
     *  the join element of the query to append the custom join sql to.
653
     * @param string $where
654
     *  the where condition of the query to append to the existing where clause.
655
     * @param string $sort
656
     *  the existing sort component of the sql query to append the custom
657
     *  sort sql code to.
658
     * @param string $order (optional)
659
     *  an optional sorting direction. this defaults to ascending. if this
660
     *  is declared either 'random' or 'rand' then a random sort is applied.
661
     */
662
    public function buildSortingSQL(&$joins, &$where, &$sort, $order = 'ASC')
663
    {
664
        if ($this->isRandomOrder($order)) {
665
            $sort = 'ORDER BY RAND()';
666
        } else {
667
            $sort = "ORDER BY `order_value` $order";
668
        }
669
    }
670
671
    /**
672
     * Build the needed SQL clause command to make `buildSortingSQL()` work on
673
     * MySQL 5.7 in strict mode, which requires all columns in the ORDER BY
674
     * clause to be included in the SELECT's projection.
675
     *
676
     * If no new projection is needed (like if the order is made via a sub-query),
677
     * simply return null.
678
     *
679
     * For backward compatibility, this method checks if the sort expression
680
     * contains `ed`.`value`. This check will be removed in Symphony 3.0.0.
681
     *
682
     * Extension developers should make their Fields implement
683
     * `buildSortingSelectSQL()` when overriding `buildSortingSQL()`.
684
     *
685
     * @since Symphony 2.7.0
686
     * @uses Field::isRandomOrder()
687
     * @see Field::buildSortingSQL()
688
     * @param string $sort
689
     *  the existing sort component of the sql query, after it has been passed
690
     *  to `buildSortingSQL()`
691
     * @param string $order (optional)
692
     *  an optional sorting direction. this defaults to ascending. Should be the
693
     *  same value that was passed to `buildSortingSQL()`
694
     * @return string
695
     *  an optional select clause to append to the generated SQL query.
696
     *  This is needed when sorting on a column that is not part of the projection.
697
     */
698
    public function buildSortingSelectSQL($sort, $order = 'ASC')
699
    {
700
        if ($this->isRandomOrder($order)) {
701
            return null;
702
        }
703
704
        $field = FieldManager::fetch($this->get('linked_field_id'));
705
        $section = SectionManager::fetch($this->get('linked_section_id'));
706
        $fieldId = $field->get('id');
707
        $sortableFieldId = 0;
708
        foreach ($section->fetchFields() as $f) {
709
            if ($f->isSortable()) {
710
                $sortableFieldId = $f->get('id');
711
                break;
712
            }
713
        }
714
        if (!$sortableFieldId) {
715
            return;
716
        }
717
718
        $sql = "SELECT `s`.`value` FROM `tbl_entries_data_$sortableFieldId` as `s` LEFT JOIN `tbl_entries_data_$fieldId` AS `d` ON `d`.`entry_id` = `s`.`entry_id` WHERE FIND_IN_SET(`e`.`id`, `d`.`entries`) LIMIT 1";
719
        return "($sql) AS `order_value`";
720
    }
721
722
    /**
723
     * Creates the table needed for the settings of the field
724
     */
725
    public static function createFieldTable()
726
    {
727
        return Symphony::Database()
728
            ->create(self::FIELD_TBL_NAME)
729
            ->ifNotExists()
730
            ->charset('utf8')
731
            ->collate('utf8_unicode_ci')
732
            ->fields([
733
                'id' => [
734
                    'type' => 'int(11)',
735
                    'auto' => true,
736
                ],
737
                'field_id' => 'int(11)',
738
                'linked_section_id' => 'int(11)',
739
                'linked_field_id' => 'int(11)',
740
                'mode' => [
741
                    'type' => 'varchar(50)',
742
                    'null' => true,
743
                ],
744
                'mode_table' => [
745
                    'type' => 'varchar(50)',
746
                    'null' => true,
747
                ],
748
                'mode_header' => [
749
                    'type' => 'varchar(50)',
750
                    'null' => true,
751
                ],
752
                'mode_footer' => [
753
                    'type' => 'varchar(50)',
754
                    'null' => true,
755
                ],
756
            ])
757
            ->keys([
758
                'id' => 'primary',
759
                'field_id' => 'unique',
760
            ])
761
            ->execute()
762
            ->success();
763
    }
764
765
    public static function update_200()
766
    {
767
        return static::createFieldTable();
768
    }
769
770
    public static function update_210()
771
    {
772
        return Symphony::Database()
773
            ->alter(self::FIELD_TBL_NAME)
774
            ->add([
775
                'mode' => [
776
                    'type' => 'varchar(50)',
777
                    'null' => true,
778
                ],
779
                'mode_table' => [
780
                    'type' => 'varchar(50)',
781
                    'null' => true,
782
                ],
783
                'mode_header' => [
784
                    'type' => 'varchar(50)',
785
                    'null' => true,
786
                ],
787
                'mode_footer' => [
788
                    'type' => 'varchar(50)',
789
                    'null' => true,
790
                ],
791
            ])
792
            ->after('linked_field_id')
793
            ->execute()
794
            ->success();
795
    }
796
797
    /**
798
     *
799
     * Drops the table needed for the settings of the field
800
     */
801
    public static function deleteFieldTable()
802
    {
803
        return Symphony::Database()
804
            ->drop(self::FIELD_TBL_NAME)
805
            ->ifExists()
806
            ->execute()
807
            ->success();
808
    }
809
}
810