• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

heimrichhannot / contao-utils-bundle / 13965748749

20 Mar 2025 09:13AM UTC coverage: 79.29% (-0.03%) from 79.32%
13965748749

push

github

koertho
adjust rector config

1095 of 1381 relevant lines covered (79.29%)

3.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

42.16
/src/Util/FormatterUtil.php
1
<?php
2

3
namespace HeimrichHannot\UtilsBundle\Util;
4

5
use Codefog\TagsBundle\Model\TagModel;
6
use Contao\Config;
7
use Contao\Controller;
8
use Contao\CoreBundle\Framework\Adapter;
9
use Contao\CoreBundle\Framework\ContaoFramework;
10
use Contao\CoreBundle\InsertTag\InsertTagParser;
11
use Contao\DataContainer;
12
use Contao\Date;
13
use Contao\Environment;
14
use Contao\Model;
15
use Contao\StringUtil as Str;
16
use Contao\Validator;
17
use Contao\Widget;
18
use HeimrichHannot\UtilsBundle\Util\FormatterUtil\FormatDcaFieldValueOptions;
19

20
class FormatterUtil
21
{
22
    public function __construct(
23
        protected ContaoFramework $framework,
24
        protected InsertTagParser $insertTagParser,
25
        protected Utils $utils,
26
        protected array $kernelBundles
27
    ) {}
1✔
28

29
    /**
30
     * Makes a DCA field value human-readable.
31
     *
32
     * This succeeds {@see https://github.com/heimrichhannot/contao-utils-bundle/blob/ee122d2e267a60aa3200ce0f40d92c22028988e8/src/Form/FormUtil.php#L99 `prepareSpecialValueForOutput(...)`} from Utils v2.
33
     *
34
     * @param DataContainer $dc The data container whose table to use and options-callback to evaluate.
35
     * @param string $field The DCA field name.
36
     * @param array|string|null $value The value to format. If an array is passed, the values will be evaluated
37
     *     recursively.
38
     * @param ?FormatDcaFieldValueOptions $settings Additional settings.
39
     * @return mixed The formatted value.
40
     */
41
    public function formatDcaFieldValue(
42
        DataContainer              $dc,
43
        string                     $field,
44
        array|string|null          $value,
45
        FormatDcaFieldValueOptions $settings = null
46
    ): mixed {
47
        $settings ??= new FormatDcaFieldValueOptions();
1✔
48

49
        $value = Str::deserialize($value);
1✔
50
        $table = $dc->table;
1✔
51

52
        $controller = $this->framework->getAdapter(Controller::class);
1✔
53
        $controller->loadLanguageFile('default');
1✔
54

55
        if ($settings->loadDca) {
1✔
56
            $controller->loadDataContainer($table);
1✔
57
            $controller->loadLanguageFile($table);
1✔
58
        }
59

60
        // dca can be overridden from outside
61
        $data = is_array($settings->dcaOverride)
1✔
62
            ? $settings->dcaOverride
1✔
63
            : ($GLOBALS['TL_DCA'][$table]['fields'][$field] ?? null);
×
64

65
        if (!is_array($data)) {
1✔
66
            return $value;
×
67
        }
68

69
        $inputType = $data['inputType'] ?? null;
1✔
70

71
        if ($inputType === 'inputUnit')
1✔
72
        {
73
            return $this->formatInputUnitField($value, $settings->arrayJoiner);
1✔
74
        }
75

76
        if ($settings->optionsCache === null || !$settings->cacheOptions)
1✔
77
        {
78
            $optionsCallback = $data['options_callback'] ?? null;
1✔
79
            $options = $data['options'] ?? null;
1✔
80
            $settings->optionsCache = $optionsCallback
1✔
81
                ? $this->utils->dca()->executeCallback($optionsCallback, $dc) ?? $options
×
82
                : $options;
1✔
83
        }
84

85
        if (!is_array($settings->optionsCache)) {
1✔
86
            $settings->optionsCache = [];
1✔
87
        }
88

89
        if ($inputType === 'multiColumnEditor' && $this->isMultiColumnsActive() && is_array($value))
1✔
90
        {
91
            $callback = (fn(int|string $f, array|string|null $v): string => $this->formatDcaFieldValue($dc, $f, $v, $settings));
×
92

93
            return $this->formatMultiColumnField($value, $data, $callback);
×
94
        }
95

96
        if (is_array($value))
1✔
97
        {
98
            $callback = (fn(array|string|null $v): string => $this->formatDcaFieldValue($dc, $field, $v, $settings));
×
99

100
            return $this->formatArray($value, $settings, $callback);
×
101
        }
102

103
        if ($inputType === 'explanation'
1✔
104
            && (!empty($textCallback = $data['eval']['text_callback'] ?? null)
1✔
105
                || isset($data['eval']['text'])))
1✔
106
        {
107
            if ($textCallback) {
×
108
                $attributes = Widget::getAttributesFromDca($data, $field, $value, $field, $table, $dc);
×
109
                return $this->utils->dca()->executeCallback($textCallback, $attributes);
×
110
            }
111
            return $data['eval']['text'];
×
112
        }
113

114
        $rgxp = $data['eval']['rgxp'] ?? null;
1✔
115

116
        if (!empty($data['foreignKey']))
1✔
117
        {
118
            [$foreignTable, $foreignField] = explode('.', (string) $data['foreignKey']);
×
119

120
            $instance = $this->utils->model()->findModelInstanceByPk($foreignTable, $value);
×
121
            if (null !== $instance) {
×
122
                $value = $instance->{$foreignField};
×
123
            }
124
        }
125

126
        if ($inputType === 'cfgTags' && $tagModel = $this->getTagModel())
1✔
127
        {
128
            /** @var Model $tagModel */
129
            $collection = $tagModel->findBy(['source=?', 'id = ?'], [$data['eval']['tagsManager'], $value]);
×
130
            $value = null;
×
131

132
            if (null !== $collection) {
×
133
                $result = $collection->fetchEach('name');
×
134
                $value = implode($settings->arrayJoiner, $result);
×
135
            }
136
        }
137
        elseif ($rgxp === 'date')
1✔
138
        {
139
            $value = Date::parse(Config::get('dateFormat'), $value);
×
140
        }
141
        elseif ($rgxp === 'time')
1✔
142
        {
143
            $value = Date::parse(Config::get('timeFormat'), $value);
×
144
        }
145
        elseif ($rgxp === 'datim')
1✔
146
        {
147
            $value = Date::parse(Config::get('datimFormat'), $value);
×
148
        }
149
        elseif (Validator::isBinaryUuid($value))
1✔
150
        {
151
            $strPath = $this->utils->file()->getPathFromUuid($value);
×
152
            $value = $strPath ? Environment::get('url') . '/' . $strPath : Str::binToUuid($value);
×
153
        }
154
        elseif ($data['eval']['isBoolean'] ??
1✔
155
            $inputType === 'checkbox' && !($data['eval']['multiple'] ?? false))
1✔
156
        {
157
            $value = ('' != $value) ? $GLOBALS['TL_LANG']['MSC']['yes'] : $GLOBALS['TL_LANG']['MSC']['no'];
×
158
        }
159
        elseif (is_array($settings->optionsCache) && !array_is_list($settings->optionsCache))
1✔
160
        {
161
            $value = $settings->optionsCache[$value] ?? $value;
×
162
        }
163

164
        if ($settings->localize && ($reference = $data['reference'][$value] ?? null))
1✔
165
        {
166
            $value = is_array($reference)
×
167
                ? $reference[0] ?? $reference[array_key_first($reference)] ?? $value
×
168
                : $reference;
×
169
        }
170

171
        if ($settings->replaceInsertTags)
1✔
172
        {
173
            $value = $this->insertTagParser->replace($value);
1✔
174
        }
175

176
        return Str::specialchars($value);
1✔
177
    }
178

179
    /**
180
     * @return Adapter<Model>|Adapter<TagModel>|Model|TagModel|null
181
     * @noinspection PhpMixedReturnTypeCanBeReducedInspection
182
     * @phpstan-ignore-next-line For PHPStan, this method returns an object of an unknown class.
183
     */
184
    private function getTagModel(): mixed
185
    {
186
        if (class_exists(TagModel::class)) {
×
187
            return $this->framework->getAdapter(TagModel::class);
×
188
        }
189

190
        return null;
×
191
    }
192

193
    private function isMultiColumnsActive(): bool
194
    {
195
        return in_array(
×
196
            'HeimrichHannot\MultiColumnEditorBundle\HeimrichHannotContaoMultiColumnEditorBundle',
×
197
            $this->kernelBundles
×
198
        );
×
199
    }
200

201
    /**
202
     * @param array $values
203
     * @param FormatDcaFieldValueOptions $settings
204
     * @param callable(array|string|null $value): string $callback The callback to format each value, possibly
205
     *     recursively.
206
     * @return string
207
     */
208
    private function formatArray(
209
        array                      $values,
210
        FormatDcaFieldValueOptions $settings,
211
        callable                   $callback
212
    ): string {
213
        foreach ($values as $k => $v)
×
214
        {
215
            $result = $callback($v);
×
216

217
            if ($settings->preserveEmptyArrayValues)
×
218
            {
219
                $values[$k] = $result;
×
220
                continue;
×
221
            }
222

223
            if (empty($result))
×
224
            {
225
                unset($values[$k]);
×
226
            }
227
            else
228
            {
229
                $values[$k] = $result;
×
230
            }
231
        }
232

233
        return implode($settings->arrayJoiner, $values);
×
234
    }
235

236
    private function formatInputUnitField(array|string|null $values, string $arraySeparator): string
237
    {
238
        $data = Str::deserialize($values, true);
1✔
239
        return ($data['value'] ?? '') . $arraySeparator . ($data['unit'] ?? '');
1✔
240
    }
241

242
    /**
243
     * @param array $values
244
     * @param array $data
245
     * @param ?callable(int|string $field, array|string|null $value): string $callback
246
     *   Callback used to format each field value, possibly recursively.
247
     * @return string
248
     */
249
    private function formatMultiColumnField(array $values, array $data, callable $callback = null): string
250
    {
251
        $formatted = '';
×
252

253
        foreach ($values as $row) {
×
254
            $formatted .= "\t\n";
×
255

256
            foreach ($row as $fieldName => $fieldValue) {
×
257
                $dca = $data['eval']['multiColumnEditor']['fields'][$fieldName];
×
258

259
                $label = '';
×
260

261
                if (!$data['eval']['skipMceFieldLabels']) {
×
262
                    $label = ($dca['label'][0] ?: $fieldName).': ';
×
263

264
                    if ($data['eval']['skipMceFieldLabelFormatting']) {
×
265
                        $label = $fieldName.': ';
×
266
                    }
267
                }
268

269
                $formatted .= "\t" . $label . ($callback ? $callback($fieldName, $fieldValue) : '');
×
270
            }
271
        }
272

273
        $formatted .= "\t\n";
×
274

275
        return $formatted;
×
276
    }
277
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc