Completed
Push — sf_cache ( ff1daa...dd5469 )
by André
15:49
created

ContentHandler::loadContentInfoByRemoteId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the ContentHandler implementation.
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\Persistence\Cache;
10
11
use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation;
12
use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandlerInterface;
13
use eZ\Publish\SPI\Persistence\Content;
14
use eZ\Publish\SPI\Persistence\Content\VersionInfo;
15
use eZ\Publish\SPI\Persistence\Content\ContentInfo;
16
use eZ\Publish\SPI\Persistence\Content\CreateStruct;
17
use eZ\Publish\SPI\Persistence\Content\UpdateStruct;
18
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct;
19
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct;
20
21
/**
22
 * @see \eZ\Publish\SPI\Persistence\Content\Handler
23
 */
24
class ContentHandler extends AbstractHandler implements ContentHandlerInterface
25
{
26
    const ALL_TRANSLATIONS_KEY = '0';
27
28
    /**
29
     * {@inheritdoc}
30
     */
31
    public function create(CreateStruct $struct)
32
    {
33
        // Cached on demand when published or loaded
34
        $this->logger->logCall(__METHOD__, array('struct' => $struct));
35
36
        return $this->persistenceHandler->contentHandler()->create($struct);
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function createDraftFromVersion($contentId, $srcVersion, $userId)
43
    {
44
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'version' => $srcVersion, 'user' => $userId));
45
46
        return $this->persistenceHandler->contentHandler()->createDraftFromVersion($contentId, $srcVersion, $userId);
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function copy($contentId, $versionNo = null)
53
    {
54
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'version' => $versionNo));
55
56
        return $this->persistenceHandler->contentHandler()->copy($contentId, $versionNo);
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function load($contentId, $versionNo, array $translations = null)
63
    {
64
        $translationsKey = empty($translations) ? self::ALL_TRANSLATIONS_KEY : implode('|', $translations);
65
        $cacheItem = $this->cache->getItem("ez-content-${contentId}-${versionNo}-${translationsKey}");
66
        if ($cacheItem->isHit()) {
67
            return $cacheItem->get();
68
        }
69
70
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'version' => $versionNo, 'translations' => $translations));
71
        $content = $this->persistenceHandler->contentHandler()->load($contentId, $versionNo, $translations);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 62 can also be of type array; however, eZ\Publish\SPI\Persistence\Content\Handler::load() does only seem to accept null|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
72
        $cacheItem->set($content);
73
        $cacheItem->tag($this->getCacheTags($content->versionInfo->contentInfo, true));
74
        $this->cache->save($cacheItem);
75
76
        return $content;
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function loadContentInfo($contentId)
83
    {
84
        $cacheItem = $this->cache->getItem("ez-content-info-${contentId}");
85
        if ($cacheItem->isHit()) {
86
            return $cacheItem->get();
87
        }
88
89
        $this->logger->logCall(__METHOD__, array('content' => $contentId));
90
        $contentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($contentId);
91
        $cacheItem->set($contentInfo);
92
        $cacheItem->tag($this->getCacheTags($contentInfo));
93
        $this->cache->save($cacheItem);
94
95
        return $contentInfo;
96
    }
97
98
    /**
99
     * Return list of unique Content Info, with content id as key.
100
     *
101
     * @param array $contentIds
102
     *
103
     * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo[]
104
     */
105 View Code Duplication
    public function loadContentInfoList(array $contentIds)
