ObjectEntity   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 34
eloc 121
dl 0
loc 296
rs 9.68
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getLinks() 0 16 2
A getTable() 0 3 2
A hasProperty() 0 15 5
A getMeta() 0 3 1
A getType() 0 3 1
A loadObjectType() 0 8 5
A isFieldTranslatable() 0 3 1
A addNotTranslatable() 0 6 1
A listAssociations() 0 6 1
A _setType() 0 11 2
A _getType() 0 8 2
A getVisible() 0 11 3
B getRelationships() 0 59 8
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2018 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
14
namespace BEdita\Core\Model\Entity;
15
16
use BEdita\Core\Utility\JsonApiSerializable;
17
use Cake\Datasource\Exception\InvalidPrimaryKeyException;
18
use Cake\Datasource\Exception\RecordNotFoundException;
19
use Cake\ORM\Entity;
20
use Cake\ORM\Table;
21
use Cake\ORM\TableRegistry;
22
use Cake\Routing\Router;
23
24
/**
25
 * Object Entity.
26
 *
27
 * @property int $id
28
 * @property int $object_type_id
29
 * @property bool $deleted
30
 * @property string $type
31
 * @property string $status
32
 * @property string $uname
33
 * @property bool $locked
34
 * @property \Cake\I18n\Time|\Cake\I18n\FrozenTime $created
35
 * @property \Cake\I18n\Time|\Cake\I18n\FrozenTime $modified
36
 * @property \Cake\I18n\Time|\Cake\I18n\FrozenTime $published
37
 * @property string $title
38
 * @property string $description
39
 * @property string $body
40
 * @property array $custom_props
41
 * @property array $extra
42
 * @property string $lang
43
 * @property int $created_by
44
 * @property int $modified_by
45
 * @property \Cake\I18n\Time|\Cake\I18n\FrozenTime $publish_start
46
 * @property \Cake\I18n\Time|\Cake\I18n\FrozenTime $publish_end
47
 *
48
 * @property \BEdita\Core\Model\Entity\ObjectType $object_type
49
 * @property \BEdita\Core\Model\Entity\User $created_by_user
50
 * @property \BEdita\Core\Model\Entity\User $modified_by_user
51
 * @property \BEdita\Core\Model\Entity\DateRange[] $date_ranges
52
 * @property \BEdita\Core\Model\Entity\Folder[] $parents
53
 * @property \BEdita\Core\Model\Entity\Tree[] $tree_nodes
54
 * @property \BEdita\Core\Model\Entity\Translation[] $translations
55
 * @since 4.0.0
56
 */
