Completed
Push — master ( 2867c5...18d669 )
by Nicolas
08:20 queued 04:11
created

FieldReverse_Relationship::update_210()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
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.cacheablefetch.php');
11
require_once(EXTENSIONS . '/entry_relationship_field/lib/class.erfxsltutilities.php');
12
13
/**
14
 *
15
 * Field class that will represent a reverse relationship between entries
16
 * @author Deux Huit Huit
17
 *
18
 */
19
class FieldReverse_Relationship extends FieldRelationship
20
{
21
    /**
22
     *
23
     * Name of the field table
24
     *  @var string
25
     */
26
    const FIELD_TBL_NAME = 'tbl_fields_reverse_relationship';
27
28
    /**
29
     *
30
     * Constructor for the Reverse_Relationship Field object
31
     */
32
    public function __construct()
33
    {
34
        // call the parent constructor
35
        parent::__construct();
36
        // set the name of the field
37
        $this->_name = __('Reverse Relationship');
38
        // allowed to make it required
39
        $this->_required = true;
40
        // allowed to show in the table columns
41
        $this->_showcolumn = true;
42
        // forbid association
43
        $this->_showassociation = false;
44
        // set as not required by default
45
        $this->set('required', 'no');
46
        // show header by default
47
        $this->set('show_header', 'yes');
48
        // allow link by default
49
        $this->set('allow_unlink', 'yes');
50
        // allow go to by default
51
        $this->set('allow_goto', 'yes');
52
        // no modes
53
        $this->set('mode', null);
54
        $this->set('mode_table', null);
55
        $this->set('mode_header', null);
56
        $this->set('mode_footer', null);
57
        // no links
58
        $this->set('linked_section_id', null);
59
        $this->set('linked_field_id', null);
60
    }
61
62
    public function isSortable()
63
    {
64
        return true;
65
    }
66
67
    public function canFilter()
68
    {
69
        return false;
70
    }
71
    
72
    public function canPublishFilter()
73
    {
74
        return false;
75
    }
76
77
    public function canImport()
78
    {
79
        return false;
80
    }
81
82
    public function canPrePopulate()
83
    {
84
        return false;
85
    }
86
    
87
    public function mustBeUnique()
88
    {
89
        return false;
90
    }
91
92
    public function allowDatasourceOutputGrouping()
93
    {
94
        return false;
95
    }
96
97
    public function requiresSQLGrouping()
98
    {
99
        return false;
100
    }
101
102
    public function allowDatasourceParamOutput()
103
    {
104
        return false;
105
    }
106
107
    public function requiresTable()
108
    {
109
        return false;
110
    }
111
112
    public function createTable()
113
    {
114
        return false;
115
    }
116
117
    public function fetchIncludableElements()
118
    {
119
    }
120
121
    /* ********** INPUT AND FIELD *********** */
122
123
    /**
124
     * 
125
     * Validates input
126
     * Called before <code>processRawFieldData</code>
127
     * @param $data
128
     * @param $message
129
     * @param $entry_id
130
     */
131
    public function checkPostFieldData($data, &$message, $entry_id=null)
132
    {
133
        return self::__OK__;
134
    }
135
136
137
    /**
138
     *
139
     * Process data before saving into database.
140
     *
141
     * @param array $data
142
     * @param int $status
143
     * @param boolean $simulate
144
     * @param int $entry_id
145
     *
146
     * @return Array - data to be inserted into DB
147
     */
148
    public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
149
    {
150
        $status = self::__OK__;
151
        return null;
152
    }
153
154
155
    /**
156
     *
157
     * Validates the field settings before saving it into the field's table
158
     */
159
    public function checkFields(Array &$errors, $checkForDuplicates = true)
160
    {
161
        $parent = parent::checkFields($errors, $checkForDuplicates);
162
        if ($parent != self::__OK__) {
163
            return $parent;
164
        }
165
        
166
        $sections = $this->get('linked_section_id');
167
        if (empty($sections)) {
168
            $errors['sections'] = __('A section must be chosen');
169
        }
170
        $field = $this->get('linked_field_id');
171
        if (empty($field)) {
172
            $errors['field'] = __('A field must be chosen');
173
        }
174
175
        return (!empty($errors) ? self::__ERROR__ : self::__OK__);
176
    }
177
178
    /**
179
     *
180
     * Save field settings into the field's table
181
     */
182
    public function commit()
183
    {
184
        // if the default implementation works...
185
        if(!parent::commit()) return false;
186
        
187
        $id = $this->get('id');
188
        
189
        // exit if there is no id
190
        if ($id == false) return false;
191
        
192
        // declare an array contains the field's settings
193
        $settings = array(
194
            'linked_section_id' => $this->get('linked_section_id'),
195
            'linked_field_id' => $this->get('linked_field_id'),
196
            'mode' => $this->get('mode'),
197
            'mode_table' => $this->get('mode_table'),
198
            'mode_header' => $this->get('mode_header'),
199
            'mode_footer' => $this->get('mode_footer'),
200
        );
201
        
202
        return FieldManager::saveSettings($id, $settings);
203
    }
204
205
    /**
206
     * Appends data into the XML tree of a Data Source
207
     * @param $wrapper
208
     * @param $data
209
     */
210
    public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
211
    {
212
        // nothing to do
213
    }
214
215
    /* ********* UI *********** */
216
    
217
    /**
218
     *
219
     * Builds the UI for the field's settings when creating/editing a section
220
     * @param XMLElement $wrapper
221
     * @param array $errors
222
     */
223
    public function displaySettingsPanel(XMLElement &$wrapper, $errors = null)
224
    {
225
        /* first line, label and such */
226
        parent::displaySettingsPanel($wrapper, $errors);
227
228
        // fieldset
229
        $fieldset = new XMLElement('fieldset', null);
230
231
        // group
232
        $group = new XMLElement('div', null, array('class' => 'two columns'));
233
        $fieldset->appendChild($group);
234
235
        // sections
236
        $sections = new XMLElement('div', null, array('class' => 'column'));
237
        $this->appendSelectionSelect($sections);
238 View Code Duplication
        if (is_array($errors) && isset($errors['sections'])) {
239
            $sections = Widget::Error($sections, $errors['sections']);
240
        }
241
        $group->appendChild($sections);
242
243
        // field
244
        $field = new XMLElement('div', null, array('class' => 'column'));
245
        $this->appendFieldSelect($field);
246 View Code Duplication
        if (is_array($errors) && isset($errors['field'])) {
247
            $field = Widget::Error($field, $errors['field']);
248
        }
249
        $group->appendChild($field);
250
251
        $wrapper->appendChild($fieldset);
252
253
        // xsl
254
        $xsl = new XMLElement('fieldset');
255
        $xsl->appendChild(new XMLElement('legend', __('Backend XSL templates options')));
256
        $xsl_cols = new XMLElement('div');
257
        $xsl_cols->setAttribute('class', 'four columns');
258
259
        // xsl mode
260
        $xslmode = Widget::Label();
261
        $xslmode->setValue(__('XSL mode for entries content template'));
262
        $xslmode->setAttribute('class', 'column');
263
        $xslmode->appendChild(Widget::Input($this->createSettingsFieldName('mode'), $this->get('mode'), 'text'));
264
        $xsl_cols->appendChild($xslmode);
265
266
        // xsl header mode
267
        $xslmodetable = Widget::Label();
268
        $xslmodetable->setValue(__('XSL mode for entries header template'));
269
        $xslmodetable->setAttribute('class', 'column');
270
        $xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_header'), $this->get('mode_header'), 'text'));
271
        $xsl_cols->appendChild($xslmodetable);
272
273
        // xsl table mode
274
        $xslmodetable = Widget::Label();
275
        $xslmodetable->setValue(__('XSL mode for publish table value'));
276
        $xslmodetable->setAttribute('class', 'column');
277
        $xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_table'), $this->get('mode_table'), 'text'));
278
        $xsl_cols->appendChild($xslmodetable);
279
280
        // xsl action bar mode
281
        $xslmodetable = Widget::Label();
282
        $xslmodetable->setValue(__('XSL mode for publish action bar'));
283
        $xslmodetable->setAttribute('class', 'column');
284
        $xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_footer'), $this->get('mode_footer'), 'text'));
285
        $xsl_cols->appendChild($xslmodetable);
286
287
        $xsl->appendChild($xsl_cols);
288
        $wrapper->appendChild($xsl);
289
290
        // Footer
291
        $this->appendStatusFooter($wrapper);
292
    }
293
294
    /**
295
     *
296
     * Builds the UI for the publish page
297
     * @param XMLElement $wrapper
298
     * @param mixed $data
299
     * @param mixed $flagWithError
300
     * @param string $fieldnamePrefix
301
     * @param string $fieldnamePostfix
302
     */
303
    public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
304
    {
305
        if (!$entry_id) {
306
            return;
307
        }
308
        $field = FieldManager::fetch($this->get('linked_field_id'));
309
        $section = SectionManager::fetch($this->get('linked_section_id'));
310
        if (!($field instanceof FieldRelationship)) {
311
            $flagWithError = __('Linked field is not valid. Please edit this field to set it to a valid ER field.');
312
        }
313
        
314
        $label = Widget::Label($this->get('label'));
315
        // label error management
316
        if ($flagWithError != null) {
317
            $wrapper->appendChild(Widget::Error($label, $flagWithError));
318
        } else {
319
            $wrapper->appendChild($label);
320
            $wrapper->appendChild($this->createEntriesList(array()));
321
            $wrapper->appendChild($this->createActionBarMenu($field));
322
        }
323
        
324
        $wrapper->setAttribute('data-field-id', $this->get('id'));
325
        $wrapper->setAttribute('data-linked-field-id', $this->get('linked_field_id'));
326
        $wrapper->setAttribute('data-linked-section-id', $this->get('linked_section_id'));
327
        $wrapper->setAttribute('data-linked-section', $section->get('handle'));
328
        $wrapper->setAttribute('data-field-label', $field->get('label'));
329
        $wrapper->setAttribute(
330
            'data-entries',
331
            implode(self::SEPARATOR, $field->findRelatedEntries($entry_id, null))
332
        );
333
        if (isset($_REQUEST['debug'])) {
334
            $wrapper->setAttribute('data-debug', true);
335
        }
336
    }
337
338
    private function createActionBarMenu($field)
339
    {
340
        $section = SectionManager::fetch($this->get('linked_section_id'));
341
        $wrap = new XMLElement('div');
342
343
        $fieldset = new XMLElement('fieldset');
344
        $fieldset->setAttribute('class', 'single');
345
        $fieldset->appendChild(new XMLElement(
346
            'span',
347
            __('Related section: '),
348
            array('class' => 'reverse-selection')
349
        ));
350
        $fieldset->appendChild(new XMLElement(
351
            'label',
352
            General::sanitize($section->get('name') . ': ' . $field->get('label')),
353
            array('class' => 'reverse-selection')
354
        ));
355
        $fieldset->appendChild(new XMLElement('button', __('Add to entry'), array(
356
            'type' => 'button',
357
            'class' => 'add',
358
            'data-add' => $section->get('handle'),
359
        )));
360
361
        $wrap->appendChild($fieldset);
362
363
        return $wrap;
364
    }
365
366
    private static $erFields = array();
367
    private function getERFields()
368
    {
369
        if (empty(self::$erFields)) {
370
            self::$erFields = FieldManager::fetch(null, null, null, 'id', 'entry_relationship');
371
        }
372
        return self::$erFields;
373
    }
374
375
    private static $erSections = array();
376
    private function getERSections()
377
    {
378
        if (empty(self::$erSections)) {
379
            $erFields = self::getERFields();
380
            $sectionIds = array_map(function ($erField) {
381
                return $erField->get('parent_section');
382
            }, $erFields);
383
            self::$erSections = SectionManager::fetch($sectionIds);
384
        }
385
        return self::$erSections;
386
    }
387
388
    private function buildSectionSelect($name)
389
    {
390
        $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...
391
        $options = array();
392
        
393 View Code Duplication
        foreach ($sections as $section) {
394
            $driver = $section->get('id');
395
            $selected = $driver === $this->get('linked_section_id');
396
            $options[] = array($driver, $selected, General::sanitize($section->get('name')));
397
        }
398
        
399
        return Widget::Select($name, $options);
400
    } 
401
402 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...
403
    {
404
        $name = $this->createSettingsFieldName('linked_section_id', false);
405
406
        $input = $this->buildSectionSelect($name);
407
        $input->setAttribute('class', 'reverse_relationship-sections');
408
409
        $label = Widget::Label();
410
411
        $label->setValue(__('Available sections %s', array($input->generate())));
412
413
        $wrapper->appendChild($label);
414
    }
415
416
    private function buildFieldSelect($name)
417
    {
418
        $section = $this->get('linked_section_id') ? SectionManager::fetch($this->get('linked_section_id')) : null;
419
        $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...
420
        $options = array();
421
        
422
        foreach ($fields as $field) {
423
            if ($section && $section->get('id') !== $field->get('parent_section')) {
424
                continue;
425
            }
426
            $driver = $field->get('id');
427
            $selected = $driver === $this->get('linked_field_id');
428
            $options[] = array($driver, $selected, General::sanitize($field->get('label')));
429
        }
430
        
431
        return Widget::Select($name, $options);
432
    } 
433
434 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...
435
    {
436
        $name = $this->createSettingsFieldName('linked_field_id', false);
437
438
        $input = $this->buildFieldSelect($name);
439
        $input->setAttribute('class', 'reverse_relationship-field');
440
441
        $label = Widget::Label();
442
443
        $label->setValue(__('Available Fields %s', array($input->generate())));
444
445
        $wrapper->appendChild($label);
446
    }
447
448
    /**
449
     *
450
     * Return a plain text representation of the field's data
451
     * @param array $data
452
     * @param int $entry_id
453
     */
454
    public function prepareTextValue($data, $entry_id = null)
455
    {
456
        $field = FieldManager::fetch($this->get('linked_field_id'));
457
        $section = SectionManager::fetch($this->get('linked_section_id'));
458
        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...
459
            return null;
460
        }
461
        $fieldId = $field->get('id');
462
        $where = $field->generateWhereFilter($entry_id);
463
        $data = Symphony::Database()->fetch("SELECT `entries` FROM `tbl_entries_data_$fieldId` AS `d` WHERE 1=1 $where");
464
        if (!is_array($data) || !($data = current($data))) {
465
            return null;
466
        }
467
        return $data['entries'];
468
    }
469
470
    /**
471
     * Format this field value for display as readable text value.
472
     *
473
     * @param array $data
474
     *  an associative array of data for this string. At minimum this requires a
475
     *  key of 'value'.
476
     * @param integer $entry_id (optional)
477
     *  An option entry ID for more intelligent processing. Defaults to null.
478
     * @param string $defaultValue (optional)
479
     *  The value to use when no plain text representation of the field's data
480
     *  can be made. Defaults to null.
481
     * @return string
482
     *  the readable text summary of the values of this field instance.
483
     */
484
    public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = 'None')
485
    {
486
        $field = FieldManager::fetch($this->get('linked_field_id'));
487
        $section = SectionManager::fetch($this->get('linked_section_id'));
488
        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...
489
            return __($defaultValue);
490
        }
491
492
        $fieldId = $field->get('id');
493
        $where = $field->generateWhereFilter($entry_id);
494
        $entries = Symphony::Database()->fetch("SELECT DISTINCT * FROM `tbl_entries_data_$fieldId` AS `d` WHERE 1=1 $where");
495
        $output = array();
496
        foreach ($entries as $e) {
497
            $e['entries'] = $entry_id;
498
            $output[] = $field->prepareReadableValue($e, $e['entry_id'], false, $defaultValue);
499
        }
500
        return implode('', $output);
501
    }
502
503
    /**
504
     * Format this field value for display in the publish index tables.
505
     *
506
     * @param array $data
507
     *  an associative array of data for this string. At minimum this requires a
508
     *  key of 'value'.
509
     * @param XMLElement $link (optional)
510
     *  an XML link structure to append the content of this to provided it is not
511
     *  null. it defaults to null.
512
     * @param integer $entry_id (optional)
513
     *  An option entry ID for more intelligent processing. defaults to null
514
     * @return string
515
     *  the formatted string summary of the values of this field instance.
516
     */
517
    public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
518
    {
519
        $field = FieldManager::fetch($this->get('linked_field_id'));
520
        $section = SectionManager::fetch($this->get('linked_section_id'));
521
        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...
522
            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...
523
        }
524
525
        $fieldId = $field->get('id');
526
        $where = $field->generateWhereFilter($entry_id);
527
        $entries = Symphony::Database()->fetch("SELECT DISTINCT * FROM `tbl_entries_data_$fieldId` AS `d` WHERE 1=1 $where");
528
        $output = array();
529
        $this->set('elements', '*');
530
        $this->set('sections', $this->get('linked_section_id'));
531
        foreach ($entries as $position => $e) {
532
            $e['entries'] = $entry_id;
533
            $value = $field->prepareTableValue($e, $link, $e['entry_id']);
534
535
            if ($this->get('mode_table')) {
536
                $entry = current(EntryManager::fetch($e['entry_id']));
537
                $cellcontent = ERFXSLTUTilities::processXSLT($this, $entry, $section->get('handle'), $section->fetchFields(), 'mode_table', isset($_REQUEST['debug']), 'entry', $position + 1);
538
539
                $cellcontent = trim($cellcontent);
540
                if (General::strlen($cellcontent)) {
541
                    if ($link) {
542
                        $link->setValue($cellcontent);
543
                        $value = $link->generate();
544
                    } else {
545
                        $value = $cellcontent;
546
                    }
547
                }
548
            } else if ($link) {
549
                $link->setValue($value);
550
                $value = $link->generate();
551
            }
552
553
            $output[] = $value;
554
        }
