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

heimrichhannot / contao-utils-bundle / 6685862485

29 Oct 2023 09:22PM UTC coverage: 74.155% (+52.0%) from 22.152%
6685862485

Pull #67

github

koertho
cleanup composer file
Pull Request #67: Version 3

216 of 216 new or added lines in 19 files covered. (100.0%)

746 of 1006 relevant lines covered (74.16%)

3.69 hits per line

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

91.67
/src/Util/ModelUtil.php
1
<?php
2

3
/*
4
 * Copyright (c) 2023 Heimrich & Hannot GmbH
5
 *
6
 * @license LGPL-3.0-or-later
7
 */
8

9
namespace HeimrichHannot\UtilsBundle\Util;
10

11
use Contao\Controller;
12
use Contao\CoreBundle\Framework\Adapter;
13
use Contao\CoreBundle\Framework\ContaoFramework;
14
use Contao\Date;
15
use Contao\Model;
16
use Contao\Model\Collection;
17

18
class ModelUtil
19
{
20
    public function __construct(
21
        private ContaoFramework $framework
22
    )
23
    {
24
    }
7✔
25

26
    /**
27
     * Adds an published check to your model query.
28
     *
29
     * Options:
30
     * - publishedField: The name of the published field. Default: "published"
31
     * - startField: The name of the start field. Default: "start"
32
     * - stopField: The name of the stop field. Default: "stop"
33
     * - invertPublishedField: Set to true, if the published field should be evaluated inverted (for "hidden" or "invisible" fields. Default: false
34
     * - invertStartStopFields: Set to true, if the start and stop fields should be evaluated in an inverted manner. Default: false
35
     * - ignoreFePreview: Set to true, frontend preview should be ignored. Default: false
36
     *
37
     * @param string $table   The table name
38
     * @param array  $columns The columns array
39
     * @param array{
40
     *     publishedField?: string,
41
     *     startField?: string,
42
     *     stopField?: string,
43
     *     invertPublishedField?: bool,
44
     *     invertStartStopFields?: bool,
45
     *     ignoreFePreview?: bool
46
     * }  $options pass additional options
47
     */
48
    public function addPublishedCheckToModelArrays(string $table, array &$columns, array $options = []): void
49
    {
50
        $defaults = [
1✔
51
            'invertPublishedField' => false,
1✔
52
            'invertStartStopFields' => false,
1✔
53
            'publishedField' => 'published',
1✔
54
            'startField' => 'start',
1✔
55
            'stopField' => 'stop',
1✔
56
            'ignoreFePreview' => false,
1✔
57
        ];
1✔
58
        $options = array_merge($defaults, $options);
1✔
59

60
        $t = $table;
1✔
61

62
        if ($options['ignoreFePreview'] || !(\defined('BE_USER_LOGGED_IN') && BE_USER_LOGGED_IN === true)) {
1✔
63
            $time = Date::floorToMinute();
1✔
64

65
            $columns[] = "($t.".$options['startField'].($options['invertStartStopFields'] ? '!=' : '=')."'' OR $t.".$options['startField'].($options['invertStartStopFields'] ? '>' : '<=')."'$time') AND ($t.".$options['stopField'].($options['invertStartStopFields'] ? '!=' : '=')."'' OR $t.".$options['stopField'].($options['invertStartStopFields'] ? '<=' : '>')."'".($time + 60)."') AND $t.".$options['publishedField'].($options['invertPublishedField'] ? '!=' : '=')."'1'";
1✔
66
        }
67
    }
68

69
    /**
70
     * Returns model instances by given table and search criteria.
71
     *
72
     * Options:
73
     * - skipReplaceInsertTags: (bool) Skip the replacement of inserttags. Default: false
74
     *
75
     * @param array{
76
     *     skipReplaceInsertTags?: bool
77
     * } $options
78
     *
79
     * @return Model[]|Collection|null
80
     */
81
    public function findModelInstancesBy(string $table, array|string|null $columns, int|string|array|null $values, array $options = []): Collection|Model|null
82
    {
83
        $defaults = [
1✔
84
            'skipReplaceInsertTags' => false,
1✔
85
        ];
1✔
86
        $options = array_merge($defaults, $options);
1✔
87

88
        /* @var string|null $modelClass */
89
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
90
            return null;
1✔
91
        }
92

93
        /* @var Model $adapter */
94
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
95
            return null;
×
96
        }
97

