Completed
Push — master ( 900782...a8f522 )
by Nate
56s
created

ElementListTrait::ensureSortOrder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipboxfactory/craft-element-lists/LICENSE
6
 * @link       https://github.com/flipboxfactory/craft-element-lists/
7
 */
8
9
namespace flipbox\craft\element\lists\fields;
10
11
use Craft;
12
use craft\base\Element;
13
use craft\base\ElementInterface;
14
use craft\events\FieldElementEvent;
15
use craft\helpers\StringHelper;
16
use flipbox\craft\element\lists\relationships\RelationshipInterface;
17
18
/**
19
 * @author Flipbox Factory <[email protected]>
20
 * @since 2.0.0
21
 *
22
 * @property string $settingsTemplate
23
 */
24
trait ElementListTrait
25
{
26
    use ModifyElementQueryTrait,
27
        NormalizeValueTrait,
28
        InputTrait;
29
30
    /**
31
     * @var bool
32
     */
33
    protected $ignoreSearchKeywords = true;
34
35
    /**
36
     * Prepares the field’s value to be stored somewhere, like the content table or JSON-encoded in an entry
37
     * revision table.
38
     *
39
     * Data types that are JSON-encodable are safe (arrays, integers, strings, booleans, etc).
40
     * Whatever this returns should be something [[normalizeValue()]] can handle.
41
     *
42
     * @param mixed $value The raw field value
43
     * @param ElementInterface|null $element The element the field is associated with, if there is one
44
     * @return mixed The serialized field value
45
     */
46
    public function serializeValue($value, ElementInterface $element = null)
47
    {
48
        return $this->normalizeValue($value, $element)->getRelationships()->pluck('targetId')->all();
49
    }
50
51
    /**
52
     * Returns whether the given value should be considered “empty” to a validator.
53
     *
54
     * @param mixed $value The field’s value
55
     * @param ElementInterface $element The element the field is associated with, if there is one
56
     * @return bool Whether the value should be considered “empty”
57
     * @see Validator::$isEmpty
58
     */
59
    public function isValueEmpty($value, ElementInterface $element): bool
60
    {
61
        return $this->normalizeValue($value, $element)->getRelationships()->isEmpty();
62
    }
63
64
    /**
65
     * /**
66
     * Returns a static (non-editable) version of the field’s input HTML.
67
     *
68
     * This function is called to output field values when viewing entry drafts.
69
     *
70
     * @param mixed $value The field’s value
71
     * @param ElementInterface $element The element the field is associated with
72
     * @return string The static version of the field’s input HTML
73
     * @throws \Twig\Error\LoaderError
74
     * @throws \Twig\Error\RuntimeError
75
     * @throws \Twig\Error\SyntaxError
76
     */
77
    public function getStaticHtml($value, ElementInterface $element): string
78
    {
79
        $relationship = $this->normalizeValue($value, $element);
80
81
        $value = $relationship->getCollection()->all();
82
83
        if (empty($value)) {
84
            return '<p class="light">' . Craft::t('app', 'Nothing selected.') . '</p>';
85
        }
86
87
        $view = Craft::$app->getView();
88
        $id = $view->formatInputId($this->handle);
89
        $html = "<div id='{$id}' class='elementselect'><div class='elements'>";
90
91
        foreach ($value as $relatedElement) {
92
            $html .= Craft::$app->getView()->renderTemplate('_elements/element', [
93
                'element' => $relatedElement
94
            ]);
95
        }
96
97
        $html .= '</div></div>';
98
99
        $nsId = $view->namespaceInputId($id);
100
        $js = <<<JS
101
(new Craft.ElementThumbLoader()).load($('#{$nsId}'));
102
JS;
103
        $view->registerJs($js);
104
105
        return $html;
106
    }
107
108
    /**
109
     * Returns the HTML that should be shown for this field in Table View.
110
     *
111
     * @param mixed $value The field’s value
112
     * @param ElementInterface $element The element the field is associated with
113
     * @return string The HTML that should be shown for this field in Table View
114
     * @throws \Twig\Error\LoaderError
115
     * @throws \Twig\Error\RuntimeError
116
     * @throws \Twig\Error\SyntaxError
117
     */
118
    public function getTableAttributeHtml($value, ElementInterface $element): string
119
    {
120
        $relationship = $this->normalizeValue($value, $element);
121
122
        if (!$element = $relationship->getCollection()->first()) {
123
            return '';
124
        }
125
126
        return Craft::$app->getView()->renderTemplate('_elements/element', [
127
            'element' => $element
128
        ]);
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134
    public function getSearchKeywords($value, ElementInterface $element): string
135
    {
136
        if ($this->ignoreSearchKeywords === true) {
137
            return '';
138
        }
139
140
        $relationship = $this->normalizeValue($value, $element);
141
142
        $titles = [];
143
        foreach ($relationship->getCollection()->all() as $relatedElement) {
144
            $titles[] = (string)$relatedElement;
145
        }
146
147
        return StringHelper::toString($titles, ' ');
148
    }
149
150
    /**
151
     * Identify whether a sort order should be enforced.
152
     *
153
     * @return bool
154
     */
155
    public function ensureSortOrder(): bool
156
    {
157
        return $this->sortable;
0 ignored issues
show
Bug introduced by
The property sortable does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
158
    }
159
160
    /**
161
     * Allow the settings to identify whether the element should be sortable
162
     *
163
     * @param bool $sortable
164
     * @return $this
165
     */
166
    public function setSortable(bool $sortable = null)
167
    {
168
        $this->sortable = ($sortable === true);
169
        return $this;
170
    }
171
172
    /**
173
     * Get the sortable attribute value
174
     *
175
     * @return bool
176
     */
177
    public function getSortable(): bool
178
    {
179
        return $this->sortable;
180
    }
181
182
    /**
183
     * @inheritDoc
184
     * @throws \Twig\Error\LoaderError
185
     * @throws \Twig\Error\RuntimeError
186
     * @throws \Twig\Error\SyntaxError
187
     */
188
    public function getSettingsHtml()
189
    {
190
        /** @var ElementInterface|string $elementType */
191
        $elementType = $this->elementType();
192
193
        return Craft::$app->getView()->renderTemplate(
194
            'element-lists/_components/fieldtypes/settings',
195
            [
196
                'settingsTemplate' => $this->settingsTemplate,
197
                'field' => $this,
198
                'pluralElementType' => $elementType::pluralDisplayName(),
199
            ]
200
        );
201
    }
202
203
    /**
204
     * Our value is not an ElementQueryInterface and therefore we should handle it
205
     * differently.
206
     *
207
     * @inheritdoc
208
     */
209
    public function afterElementSave(ElementInterface $element, bool $isNew)
210
    {
211
        // Skip if the element is just propagating, and we're not localizing relations
212
        /** @var Element $element */
213
        if (!$element->propagating || $this->localizeRelations) {
0 ignored issues
show
Bug introduced by
The property localizeRelations does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
214
            /** @var RelationshipInterface $value */
215
            $value = $element->getFieldValue($this->handle);
216
217
            if ($value->isMutated()) {
218
                $value->save();
219
            }
220
        }
221
222
        // Trigger an 'afterElementSave' event
223
        if ($this->hasEventHandlers(self::EVENT_AFTER_ELEMENT_SAVE)) {
0 ignored issues
show
Bug introduced by
It seems like hasEventHandlers() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
224
            $this->trigger(self::EVENT_AFTER_ELEMENT_SAVE, new FieldElementEvent([
0 ignored issues
show
Bug introduced by
It seems like trigger() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
225
                'element' => $element,
226
                'isNew' => $isNew,
227
            ]));
228
        }
229
    }
230
}
231