555
        return implode('', $output);
556
    }
557
558
    /**
559
     * Build the SQL command to append to the default query to enable
560
     * sorting of this field. By default this will sort the results by
561
     * the entry id in ascending order.
562
     *
563
     * Extension developers should always implement both `buildSortingSQL()`
564
     * and `buildSortingSelectSQL()`.
565
     *
566
     * @uses Field::isRandomOrder()
567
     * @see Field::buildSortingSelectSQL()
568
     * @param string $joins
569
     *  the join element of the query to append the custom join sql to.
570
     * @param string $where
571
     *  the where condition of the query to append to the existing where clause.
572
     * @param string $sort
573
     *  the existing sort component of the sql query to append the custom
574
     *  sort sql code to.
575
     * @param string $order (optional)
576
     *  an optional sorting direction. this defaults to ascending. if this
577
     *  is declared either 'random' or 'rand' then a random sort is applied.
578
     */
579
    public function buildSortingSQL(&$joins, &$where, &$sort, $order = 'ASC')
580
    {
581
        if ($this->isRandomOrder($order)) {
582
            $sort = 'ORDER BY RAND()';
583
        } else {
584
            $sort = "ORDER BY `order_value` $order";
585
        }
586
    }
587
588
    /**
589
     * Build the needed SQL clause command to make `buildSortingSQL()` work on
590
     * MySQL 5.7 in strict mode, which requires all columns in the ORDER BY
591
     * clause to be included in the SELECT's projection.
592
     *
593
     * If no new projection is needed (like if the order is made via a sub-query),
594
     * simply return null.
595
     *
596
     * For backward compatibility, this method checks if the sort expression
597
     * contains `ed`.`value`. This check will be removed in Symphony 3.0.0.
598
     *
599
     * Extension developers should make their Fields implement
600
     * `buildSortingSelectSQL()` when overriding `buildSortingSQL()`.
601
     *
602
     * @since Symphony 2.7.0
603
     * @uses Field::isRandomOrder()
604
     * @see Field::buildSortingSQL()
605
     * @param string $sort
606
     *  the existing sort component of the sql query, after it has been passed
607
     *  to `buildSortingSQL()`
608
     * @param string $order (optional)
609
     *  an optional sorting direction. this defaults to ascending. Should be the
610
     *  same value that was passed to `buildSortingSQL()`
611
     * @return string
612
     *  an optional select clause to append to the generated SQL query.
613
     *  This is needed when sorting on a column that is not part of the projection.
614
     */
