Completed
Push — 6.7 ( eacd1f...2a3684 )
by
unknown
25:16
created

DomainMapper::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 5
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the DomainMapper class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Repository\Helper;
10
11
use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandler;
12
use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler;
13
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
14
use eZ\Publish\SPI\Persistence\Content\Type\Handler as TypeHandler;
15
use eZ\Publish\Core\Repository\Values\Content\Content;
16
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
17
use eZ\Publish\Core\Repository\Values\Content\VersionInfo;
18
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
19
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
20
use eZ\Publish\API\Repository\Values\Content\Field;
21
use eZ\Publish\Core\Repository\Values\Content\Relation;
22
use eZ\Publish\API\Repository\Values\Content\Location as APILocation;
23
use eZ\Publish\Core\Repository\Values\Content\Location;
24
use eZ\Publish\SPI\Persistence\Content as SPIContent;
25
use eZ\Publish\SPI\Persistence\Content\Location as SPILocation;
26
use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo;
27
use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo;
28
use eZ\Publish\SPI\Persistence\Content\Relation as SPIRelation;
29
use eZ\Publish\SPI\Persistence\Content\Type as SPIType;
30
use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct as SPILocationCreateStruct;
31
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
32
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
33
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
34
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType;
35
use DateTime;
36
37
/**
38
 * DomainMapper is an internal service.
39
 *
40
 * @internal Meant for internal use by Repository.
41
 */