106
    {
107
        list($cacheMissIds, $list) = $this->getMultipleCacheItems($contentIds, 'ez-content-info-');
108
        if (empty($cacheMissIds)) {
109
            return $list;
110
        }
111
112
        // Load cache misses
113
        $this->logger->logCall(__METHOD__, array('content' => $cacheMissIds));
114
        $cacheMissList = $this->persistenceHandler->contentHandler()->loadContentInfoList($cacheMissIds);
115
116
        // Populate cache misses with data and set final info data instead on list
117
        foreach ($cacheMissList as $id => $contentInfo) {
118
            $this->cache->save(
119
                $list[$id]
120
                    ->set($contentInfo)
121
                    ->tag($this->getCacheTags($contentInfo))
122
            );
123
            $list[$id] = $contentInfo;
124
        }
125
126
        return $list;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function loadContentInfoByRemoteId($remoteId)
133
    {
134
        $cacheItem = $this->cache->getItem("ez-content-info-byRemoteId-${remoteId}");
135
        if ($cacheItem->isHit()) {
136
            return $cacheItem->get();
137
        }
138
139
        $this->logger->logCall(__METHOD__, array('content' => $remoteId));
140
        $contentInfo = $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($remoteId);
141
        $cacheItem->set($contentInfo);
142
        $cacheItem->tag($this->getCacheTags($contentInfo));
143
        $this->cache->save($cacheItem);
144
145
        return $contentInfo;
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function loadVersionInfo($contentId, $versionNo)
152
    {
153
        $cacheItem = $this->cache->getItem("ez-content-version-info-${contentId}-${versionNo}");
154
        if ($cacheItem->isHit()) {
155
            return $cacheItem->get();
156
        }
157
158
        $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $versionNo]);
159
        $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo($contentId, $versionNo);
160
        $cacheItem->set($versionInfo);
161
        $cacheItem->tag($this->getCacheTags($versionInfo->contentInfo));
162
        $this->cache->save($cacheItem);
163
164
        return $versionInfo;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     *
170
     * @todo With a user-drafts-<user-id> tag we can identify operations that will need to clear it to introduce cache
171
     *       here, as long as those have access to user id.
172
     */
173
    public function loadDraftsForUser($userId)
174
    {
175
        $this->logger->logCall(__METHOD__, array('user' => $userId));
176
177
        return $this->persistenceHandler->contentHandler()->loadDraftsForUser($userId);
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function setStatus($contentId, $status, $versionNo)
184
    {
185
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'status' => $status, 'version' => $versionNo));
186
        $return = $this->persistenceHandler->contentHandler()->setStatus($contentId, $status, $versionNo);
187
188
        $this->cache->deleteItem("ez-content-version-info-${contentId}-${versionNo}");
189
        if ($status === VersionInfo::STATUS_PUBLISHED) {
190
            $this->cache->invalidateTags(['content-'.$contentId]);
191
        }
192
193
        return $return;
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199
    public function updateMetadata($contentId, MetadataUpdateStruct $struct)
200
    {
201
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'struct' => $struct));
202
        $contentInfo =  $this->persistenceHandler->contentHandler()->updateMetadata($contentId, $struct);
203
        $this->cache->invalidateTags(['content-'.$contentId]);
204
205
        return $contentInfo;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function updateContent($contentId, $versionNo, UpdateStruct $struct)
212
    {
213
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'version' => $versionNo, 'struct' => $struct));
214
        $content = $this->persistenceHandler->contentHandler()->updateContent($contentId, $versionNo, $struct);
215
        $this->cache->invalidateTags(['content-'.$contentId]);
216
217
        return $content;
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223
    public function deleteContent($contentId)
224
    {
225
        $this->logger->logCall(__METHOD__, array('content' => $contentId));
226
227
        // Load reverse field relations first
228
        $reverseRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations(
229
            $contentId,
230
            APIRelation::FIELD
231
        );
232
233
        $return = $this->persistenceHandler->contentHandler()->deleteContent($contentId);
234
235
        $this->cache->invalidateTags(['content-'.$contentId]);
236
        if (!empty($reverseRelations)) {
237
            $this->cache->invalidateTags(
238
                array_map(
239
                    function($relation){
240
                        // only the full content object *with* fields is affected by this
241
                        return 'content-fields-'.$relation->sourceContentId;
242
                    },
243
                    $reverseRelations
244
                )
245
            );
246
        }
247
248
249
250
        return $return;
251
    }
252
253
    /**
254
     * {@inheritdoc}
255
     */
256
    public function deleteVersion($contentId, $versionNo)
