Completed
Push — sf_cache ( f6a6ab...bf1241 )
by André
35:58 queued 24:16
created

ContentHandler   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 314
Duplicated Lines 0.96 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 3
loc 314
rs 10
c 0
b 0
f 0
wmc 30
lcom 1
cbo 11

20 Methods

Rating   Name   Duplication   Size   Complexity  
A loadContentInfo() 0 15 2
A loadContentInfoByRemoteId() 0 15 2
A listVersions() 0 6 1
A addRelation() 0 6 1
A removeRelation() 0 5 1
A create() 0 7 1
A createDraftFromVersion() 0 6 1
A copy() 0 6 1
A load() 0 16 3
A loadVersionInfo() 0 15 2
A loadDraftsForUser() 0 6 1
A setStatus() 0 12 2
A updateMetadata() 0 8 1
A updateContent() 0 8 1
B deleteContent() 0 29 2
A deleteVersion() 0 8 1
A loadRelations() 0 13 1
A loadReverseRelations() 0 6 1
A publish() 0 8 1
A getCacheTags() 3 20 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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