|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of Transfer. |
|
5
|
|
|
* |
|
6
|
|
|
* For the full copyright and license information, please view the LICENSE file located |
|
7
|
|
|
* in the root directory. |
|
8
|
|
|
*/ |
|
9
|
|
|
|
|
10
|
|
|
namespace Transfer\EzPlatform\Repository\Manager; |
|
11
|
|
|
|
|
12
|
|
|
use eZ\Publish\API\Repository\ContentService; |
|
13
|
|
|
use eZ\Publish\API\Repository\ContentTypeService; |
|
14
|
|
|
use eZ\Publish\API\Repository\Exceptions\NotFoundException; |
|
15
|
|
|
use eZ\Publish\API\Repository\LocationService; |
|
16
|
|
|
use eZ\Publish\API\Repository\Repository; |
|
17
|
|
|
use eZ\Publish\API\Repository\Values\Content\Content; |
|
18
|
|
|
use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; |
|
19
|
|
|
use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; |
|
20
|
|
|
use eZ\Publish\API\Repository\Values\Content\Location; |
|
21
|
|
|
use Psr\Log\LoggerAwareInterface; |
|
22
|
|
|
use Psr\Log\LoggerInterface; |
|
23
|
|
|
use Transfer\Data\ObjectInterface; |
|
24
|
|
|
use Transfer\Data\ValueObject; |
|
25
|
|
|
use Transfer\EzPlatform\Repository\Values\ContentObject; |
|
26
|
|
|
use Transfer\EzPlatform\Repository\Values\LocationObject; |
|
27
|
|
|
use Transfer\EzPlatform\Exception\MissingIdentificationPropertyException; |
|
28
|
|
|
use Transfer\EzPlatform\Exception\UnsupportedObjectOperationException; |
|
29
|
|
|
use Transfer\EzPlatform\Repository\Manager\Type\CreatorInterface; |
|
30
|
|
|
use Transfer\EzPlatform\Repository\Manager\Type\FinderInterface; |
|
31
|
|
|
use Transfer\EzPlatform\Repository\Manager\Type\RemoverInterface; |
|
32
|
|
|
use Transfer\EzPlatform\Repository\Manager\Type\UpdaterInterface; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Content manager. |
|
36
|
|
|
* |
|
37
|
|
|
* @internal |
|
38
|
|
|
*/ |
|
39
|
|
|
class ContentManager implements LoggerAwareInterface, CreatorInterface, UpdaterInterface, RemoverInterface, FinderInterface |
|
40
|
|
|
{ |
|
41
|
|
|
/** |
|
42
|
|
|
* @var LocationManager |
|
43
|
|
|
*/ |
|
44
|
|
|
private $locationManager; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @var ContentService |
|
48
|
|
|
*/ |
|
49
|
|
|
protected $contentService; |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* @var ContentTypeService |
|
53
|
|
|
*/ |
|
54
|
|
|
protected $contentTypeService; |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* @var LocationService |
|
58
|
|
|
*/ |
|
59
|
|
|
protected $locationService; |
|
60
|
|
|
|
|
61
|
|
|
/** |
|
62
|
|
|
* @var LoggerInterface |
|
63
|
|
|
*/ |
|
64
|
|
|
protected $logger; |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* @param Repository $repository |
|
68
|
|
|
* @param LocationManager $locationManager |
|
69
|
|
|
*/ |
|
70
|
9 |
|
public function __construct(Repository $repository, LocationManager $locationManager) |
|
71
|
|
|
{ |
|
72
|
9 |
|
$this->locationManager = $locationManager; |
|
73
|
9 |
|
$this->contentService = $repository->getContentService(); |
|
74
|
9 |
|
$this->contentTypeService = $repository->getContentTypeService(); |
|
75
|
9 |
|
$this->locationService = $repository->getLocationService(); |
|
76
|
9 |
|
} |
|
77
|
|
|
|
|
78
|
|
|
/** |
|
79
|
|
|
* {@inheritdoc} |
|
80
|
|
|
*/ |
|
81
|
9 |
|
public function setLogger(LoggerInterface $logger) |
|
82
|
|
|
{ |
|
83
|
9 |
|
$this->logger = $logger; |
|
84
|
9 |
|
} |
|
85
|
|
|
|
|
86
|
|
|
/** |
|
87
|
|
|
* Finds a content object by content ID or remote ID. |
|
88
|
|
|
* Returns ContentObject with populated properties, or false|NotFoundException. |
|
89
|
|
|
* |
|
90
|
|
|
* @param ValueObject|ContentObject $object |
|
91
|
|
|
* @param bool $throwException |
|
92
|
|
|
* |
|
93
|
|
|
* @return false|ContentObject |
|
94
|
|
|
* |
|
95
|
|
|
* @throws NotFoundException |
|
96
|
|
|
*/ |
|
97
|
11 |
|
public function find(ValueObject $object, $throwException = false) |
|
98
|
|
|
{ |
|
99
|
|
|
try { |
|
100
|
11 |
|
if ($object->getProperty('remote_id')) { |
|
101
|
11 |
|
$content = $this->contentService->loadContentByRemoteId($object->getProperty('remote_id')); |
|
102
|
8 |
|
} elseif ($object->getProperty('id')) { |
|
103
|
|
|
$content = $this->contentService->loadContent($object->getProperty('id')); |
|
104
|
|
|
} |
|
105
|
11 |
|
} catch (NotFoundException $notFoundException) { |
|
106
|
6 |
|
$exception = $notFoundException; |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
11 |
|
if (!isset($content)) { |
|
110
|
6 |
|
if (isset($exception) && $throwException) { |
|
111
|
|
|
throw $exception; |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
6 |
|
return false; |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
8 |
|
$object = new ContentObject(array()); |
|
118
|
8 |
|
$object->getMapper()->contentToObject($content); |
|
119
|
|
|
|
|
120
|
8 |
|
if ($content->contentInfo->published) { |
|
121
|
8 |
|
$locations = $this->locationService->loadLocations($content->contentInfo); |
|
122
|
8 |
|
$object->setParentLocations($locations); |
|
123
|
8 |
|
} |
|
124
|
|
|
|
|
125
|
8 |
|
$type = $this->contentTypeService->loadContentType($content->contentInfo->contentTypeId); |
|
126
|
8 |
|
$object->setProperty('content_type_identifier', $type->identifier); |
|
127
|
|
|
|
|
128
|
8 |
|
return $object; |
|
|
|
|
|
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
|
|
/** |
|
132
|
|
|
* {@inheritdoc} |
|
133
|
|
|
*/ |
|
134
|
6 |
|
public function create(ObjectInterface $object) |
|
135
|
|
|
{ |
|
136
|
6 |
|
if (!$object instanceof ContentObject) { |
|
137
|
|
|
throw new UnsupportedObjectOperationException(ContentObject::class, get_class($object)); |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
6 |
|
$createStruct = $this->contentService->newContentCreateStruct( |
|
141
|
6 |
|
$this->contentTypeService->loadContentTypeByIdentifier($object->getProperty('content_type_identifier')), |
|
142
|
6 |
|
$object->getProperty('language') |
|
143
|
6 |
|
); |
|
144
|
|
|
|
|
145
|
6 |
|
$this->mapObjectToContentStruct($object, $createStruct); |
|
146
|
|
|
|
|
147
|
|
|
/** @var LocationObject[] $locationObjects */ |
|
148
|
6 |
|
$locationObjects = $object->getProperty('parent_locations'); |
|
149
|
6 |
|
$locationCreateStructs = []; |
|
150
|
6 |
|
if (is_array($locationObjects) && count($locationObjects) > 0) { |
|
151
|
2 |
|
foreach ($locationObjects as $locationObject) { |
|
152
|
2 |
|
$locationCreateStruct = $this->locationService->newLocationCreateStruct($locationObject->data['parent_location_id']); |
|
153
|
2 |
|
$locationObject->getMapper()->getNewLocationCreateStruct($locationCreateStruct); |
|
154
|
2 |
|
$locationCreateStructs[] = $locationCreateStruct; |
|
155
|
2 |
|
} |
|
156
|
2 |
|
} |
|
157
|
|
|
|
|
158
|
6 |
|
$content = $this->contentService->createContent($createStruct, $locationCreateStructs); |
|
159
|
6 |
|
$this->contentService->publishVersion($content->versionInfo); |
|
160
|
|
|
|
|
161
|
6 |
|
if ($this->logger) { |
|
162
|
5 |
|
$this->logger->info(sprintf('Published new version of %s', $object->getProperty('name')), array('ContentManager::create')); |
|
163
|
5 |
|
} |
|
164
|
|
|
|
|
165
|
6 |
|
$object->setProperty('id', $content->contentInfo->id); |
|
166
|
6 |
|
$object->setProperty('version_info', $content->versionInfo); |
|
167
|
6 |
|
$object->setProperty('content_info', $content->contentInfo); |
|
168
|
|
|
|
|
169
|
6 |
|
return $object; |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
/** |
|
173
|
|
|
* {@inheritdoc} |
|
174
|
|
|
*/ |
|
175
|
11 |
|
public function update(ObjectInterface $object) |
|
176
|
8 |
|
{ |
|
177
|
7 |
|
if (!$object instanceof ContentObject) { |
|
178
|
|
|
throw new UnsupportedObjectOperationException(ContentObject::class, get_class($object)); |
|
179
|
11 |
|
} |
|
180
|
|
|
|
|
181
|
7 |
|
$existingContent = $this->find($object); |
|
182
|
7 |
|
if (null === $object->getProperty('content_info')) { |
|
183
|
6 |
|
$object->setProperty('content_info', $existingContent->getProperty('content_info')); |
|
184
|
6 |
|
} |
|
185
|
|
|
|
|
186
|
7 |
|
$contentDraft = $this->contentService->createContentDraft($object->getProperty('content_info')); |
|
187
|
|
|
|
|
188
|
7 |
|
$contentUpdateStruct = $this->contentService->newContentUpdateStruct(); |
|
189
|
7 |
|
$this->mapObjectToUpdateStruct($object, $contentUpdateStruct); |
|
190
|
|
|
|
|
191
|
7 |
|
$contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); |
|
192
|
7 |
|
$content = $this->contentService->publishVersion($contentDraft->versionInfo); |
|
193
|
|
|
|
|
194
|
7 |
|
if ($this->logger) { |
|
195
|
4 |
|
$this->logger->info(sprintf('Published new version of %s', $object->getProperty('name')), array('ContentManager::update')); |
|
196
|
4 |
|
} |
|
197
|
|
|
|
|
198
|
7 |
|
$object->setProperty('id', $content->contentInfo->id); |
|
199
|
7 |
|
$object->setProperty('version_info', $content->versionInfo); |
|
200
|
7 |
|
$object->setProperty('content_info', $content->contentInfo); |
|
201
|
|
|
|
|
202
|
|
|
// Add/Update/Delete parent locations |
|
203
|
7 |
|
$this->locationManager->syncronizeLocationsFromContentObject($object); |
|
204
|
|
|
|
|
205
|
7 |
|
return $object; |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
/** |
|
209
|
|
|
* {@inheritdoc} |
|
210
|
|
|
*/ |
|
211
|
11 |
|
public function createOrUpdate(ObjectInterface $object) |
|
212
|
|
|
{ |
|
213
|
11 |
|
if (!$object instanceof ContentObject) { |
|
214
|
|
|
throw new UnsupportedObjectOperationException(ContentObject::class, get_class($object)); |
|
215
|
|
|
} |
|
216
|
|
|
|
|
217
|
11 |
|
if (!$object->getProperty('content_id') && !$object->getProperty('remote_id')) { |
|
218
|
1 |
|
throw new MissingIdentificationPropertyException($object); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
10 |
|
if ($this->find($object)) { |
|
222
|
7 |
|
return $this->update($object); |
|
223
|
1 |
|
} else { |
|
224
|
6 |
|
return $this->create($object); |
|
225
|
|
|
} |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
/** |
|
229
|
|
|
* {@inheritdoc} |
|
230
|
|
|
*/ |
|
231
|
1 |
|
public function remove(ObjectInterface $object) |
|
232
|
|
|
{ |
|
233
|
1 |
|
if (!$object instanceof ContentObject) { |
|
234
|
|
|
throw new UnsupportedObjectOperationException(ContentObject::class, get_class($object)); |
|
235
|
|
|
} |
|
236
|
|
|
|
|
237
|
1 |
|
$object = $this->find($object); |
|
238
|
|
|
|
|
239
|
1 |
|
if ($object instanceof ContentObject && $object->getProperty('content_info')) { |
|
240
|
1 |
|
$this->contentService->deleteContent($object->getProperty('content_info')); |
|
241
|
|
|
|
|
242
|
1 |
|
return true; |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
return false; |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
/** |
|
249
|
|
|
* Assigns a main location ID for a content object. |
|
250
|
|
|
* |
|
251
|
|
|
* @param ContentObject $object Content object |
|
252
|
|
|
* @param Location $location Location |
|
253
|
|
|
* |
|
254
|
|
|
* @return Content |
|
255
|
|
|
*/ |
|
256
|
8 |
|
public function setMainLocation(ContentObject $object, Location $location) |
|
257
|
|
|
{ |
|
258
|
8 |
|
$contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); |
|
259
|
|
|
|
|
260
|
2 |
|
$contentMetadataUpdateStruct->mainLocationId = $location->id; |
|
261
|
|
|
|
|
262
|
2 |
|
$object->setProperty('main_location_id', $location->id); |
|
263
|
|
|
|
|
264
|
2 |
|
return $this->contentService->updateContentMetadata($object->getProperty('content_info'), $contentMetadataUpdateStruct); |
|
265
|
|
|
} |
|
266
|
|
|
|
|
267
|
|
|
/** |
|
268
|
|
|
* Maps object data to create struct. |
|
269
|
|
|
* |
|
270
|
|
|
* @param ContentObject $object Content object to map from |
|
271
|
|
|
* @param ContentCreateStruct $createStruct Content create struct to map to |
|
272
|
|
|
* |
|
273
|
|
|
* @throws \InvalidArgumentException |
|
274
|
|
|
*/ |
|
275
|
6 |
|
private function mapObjectToContentStruct(ContentObject $object, ContentCreateStruct $createStruct) |
|
276
|
|
|
{ |
|
277
|
6 |
|
$this->assignStructFieldValues($object, $createStruct); |
|
278
|
|
|
|
|
279
|
6 |
|
if ($object->getProperty('language')) { |
|
280
|
6 |
|
$createStruct->mainLanguageCode = $object->getProperty('language'); |
|
281
|
6 |
|
} |
|
282
|
|
|
|
|
283
|
6 |
|
if ($object->getProperty('remote_id')) { |
|
284
|
6 |
|
$createStruct->remoteId = $object->getProperty('remote_id'); |
|
285
|
6 |
|
} |
|
286
|
6 |
|
} |
|
287
|
|
|
|
|
288
|
|
|
/** |
|
289
|
|
|
* Maps object data to update struct. |
|
290
|
|
|
* |
|
291
|
|
|
* @param ContentObject $object Content object to map from |
|
292
|
|
|
* @param ContentUpdateStruct $contentUpdateStruct Content update struct to map to |
|
293
|
|
|
* |
|
294
|
|
|
* @throws \InvalidArgumentException |
|
295
|
|
|
*/ |
|
296
|
7 |
|
private function mapObjectToUpdateStruct(ContentObject $object, ContentUpdateStruct $contentUpdateStruct) |
|
297
|
|
|
{ |
|
298
|
7 |
|
$this->assignStructFieldValues($object, $contentUpdateStruct); |
|
299
|
7 |
|
} |
|
300
|
|
|
|
|
301
|
|
|
/** |
|
302
|
|
|
* Copies content object data from a struct. |
|
303
|
|
|
* |
|
304
|
|
|
* @param ContentObject $object Content object to get values from |
|
305
|
|
|
* @param object $struct Struct to assign values to |
|
306
|
|
|
*/ |
|
307
|
10 |
|
private function assignStructFieldValues(ContentObject $object, $struct) |
|
308
|
|
|
{ |
|
309
|
10 |
|
foreach ($object->data as $key => $value) { |
|
310
|
10 |
|
if (is_array($value)) { |
|
311
|
1 |
|
$value = end($value); |
|
312
|
1 |
|
} |
|
313
|
|
|
|
|
314
|
10 |
|
$struct->setField($key, $value); |
|
315
|
10 |
|
} |
|
316
|
10 |
|
} |
|
317
|
|
|
} |
|
318
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.