615
    public function buildSortingSelectSQL($sort, $order = 'ASC')
616
    {
617
        if ($this->isRandomOrder($order)) {
618
            return null;
619
        }
620
621
        $section = SectionManager::fetch($this->get('linked_section_id'));
622
        $field = FieldManager::fetch($this->get('linked_field_id'));
623
        $fieldId = $field->get('id');
624
        $sortableFieldId = 0;
625
        foreach ($section->fetchFields() as $f) {
626
            if ($f->isSortable()) {
627
                $sortableFieldId = $f->get('id');
628
                break;
629
            }
630
        }
631
        if (!$sortableFieldId) {
632
            return;
633
        }
634
635
        $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";
636
        return "($sql) AS `order_value`";
637
    }
638
639
    /**
640
     * Creates the table needed for the settings of the field
641
     */
642
    public static function createFieldTable()
643
    {
644
        $tbl = self::FIELD_TBL_NAME;
645
646
        return Symphony::Database()->query("
647
            CREATE TABLE IF NOT EXISTS `$tbl` (
648
                `id`                int(11) unsigned NOT NULL AUTO_INCREMENT,
649
                `field_id`          int(11) unsigned NOT NULL,
650
                `linked_section_id` int(11) unsigned NOT NULL,
651
                `linked_field_id`   int(11) unsigned NOT NULL,
652
                `mode`              varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
653
                `mode_table`        varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
654
                `mode_header`       varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
655
                `mode_footer`       varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
656
                PRIMARY KEY (`id`),
657
                UNIQUE KEY `field_id` (`field_id`)
658
            ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
659
        ");
660
    }
661
662
    public static function update_200()
663
    {
664
        return static::createFieldTable();
665
    }
666
667
    public static function update_210()
668
    {
669
        $tbl = self::FIELD_TBL_NAME;
670
        $sql = "
671
            ALTER TABLE `$tbl`
672
                ADD COLUMN `mode` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
673
                    AFTER `linked_field_id`,
674
                ADD COLUMN `mode_table` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
675
                    AFTER `mode`,
676
                ADD COLUMN `mode_header` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
677
                    AFTER `mode_table`,
678
                ADD COLUMN `mode_footer` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
679
                    AFTER `mode_header`
680
        ";
681
        return Symphony::Database()->query($sql);
682
    }
683
684
    /**
685
     *
686
     * Drops the table needed for the settings of the field
687
     */
688
    public static function deleteFieldTable()
689
    {
690
        $tbl = self::FIELD_TBL_NAME;
691
692
        return Symphony::Database()->query("
693
            DROP TABLE IF EXISTS `$tbl`
694
        ");
695
    }
696
}
697