57
class ObjectEntity extends Entity implements JsonApiSerializable
58
{
59
    use JsonApiTrait {
60
        listAssociations as protected jsonApiListAssociations;
61
        getMeta as protected jsonApiGetMeta;
62
    }
63
64
    /**
65
     * @inheritDoc
66
     */
67
    protected $_accessible = [
68
        '*' => true,
69
        'id' => false,
70
        'object_type_id' => false,
71
        'object_type' => false,
72
        'type' => false,
73
        'deleted' => false,
74
        'locked' => false,
75
        'created' => false,
76
        'modified' => false,
77
        'published' => false,
78
        'created_by' => false,
79
        'modified_by' => false,
80
    ];
81
82
    /**
83
     * @inheritDoc
84
     */
85
    protected $_virtual = [
86
        'type',
87
    ];
88
89
    /**
90
     * @inheritDoc
91
     */
92
    protected $_hidden = [
93
        'created_by_user',
94
        'modified_by_user',
95
        'object_type_id',
96
        'object_type',
97
        'deleted',
98
        'custom_props',
99
        'tree_nodes',
100
    ];
101
102
    /**
103
     * Text properties that are not translatable
104
     *
105
     * @var array
106
     */
107
    protected $notTranslatable = [
108
        'custom_props',
109
        'extra',
110
        'lang',
111
        'status',
112
        'uname',
113
    ];
114
115
    /**
116
     * See if a property has been set in an entity.
117
     * Could be set in `_properties` array or a virtual one.
118
     * Options to exclude hidden properties and to include virtual properties.
119
     *
120
     * @param string $property Property name
121
     * @param bool $hidden Include hidden (default true)
122
     * @param bool $virtual Include virtual (default false)
123
     * @return bool
124
     */
125
    public function hasProperty(string $property, bool $hidden = true, bool $virtual = false)
126
    {
127
        if ($hidden && !$virtual) {
128
            return array_key_exists($property, $this->_properties);
129
        }
130
131
        $properties = array_keys($this->_properties);
132
        if (!$hidden) {
133
            $properties = array_diff($properties, $this->_hidden);
134
        }
135
        if ($virtual) {
136
            $properties = array_merge($properties, $this->_virtual);
137
        }
138
139
        return in_array($property, $properties);
140
    }
141
142
    /**
143
     * @inheritDoc
144
     */
145
    public function getTable()
146
    {
147
        return TableRegistry::getTableLocator()->get($this->type ?: $this->getSource());
148
    }
149
150
    /**
151
     * @inheritDoc
152
     */
153
    protected function getType()
154
    {
155
        return $this->type;
156
    }
157
158
    /**
159
     * @inheritDoc
160
     */
161
    protected function getMeta()
162
    {
163
        return array_diff_key($this->jsonApiGetMeta(), array_flip(['type']));
164
    }
165
166
    /**
167
     * @inheritDoc
168
     */
169
    protected function getLinks()
170
    {
171
        $options = [
172
            '_name' => 'api:objects:resource',
173
            'object_type' => $this->type,
174
            'id' => $this->id,
175
        ];
176
        if ($this->deleted) {
177
            $options = [
178
                '_name' => 'api:trash:resource',
179
                'id' => $this->id,
180
            ];
181
        }
182
183
        return [
184
            'self' => Router::url($options, true),
185
        ];
186
    }
187
188
    /**
189
     * @inheritDoc
190
     */
191
    protected static function listAssociations(Table $Table, array $hidden = [])
192
    {
193
        $associations = static::jsonApiListAssociations($Table, $hidden);
194
        $associations = array_diff($associations, ['date_ranges', 'categories', 'tags']);
195
196
        return $associations;
197
    }
198
199
    /**
200
     * @inheritDoc
201
     */
202
    protected function getRelationships()
203
    {
204
        $relationships = $included = [];
205
        if ($this->deleted) {
206
            return [$relationships, $included];
207
        }
208
209
        $entity = $this;
210
        $table = $this->getTable();
211
        if ($table->getRegistryAlias() !== $this->getSource()) {
212
            $entity = $table->newEntity([]);
213
        }
214
215
        $associations = $entity::listAssociations($table, $entity->getHidden());
0 ignored issues
show
Bug introduced by
The method listAssociations() does not exist on Cake\Datasource\EntityInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Cake\ORM\Entity or BEdita\Core\Model\Entity\AuthProvider or BEdita\Core\Model\Entity\UserToken or BEdita\Core\Model\Entity\ObjectRelation or BEdita\Core\Model\Entity\ObjectPermission or BEdita\Core\Model\Entity\RolesUser or BEdita\Core\Model\Entity\ObjectProperty or DebugKit\Model\Entity\Request or BEdita\Core\Model\Entity\DateRange or DebugKit\Model\Entity\Panel or BEdita\Core\Model\Entity\RelationType or BEdita\Core\Model\Entity\Tree or BEdita\Core\Model\Entity\ExternalAuth. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

215
        /** @scrutinizer ignore-call */ 
216
        $associations = $entity::listAssociations($table, $entity->getHidden());
Loading history...
216
        foreach ($associations as $relationship) {
217
            $self = Router::url(
218
                [
219
                    '_name' => 'api:objects:relationships',
220
                    'object_type' => $this->type,
221
                    'relationship' => $relationship,
222
                    'id' => $this->getId(),
223
                ],
224
                true
225
            );
226
            $related = Router::url(
227
                [
228
                    '_name' => 'api:objects:related',
229
                    'object_type' => $this->type,
230
                    'relationship' => $relationship,
231
                    'related_id' => $this->getId(),
232
                ],
233
                true
234
            );
235
236
            if ($this->has($relationship)) {
237
                $entities = $this->get($relationship);
238
                $data = $this->getIncluded($entities);
239
                if (!is_array($entities)) {
240
                    $entities = [$entities];
241
                }
242
                $included = array_merge($included, $entities);
243
            }
244
245
            $relationships[$relationship] = [
246
                'links' => compact('related', 'self'),
247
            ];
248
            if (isset($data)) {
249
                $relationships[$relationship] += compact('data');
250
                unset($data);
251
            }
252
            $count = $this->getRelationshipCount($relationship);
253
            if ($count !== null) {
254
                $relationships[$relationship] += [
255
                    'meta' => compact('count'),
256
                ];
257
            }
258
        }
259
260
        return [$relationships, $included];
261
    }
262
263
    /**
264
     * @inheritDoc
265
     */
266
    public function getVisible(): array
267
    {
268
        $visible = parent::getVisible();
269
        $this->loadObjectType();
270
        if (!$this->object_type) {
271
            return $visible;
272
        }
273
274
        $hidden = !empty($this->object_type->hidden) ? $this->object_type->hidden : [];
275
276
        return array_diff($visible, $hidden);
277
    }
278
279
    /**
280
     * Load `object_type`, read from object_types table if not set.
281
     *
282
     * @return void
283
     */
284
    protected function loadObjectType()
285
    {
286
        if (!$this->object_type) {
287
            try {
288
                $typeId = $this->object_type_id ?: $this->getSource();
289
                $this->object_type = TableRegistry::getTableLocator()->get('ObjectTypes')->get($typeId);
290
            } catch (RecordNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
291
            } catch (InvalidPrimaryKeyException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
292
            }
293
        }
294
    }
295
296
    /**
297
     * Getter for `type` virtual property.
298
     *
299
     * @return string
300
     */
301
    protected function _getType()
302
    {
303
        $this->loadObjectType();
304
        if (!$this->object_type) {
305
            return null;
306
        }
307
308
        return $this->object_type->name;
309
    }
310
311
    /**
312
     * Setter for `type` virtual property.
313
     *
314
     * @param string $type Object type name.
315
     * @return string
316
     */
317
    protected function _setType($type)
318
    {
319
        try {
320
            $this->object_type = TableRegistry::getTableLocator()->get('ObjectTypes')->get($type);
321
            $this->object_type_id = $this->object_type->id;
322
            $this->setDirty('object_type_id', true);
323
        } catch (RecordNotFoundException $e) {
324
            return null;
325
        }
326
327
        return $type;
328
    }
329
330
    /**
331
     * Adds not-translatable fields on this entity.
332
     *
333
     * @param array<string> $fields An array of not translatable fields.
334
     * @return $this
335
     */
336
    public function addNotTranslatable(array $fields)
337
    {
338
        $fields = array_merge($this->notTranslatable, $fields);
339
        $this->notTranslatable = array_unique($fields);
340
341
        return $this;
342
    }
343
344
    /**
345
     * See if a certain field is translatable by looking at `notTranslatable` internal array
346
     *
347
     * @param string $name Field name
348
     * @return bool
349
     */
350
    public function isFieldTranslatable(string $name): bool
351
    {
352
        return !in_array($name, $this->notTranslatable);
353
    }
354
}
355