98
        if (\is_array($values) && true !== $options['skipReplaceInsertTags']) {
1✔
99
            $values = array_map([$this->framework->getAdapter(Controller::class), 'replaceInsertTags'], $values);
1✔
100
        }
101

102
        if (empty($columns)) {
1✔
103
            $columns = null;
1✔
104
        }
105

106
        return $adapter->findBy($columns, $values, $options);
1✔
107
    }
108

109
    /**
110
     * Find a single model instance for given table by its primary key (id).
111
     *
112
     * @param string $table The table
113
     * @param int|string $pk The property value
114
     * @param array $options An optional options array
115
     *
116
     * @return Model|null The model or null if the result is empty
117
     */
118
    public function findModelInstanceByPk(string $table, int|string $pk, array $options = []): ?Model
119
    {
120
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
121
            return null;
1✔
122
        }
123

124
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
125
            return null;
×
126
        }
127

128
        /** @var Model|Adapter $adapter */
129
        return $adapter->findByPk($pk, $options);
1✔
130
    }
131

132
    /**
133
     * Return a single model instance by table and search criteria.
134
     *
135
     * Options:
136
     * - skipReplaceInsertTags: Skip the replacement of inserttags. Default: false
137
     *
138
     * @param array{
139
     *     skipReplaceInsertTags?: bool
140
     * } $options
141
     *
142
     */
143
    public function findOneModelInstanceBy(string $table, array $columns, array $values, array $options = []): ?Model
144
    {
145
        $options = array_merge([
1✔
146
            'skipReplaceInsertTags' => false,
1✔
147
        ], $options);
1✔
148

149
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
150
            return null;
1✔
151
        }
152

153

154
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
155
            return null;
×
156
        }
157

158
        if (\is_array($values) && !$options['skipReplaceInsertTags']) {
1✔
159
            $values = array_map([$this->framework->getAdapter(Controller::class), 'replaceInsertTags'], $values);
1✔
160
        }
161

162
        if (empty($columns)) {
1✔
163
            $columns = null;
1✔
164
        }
165

166
        /* @var Model|Adapter $adapter */
167
        return $adapter->findOneBy($columns, $values, $options);
1✔
168
    }
169

170
    /**
171
     * Returns multiple model instances by given table and ids.
172
     */
173
    public function findMultipleModelInstancesByIds(string $table, array $ids, array $options = []): Collection|Model|null
174
    {
175
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
176
            return null;
1✔
177
        }
178

179
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
180
            return null;
×
181
        }
182

183
        /** @var Model|Adapter $adapter */
184
        return $adapter->findBy(["$table.id IN(".implode(',', array_map('\intval', $ids)).')'], null, $options);
1✔
185
    }
186

187
    /**
188
     * Returns model instance by given table and id or alias.
189
     */
190
    public function findModelInstanceByIdOrAlias(string $table, int|string $idOrAlias, array $options = []): ?Model
191
    {
192
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
193
            return null;
1✔
194
        }
195

196
        /* @var Model $adapter */
197
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
198
            return null;
×
199
        }
200

201
        $options = array_merge(
1✔
202
            [
1✔
203
                'limit' => 1,
1✔
204
                'column' => !is_numeric($idOrAlias) ? ["$table.alias=?"] : ["$table.id=?"],
1✔
205
                'value' => $idOrAlias,
1✔
206
                'return' => 'Model',
1✔
207
            ],
1✔
208
            $options
1✔
209
        );
1✔
210

211
        return $adapter->findByIdOrAlias($idOrAlias, $options);
1✔
212
    }
213

214
    /**
215
     * Returns an array of a model instance's parents in ascending order, i.e. the root parent comes first.
216
     *
217
     * @template T of Model
218
     * @param T $instance
219
     * @param string $parentProperty
220
     * @return array<T>
221
     */
222
    public function findParentsRecursively(Model $instance, string $parentProperty = 'pid'): array
223
    {
224
        $table = call_user_func([$instance, 'getTable']);
1✔
225

226
        $parents = [];
1✔
227
        $model = $this->framework->getAdapter(Model::class);
1✔
228
        $modelClass = $model->getClassFromTable($table);
1✔
229

230
        if (!$instance->{$parentProperty}) {
1✔
231
            return $parents;
1✔
232
        }
233

234
        if (null === ($parentInstance = $this->framework->getAdapter($modelClass)->findByPk($instance->{$parentProperty}))) {
1✔
235
            return $parents;
×
236
        }
237

238
        return array_merge($this->findParentsRecursively($parentInstance, $parentProperty), [$parentInstance]);
1✔
239
    }
240
}
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