42
class DomainMapper
43
{
44
    /**
45
     * @var \eZ\Publish\SPI\Persistence\Content\Handler
46
     */
47
    protected $contentHandler;
48
49
    /**
50
     * @var \eZ\Publish\SPI\Persistence\Content\Location\Handler
51
     */
52
    protected $locationHandler;
53
54
    /**
55
     * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler
56
     */
57
    protected $contentTypeHandler;
58
59
    /**
60
     * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler
61
     */
62
    protected $contentLanguageHandler;
63
64
    /**
65
     * @var FieldTypeRegistry
66
     */
67
    protected $fieldTypeRegistry;
68
69
    /**
70
     * Setups service with reference to repository.
71
     *
72
     * @param \eZ\Publish\SPI\Persistence\Content\Handler $contentHandler
73
     * @param \eZ\Publish\SPI\Persistence\Content\Location\Handler $locationHandler
74
     * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler
75
     * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $contentLanguageHandler
76
     * @param FieldTypeRegistry $fieldTypeRegistry
77
     */
78
    public function __construct(
79
        ContentHandler $contentHandler,
80
        LocationHandler $locationHandler,
81
        TypeHandler $contentTypeHandler,
82
        LanguageHandler $contentLanguageHandler,
83
        FieldTypeRegistry $fieldTypeRegistry
84
    ) {
85
        $this->contentHandler = $contentHandler;
86
        $this->locationHandler = $locationHandler;
87
        $this->contentTypeHandler = $contentTypeHandler;
88
        $this->contentLanguageHandler = $contentLanguageHandler;
89
        $this->fieldTypeRegistry = $fieldTypeRegistry;
90
    }
91
92
    /**
93
     * Builds a Content domain object from value object returned from persistence.
94
     *
95
     * @param \eZ\Publish\SPI\Persistence\Content $spiContent
96
     * @param ContentType|SPIType $contentType
97
     * @param array|null $fieldLanguages Language codes to filter fields on
98
     * @param string|null $fieldAlwaysAvailableLanguage Language code fallback if a given field is not found in $fieldLanguages
99
     *
100
     * @return \eZ\Publish\Core\Repository\Values\Content\Content
101
     */
102
    public function buildContentDomainObject(SPIContent $spiContent, $contentType = null, array $fieldLanguages = null, $fieldAlwaysAvailableLanguage = null)
103
    {
104
        if ($contentType === null) {
105
            $contentType = $this->contentTypeHandler->load(
106
                $spiContent->versionInfo->contentInfo->contentTypeId
107
            );
108
        }
109
110
        return new Content(
111
            array(
112
                'internalFields' => $this->buildDomainFields($spiContent->fields, $contentType, $fieldLanguages, $fieldAlwaysAvailableLanguage),
113
                'versionInfo' => $this->buildVersionInfoDomainObject($spiContent->versionInfo),
114
            )
115
        );
116
    }
117
118
    /**
119
     * Returns an array of domain fields created from given array of SPI fields.
120
     *
121
     * @throws InvalidArgumentType On invalid $contentType
122
     *
123
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
124
     * @param ContentType|SPIType $contentType
125
     * @param array|null $languages Language codes to filter fields on
126
     * @param string|null $alwaysAvailableLanguage Language code fallback if a given field is not found in $languages
127
     *
128
     * @return array
129
     */
130
    public function buildDomainFields(
131
        array $spiFields,
132
        $contentType,
133
        array $languages = null,
134
        $alwaysAvailableLanguage = null
135
    ) {
136
        if (!$contentType instanceof SPIType && !$contentType instanceof ContentType) {
137
            throw new InvalidArgumentType('$contentType', 'SPI ContentType | API ContentType');
138
        }
139
140
        $fieldIdentifierMap = array();
141
        foreach ($contentType->fieldDefinitions as $fieldDefinitions) {
142
            $fieldIdentifierMap[$fieldDefinitions->id] = $fieldDefinitions->identifier;
143
        }
144
145
        $fieldInFilterLanguagesMap = array();
146
        if (!empty($languages) && $alwaysAvailableLanguage !== null) {
147
            foreach ($spiFields as $spiField) {
148
                if (in_array($spiField->languageCode, $languages)) {
149
                    $fieldInFilterLanguagesMap[$spiField->fieldDefinitionId] = true;
150
                }
151
            }
152
        }
153
154
        $fields = array();
155
        foreach ($spiFields as $spiField) {
156
            // We ignore fields in content not part of the content type
157
            if (!isset($fieldIdentifierMap[$spiField->fieldDefinitionId])) {
158
                continue;
159
            }
160
161
            if (!empty($languages) && !in_array($spiField->languageCode, $languages)) {
162
                // If filtering is enabled we ignore fields in other languages then $fieldLanguages, if:
163
                if ($alwaysAvailableLanguage === null) {
164
                    // Ignore field if we don't have $alwaysAvailableLanguageCode fallback
165
                    continue;
166
                } elseif (!empty($fieldInFilterLanguagesMap[$spiField->fieldDefinitionId])) {
167
                    // Ignore field if it exists in one of the filtered languages
168
                    continue;
169
                } elseif ($spiField->languageCode !== $alwaysAvailableLanguage) {
170
                    // Also ignore if field is not in $alwaysAvailableLanguageCode
171
                    continue;
172
                }
173
            }
174
175
            $fields[] = new Field(
176
                array(
177
                    'id' => $spiField->id,
178
                    'value' => $this->fieldTypeRegistry->getFieldType($spiField->type)
179
                        ->fromPersistenceValue($spiField->value),
180
                    'languageCode' => $spiField->languageCode,
181
                    'fieldDefIdentifier' => $fieldIdentifierMap[$spiField->fieldDefinitionId],
182
                )
183
            );
184
        }
185
186
        return $fields;
187
    }
188
189
    /**
190
     * Builds a VersionInfo domain object from value object returned from persistence.
191
     *
192
     * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $spiVersionInfo
193
     *
194
     * @return \eZ\Publish\Core\Repository\Values\Content\VersionInfo
195
     */
196
    public function buildVersionInfoDomainObject(SPIVersionInfo $spiVersionInfo)
197
    {
198
        $languageCodes = array();
199
        foreach ($spiVersionInfo->languageIds as $languageId) {
200
            $languageCodes[] = $this->contentLanguageHandler->load($languageId)->languageCode;
201
        }
202
203
        // Map SPI statuses to API
204
        switch ($spiVersionInfo->status) {
205
            case SPIVersionInfo::STATUS_ARCHIVED:
206
                $status = APIVersionInfo::STATUS_ARCHIVED;
207
                break;
208
209
            case SPIVersionInfo::STATUS_PUBLISHED:
210
                $status = APIVersionInfo::STATUS_PUBLISHED;
211
                break;
212
213
            case SPIVersionInfo::STATUS_DRAFT:
214
            default:
215
                $status = APIVersionInfo::STATUS_DRAFT;
216
        }
217
218
        return new VersionInfo(
219
            array(
220
                'id' => $spiVersionInfo->id,
221
                'versionNo' => $spiVersionInfo->versionNo,
222
                'modificationDate' => $this->getDateTime($spiVersionInfo->modificationDate),
223
                'creatorId' => $spiVersionInfo->creatorId,
224
                'creationDate' => $this->getDateTime($spiVersionInfo->creationDate),
225
                'status' => $status,
226
                'initialLanguageCode' => $spiVersionInfo->initialLanguageCode,
227
                'languageCodes' => $languageCodes,
228
                'names' => $spiVersionInfo->names,
229
                'contentInfo' => $this->buildContentInfoDomainObject($spiVersionInfo->contentInfo),
230
            )
231
        );
232
    }
233
234
    /**
235
     * Builds a ContentInfo domain object from value object returned from persistence.
236
     *
237
     * @param \eZ\Publish\SPI\Persistence\Content\ContentInfo $spiContentInfo
238
     *
239
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
240
     */
241
    public function buildContentInfoDomainObject(SPIContentInfo $spiContentInfo)
242
    {
243
        // Map SPI statuses to API
244
        switch ($spiContentInfo->status) {
245
            case SPIContentInfo::STATUS_TRASHED:
246
                $status = ContentInfo::STATUS_TRASHED;
247
                break;
248
249
            case SPIContentInfo::STATUS_PUBLISHED:
250
                $status = ContentInfo::STATUS_PUBLISHED;
251
                break;
252
253
            case SPIContentInfo::STATUS_DRAFT:
254
            default:
255
                $status = ContentInfo::STATUS_DRAFT;
256
        }
257
258
        return new ContentInfo(
259
            array(
260
                'id' => $spiContentInfo->id,
261
                'contentTypeId' => $spiContentInfo->contentTypeId,
262
                'name' => $spiContentInfo->name,
263
                'sectionId' => $spiContentInfo->sectionId,
264
                'currentVersionNo' => $spiContentInfo->currentVersionNo,
265
                'published' => $spiContentInfo->isPublished,
0 ignored issues
show
Deprecated Code introduced by
The property eZ\Publish\SPI\Persisten...ntentInfo::$isPublished has been deprecated with message: Use SPI\ContentInfo::$status (with value ContentInfo::STATUS_PUBLISHED) Flag indicating if content is currently published.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
266
                'ownerId' => $spiContentInfo->ownerId,
267
                'modificationDate' => $spiContentInfo->modificationDate == 0 ?
268
                    null :
269
                    $this->getDateTime($spiContentInfo->modificationDate),
270
                'publishedDate' => $spiContentInfo->publicationDate == 0 ?
271
                    null :
272
                    $this->getDateTime($spiContentInfo->publicationDate),
273
                'alwaysAvailable' => $spiContentInfo->alwaysAvailable,
274
                'remoteId' => $spiContentInfo->remoteId,
275
                'mainLanguageCode' => $spiContentInfo->mainLanguageCode,
276
                'mainLocationId' => $spiContentInfo->mainLocationId,
277
                'status' => $status,
278
            )
279
        );
280
    }
281
282
    /**
283
     * Builds API Relation object from provided SPI Relation object.
284
     *
285
     * @param \eZ\Publish\SPI\Persistence\Content\Relation $spiRelation
286
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $sourceContentInfo
287
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContentInfo
288
     *
289
     * @return \eZ\Publish\API\Repository\Values\Content\Relation
290
     */
291
    public function buildRelationDomainObject(
292
        SPIRelation $spiRelation,
293
        ContentInfo $sourceContentInfo,
294
        ContentInfo $destinationContentInfo
295
    ) {
296
        $sourceFieldDefinitionIdentifier = null;
297
        if ($spiRelation->sourceFieldDefinitionId !== null) {
298
            $contentType = $this->contentTypeHandler->load($sourceContentInfo->contentTypeId);
299
            foreach ($contentType->fieldDefinitions as $fieldDefinition) {
300
                if ($fieldDefinition->id !== $spiRelation->sourceFieldDefinitionId) {
301
                    continue;
302
                }
303
304
                $sourceFieldDefinitionIdentifier = $fieldDefinition->identifier;
305
                break;
306
            }
307
        }
308
309
        return new Relation(
310
            array(
311
                'id' => $spiRelation->id,
312
                'sourceFieldDefinitionIdentifier' => $sourceFieldDefinitionIdentifier,
313
                'type' => $spiRelation->type,
314
                'sourceContentInfo' => $sourceContentInfo,
315
                'destinationContentInfo' => $destinationContentInfo,
316
            )
317
        );
318
    }
319
320
    /**
321
     * Builds domain location object from provided persistence location.
322
     *
323
     * @param \eZ\Publish\SPI\Persistence\Content\Location $spiLocation
324
     * @param \eZ\Publish\SPI\Persistence\Content\ContentInfo $spiContentInfo pre-loaded Content Info
325
     *
326
     * @return \eZ\Publish\API\Repository\Values\Content\Location
327
     *
328
     * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException In case if the given Content does not belong to the given Location
329
     */
330
    public function buildLocationDomainObject(SPILocation $spiLocation, SPIContentInfo $spiContentInfo = null)
331
    {
332
        // TODO: this is hardcoded workaround for missing ContentInfo on root location
333
        if ($spiLocation->id == 1) {
334
            $legacyDateTime = $this->getDateTime(1030968000); //  first known commit of eZ Publish 3.x
335
            $contentInfo = new ContentInfo(
336
                array(
337
                    'id' => 0,
338
                    'name' => 'Top Level Nodes',
339
                    'sectionId' => 1,
340
                    'mainLocationId' => 1,
341
                    'contentTypeId' => 1,
342
                    'currentVersionNo' => 1,
343
                    'published' => 1,
344
                    'ownerId' => 14, // admin user
345
                    'modificationDate' => $legacyDateTime,
346
                    'publishedDate' => $legacyDateTime,
347
                    'alwaysAvailable' => 1,
348
                    'remoteId' => null,
349
                    'mainLanguageCode' => 'eng-GB',
350
                )
351
            );
352
        } elseif (null !== $spiContentInfo) {
353
            if ($spiLocation->contentId !== $spiContentInfo->id) {
354
                throw new InvalidArgumentException(
355
                    '$spiContentInfo',
356
                    sprintf(
357
                        'Content Id %d does not belong to the Location %d',
358
                        $spiContentInfo->id,
359
                        $spiLocation->id
360
                    )
361
                );
362
            }
363
364
            $contentInfo = $this->buildContentInfoDomainObject($spiContentInfo);
365
        } else {
366
            $contentInfo = $this->buildContentInfoDomainObject(
367
                $this->contentHandler->loadContentInfo($spiLocation->contentId)
368
            );
369
        }
370
371
        return new Location(
372
            array(
373
                'contentInfo' => $contentInfo,
374
                'id' => $spiLocation->id,
375
                'priority' => $spiLocation->priority,
376
                'hidden' => $spiLocation->hidden,
377
                'invisible' => $spiLocation->invisible,
378
                'remoteId' => $spiLocation->remoteId,
379
                'parentLocationId' => $spiLocation->parentId,
380
                'pathString' => $spiLocation->pathString,
381
                'depth' => $spiLocation->depth,
382
                'sortField' => $spiLocation->sortField,
383
                'sortOrder' => $spiLocation->sortOrder,
384
            )
385
        );
386
    }
387
388
    /**
389
     * Creates an array of SPI location create structs from given array of API location create structs.
390
     *
391
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
392
     *
393
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $locationCreateStruct
394
     * @param \eZ\Publish\API\Repository\Values\Content\Location $parentLocation
395
     * @param mixed $mainLocation
396
     * @param mixed $contentId
397
     * @param mixed $contentVersionNo
398
     *
399
     * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct
400
     */
401
    public function buildSPILocationCreateStruct(
402
        $locationCreateStruct,
403
        APILocation $parentLocation,
404
        $mainLocation,
405
        $contentId,
406
        $contentVersionNo
407
    ) {
408
        if ($locationCreateStruct->priority !== null && !is_int($locationCreateStruct->priority)) {
409
            throw new InvalidArgumentValue('priority', $locationCreateStruct->priority, 'LocationCreateStruct');
410
        }
411
412
        if (!is_bool($locationCreateStruct->hidden)) {
413
            throw new InvalidArgumentValue('hidden', $locationCreateStruct->hidden, 'LocationCreateStruct');
414
        }
415
416
        if ($locationCreateStruct->remoteId !== null && (!is_string($locationCreateStruct->remoteId) || empty($locationCreateStruct->remoteId))) {
417
            throw new InvalidArgumentValue('remoteId', $locationCreateStruct->remoteId, 'LocationCreateStruct');
418
        }
419
420
        if ($locationCreateStruct->sortField !== null && !$this->isValidLocationSortField($locationCreateStruct->sortField)) {
421
            throw new InvalidArgumentValue('sortField', $locationCreateStruct->sortField, 'LocationCreateStruct');
422
        }
423
424
        if ($locationCreateStruct->sortOrder !== null && !$this->isValidLocationSortOrder($locationCreateStruct->sortOrder)) {
425
            throw new InvalidArgumentValue('sortOrder', $locationCreateStruct->sortOrder, 'LocationCreateStruct');
426
        }
427
428
        $remoteId = $locationCreateStruct->remoteId;
429
        if (null === $remoteId) {
430
            $remoteId = $this->getUniqueHash($locationCreateStruct);
431
        } else {
432
            try {
433
                $this->locationHandler->loadByRemoteId($remoteId);
434
                throw new InvalidArgumentException(
435
                    '$locationCreateStructs',
436
                    "Another Location with remoteId '{$remoteId}' exists"
437
                );
438
            } catch (NotFoundException $e) {
439
                // Do nothing
440
            }
441
        }
442
443
        return new SPILocationCreateStruct(
444
            array(
445
                'priority' => $locationCreateStruct->priority,
446
                'hidden' => $locationCreateStruct->hidden,
447
                // If we declare the new Location as hidden, it is automatically invisible
448
                // Otherwise it picks up visibility from parent Location
449
                // Note: There is no need to check for hidden status of parent, as hidden Location
450
                // is always invisible as well
451
                'invisible' => ($locationCreateStruct->hidden === true || $parentLocation->invisible),
452
                'remoteId' => $remoteId,
453
                'contentId' => $contentId,
454
                'contentVersion' => $contentVersionNo,
455
                // pathIdentificationString will be set in storage
456
                'pathIdentificationString' => null,
457
                'mainLocationId' => $mainLocation,
458
                'sortField' => $locationCreateStruct->sortField !== null ? $locationCreateStruct->sortField : Location::SORT_FIELD_NAME,
459
                'sortOrder' => $locationCreateStruct->sortOrder !== null ? $locationCreateStruct->sortOrder : Location::SORT_ORDER_ASC,
460
                'parentId' => $locationCreateStruct->parentLocationId,
461
            )
462
        );
463
    }
464
465
    /**
466
     * Checks if given $sortField value is one of the defined sort field constants.
467
     *
468
     * @param mixed $sortField
469
     *
470
     * @return bool
471
     */
472
    public function isValidLocationSortField($sortField)
473
    {
474
        switch ($sortField) {
475
            case APILocation::SORT_FIELD_PATH:
476
            case APILocation::SORT_FIELD_PUBLISHED:
477
            case APILocation::SORT_FIELD_MODIFIED:
478
            case APILocation::SORT_FIELD_SECTION:
479
            case APILocation::SORT_FIELD_DEPTH:
480
            case APILocation::SORT_FIELD_CLASS_IDENTIFIER:
481
            case APILocation::SORT_FIELD_CLASS_NAME:
482
            case APILocation::SORT_FIELD_PRIORITY:
483
            case APILocation::SORT_FIELD_NAME:
484
            case APILocation::SORT_FIELD_MODIFIED_SUBNODE:
0 ignored issues
show
Deprecated Code introduced by
The constant eZ\Publish\API\Repositor..._FIELD_MODIFIED_SUBNODE has been deprecated.

This class constant has been deprecated.

Loading history...
485
            case APILocation::SORT_FIELD_NODE_ID:
486
            case APILocation::SORT_FIELD_CONTENTOBJECT_ID:
487
                return true;
488
        }
489
490
        return false;
491
    }
492
493
    /**
494
     * Checks if given $sortOrder value is one of the defined sort order constants.
495
     *
496
     * @param mixed $sortOrder
497
     *
498
     * @return bool
499
     */
500
    public function isValidLocationSortOrder($sortOrder)
501
    {
502
        switch ($sortOrder) {
503
            case APILocation::SORT_ORDER_DESC:
504
            case APILocation::SORT_ORDER_ASC:
505
                return true;
506
        }
507
508
        return false;
509
    }
510
511
    /**
512
     * Validates given translated list $list, which should be an array of strings with language codes as keys.
513
     *
514
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
515
     *
516
     * @param mixed $list
517
     * @param string $argumentName
518
     */
519
    public function validateTranslatedList($list, $argumentName)
520
    {
521
        if (!is_array($list)) {
522
            throw new InvalidArgumentType($argumentName, 'array', $list);
523
        }
524
525
        foreach ($list as $languageCode => $translation) {
526
            $this->contentLanguageHandler->loadByLanguageCode($languageCode);
527
528
            if (!is_string($translation)) {
529
                throw new InvalidArgumentType($argumentName . "['$languageCode']", 'string', $translation);
530
            }
531
        }
532
    }
533
534
    /**
535
     * Returns \DateTime object from given $timestamp in environment timezone.
536
     *
537
     * This method is needed because constructing \DateTime with $timestamp will
538
     * return the object in UTC timezone.
539
     *
540
     * @param int $timestamp
541
     *
542
     * @return \DateTime
543
     */
544
    public function getDateTime($timestamp)
545
    {
546
        $dateTime = new DateTime();
547
        $dateTime->setTimestamp($timestamp);
548
549
        return $dateTime;
550
    }
551
552
    /**
553
     * Creates unique hash string for given $object.
554
     *
555
     * Used for remoteId.
556
     *
557
     * @param object $object
558
     *
559
     * @return string
560
     */
561
    public function getUniqueHash($object)
562
    {
563
        return md5(uniqid(get_class($object), true));
564
    }
565
}
566