257
    {
258
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'version' => $versionNo));
259
        $return = $this->persistenceHandler->contentHandler()->deleteVersion($contentId, $versionNo);
260
        $this->cache->invalidateTags(['content-'.$contentId]);
261
262
        return $return;
263
    }
264
265
    /**
266
     * {@inheritdoc}
267
     *
268
     * @todo Could cache this now by identifying which operations affect it and needed tag, eg content-<id>-versions
269
     */
270
    public function listVersions($contentId, $status = null, $limit = -1)
271
    {
272
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'status' => $status));
273
274
        return $this->persistenceHandler->contentHandler()->listVersions($contentId, $status, $limit);
275
    }
276
277
    /**
278
     * {@inheritdoc}
279
     */
280
    public function addRelation(RelationCreateStruct $relation)
281
    {
282
        $this->logger->logCall(__METHOD__, array('struct' => $relation));
283
284
        return $this->persistenceHandler->contentHandler()->addRelation($relation);
285
    }
286
287
    /**
288
     * {@inheritdoc}
289
     */
290
    public function removeRelation($relationId, $type)
291
    {
292
        $this->logger->logCall(__METHOD__, array('relation' => $relationId, 'type' => $type));
293
        $this->persistenceHandler->contentHandler()->removeRelation($relationId, $type);
294
    }
295
296
    /**
297
     * {@inheritdoc}
298
     *
299
     * @todo Could cache this now by identifying which operations affect it and needed tag, eg content-<id>-relations
300
     */
301
    public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null)
302
    {
303
        $this->logger->logCall(
304
            __METHOD__,
305
            array(
306
                'content' => $sourceContentId,
307
                'version' => $sourceContentVersionNo,
308
                'type' => $type,
309
            )
310
        );
311
312
        return $this->persistenceHandler->contentHandler()->loadRelations($sourceContentId, $sourceContentVersionNo, $type);
313
    }
314
315
    /**
316
     * {@inheritdoc}
317
     *
318
     * @todo Could cache this now by identifying which operations affect it and needed tag, eg content-<id>-reverse-relations
319
     */
320
    public function loadReverseRelations($destinationContentId, $type = null)
321
    {
322
        $this->logger->logCall(__METHOD__, array('content' => $destinationContentId, 'type' => $type));
323
324
        return $this->persistenceHandler->contentHandler()->loadReverseRelations($destinationContentId, $type);
325
    }
326
327
    /**
328
     * {@inheritdoc}
329
     */
330
    public function publish($contentId, $versionNo, MetadataUpdateStruct $struct)
331
    {
332
        $this->logger->logCall(__METHOD__, array('content' => $contentId, 'version' => $versionNo, 'struct' => $struct));
333
        $content = $this->persistenceHandler->contentHandler()->publish($contentId, $versionNo, $struct);
334
        $this->cache->invalidateTags(['content-'.$contentId]);
335
336
        return $content;
337
    }
338
339
    /**
340
     * Return relevant content and location tags so cache can be purged reliably.
341
     *
342
     * @param ContentInfo $contentInfo
343
     * @param bool $withFields Set to true if item contains fields which should be expired on relation or type updates.
344
     * @param array $tags Optional, can be used to specify other tags.
345
     *
346
     * @return array
347
     */
348
    private function getCacheTags(ContentInfo $contentInfo, $withFields = false, array $tags = [])
349
    {
350
        $tags[] = 'content-'.$contentInfo->id;
351
352
        if ($withFields) {
353
            $tags[] = 'content-fields-'.$contentInfo->id;
354
            $tags[] = 'content-fields-type-'.$contentInfo->contentTypeId;
355
        }
356
357
        if ($contentInfo->mainLocationId) {
358
            $tags[] = 'location-'.$contentInfo->mainLocationId;
359
360
            $location = $this->persistenceHandler->locationHandler()->load($contentInfo->mainLocationId);
361 View Code Duplication
            foreach (explode('/', trim($location->pathString, '/')) as $pathId) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
362
                $tags[] = 'location-path-'.$pathId;
363
            }
364
        }
365
366
        return $tags;
367
    }
368
}
369