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

heimrichhannot / contao-utils-bundle / 6629530024

24 Oct 2023 04:06PM UTC coverage: 22.835% (-0.02%) from 22.858%
6629530024

push

github

koertho
add Utils/ModelUtil::findParentsRecursively()

10 of 10 new or added lines in 2 files covered. (100.0%)

1255 of 5496 relevant lines covered (22.83%)

1.55 hits per line

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

79.71
/src/Util/Model/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\Model;
10

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

18
class ModelUtil
19
{
20
    /**
21
     * @var ContaoFrameworkInterface
22
     */
23
    protected $framework;
24

25
    public function __construct(ContaoFramework $contaoFramework)
26
    {
27
        $this->framework = $contaoFramework;
6✔
28
    }
29

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

57
        $t = $table;
1✔
58

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

62
            $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✔
63
        }
64
    }
65

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

84
        /* @var string|null $modelClass */
85
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
86
            return null;
1✔
87
        }
88

89
        /* @var Model $adapter */
90
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
91
            return null;
×
92
        }
93

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

98
        if (empty($columns)) {
1✔
99
            $columns = null;
1✔
100
        }
101

102
        return $adapter->findBy($columns, $values, $options);
1✔
103
    }
104

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

121
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
122
            return null;
×
123
        }
124

125
        return $adapter->findByPk($pk, $options);
1✔
126
    }
127

128
    /**
129
     * Return a single model instance by table and search criteria.
130
     *
131
     * Options:
132
     * - skipReplaceInsertTags: (bool) Skip the replacement of inserttags. Default: false
133
     *
134
     * @return mixed
135
     */
136
    public function findOneModelInstanceBy(string $table, array $columns, array $values, array $options = [])
137
    {
138
        /* @var Model $adapter */
139
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
140
            return null;
1✔
141
        }
142

143
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
144
            return null;
×
145
        }
146

147
        if (\is_array($values) && (!isset($options['skipReplaceInsertTags']) || !$options['skipReplaceInsertTags'])) {
1✔
148
            $values = array_map([$this->framework->getAdapter(Controller::class), 'replaceInsertTags'], $values);
1✔
149
        }
150

151
        if (empty($columns)) {
1✔
152
            $columns = null;
1✔
153
        }
154

155
        return $adapter->findOneBy($columns, $values, $options);
1✔
156
    }
157

158
    /**
159
     * Returns multiple model instances by given table and ids.
160
     *
161
     * @return Collection|Model[]|Model|null
162
     */
163
    public function findMultipleModelInstancesByIds(string $table, array $ids, array $options = [])
164
    {
165
        /* @var Model $adapter */
166
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
167
            return null;
1✔
168
        }
169

170
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
171
            return null;
×
172
        }
173

174
        return $adapter->findBy(["$table.id IN(".implode(',', array_map('\intval', $ids)).')'], null, $options);
1✔
175
    }
176

177
    /**
178
     * Returns multiple model instances by given table and id or alias.
179
     *
180
     * @param mixed $idOrAlias
181
     *
182
     * @return Model|null
183
     */
184
    public function findModelInstanceByIdOrAlias(string $table, $idOrAlias, array $options = [])
185
    {
186
        if (!($modelClass = $this->framework->getAdapter(Model::class)->getClassFromTable($table))) {
1✔
187
            return null;
1✔
188
        }
189

190
        /* @var Model $adapter */
191
        if (null === ($adapter = $this->framework->getAdapter($modelClass))) {
1✔
192
            return null;
×
193
        }
194

195
        $options = array_merge(
1✔
196
            [
1✔
197
                'limit' => 1,
1✔
198
                'column' => !is_numeric($idOrAlias) ? ["$table.alias=?"] : ["$table.id=?"],
1✔
199
                'value' => $idOrAlias,
1✔
200
                'return' => 'Model',
1✔
201
            ],
1✔
202
            $options
1✔
203
        );
1✔
204

205
        return $adapter->findByIdOrAlias($idOrAlias, $options);
1✔
206
    }
207

208
    /**
209
     * Returns an array of a model instance's parents in ascending order, i.e. the root parent comes first.
210
     *
211
     * @template T of Model
212
     * @param T $instance
213
     * @param string $parentProperty
214
     * @return array<T>
215
     */
216
    public function findParentsRecursively(Model $instance, string $parentProperty = 'pid'): array
217
    {
218
        $table = call_user_func([$instance, 'getTable']);
×
219

220
        $parents = [];
×
221
        $model = $this->framework->getAdapter(Model::class);
×
222
        $modelClass = $model->getClassFromTable($table);
×
223

224
        if (!$instance->{$parentProperty}) {
×
225
            return $parents;
×
226
        }
227

228
        if (null === ($parentInstance = $this->framework->getAdapter($modelClass)->findByPk($instance->{$parentProperty}))) {
×
229
            return $parents;
×
230
        }
231

232
        return array_merge($this->findParentsRecursively($parentInstance, $parentProperty), [$parentInstance]);
×
233
    }
234
}
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