Completed
Pull Request — 1.0 (#53)
by Harald
06:53
created

LocationManager::find()   C

Complexity

Conditions 8
Paths 20

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 8

Importance

Changes 5
Bugs 0 Features 1
Metric Value
c 5
b 0
f 1
dl 0
loc 22
ccs 13
cts 13
cp 1
rs 6.6037
cc 8
eloc 13
nc 20
nop 2
crap 8
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\Exceptions\NotFoundException;
14
use eZ\Publish\API\Repository\LocationService;
15
use eZ\Publish\API\Repository\Repository;
16
use eZ\Publish\API\Repository\Values\Content\Location;
17
use Psr\Log\LoggerAwareInterface;
18
use Psr\Log\LoggerInterface;
19
use Transfer\Data\ObjectInterface;
20
use Transfer\Data\ValueObject;
21
use Transfer\EzPlatform\Exception\UnsupportedObjectOperationException;
22
use Transfer\EzPlatform\Repository\Values\ContentObject;
23
use Transfer\EzPlatform\Repository\Values\LocationObject;
24
use Transfer\EzPlatform\Repository\Manager\Type\CreatorInterface;
25
use Transfer\EzPlatform\Repository\Manager\Type\RemoverInterface;
26
use Transfer\EzPlatform\Repository\Manager\Type\UpdaterInterface;
27
28
/**
29
 * Location manager.
30
 *
31
 * @internal
32
 */
33
class LocationManager implements LoggerAwareInterface, CreatorInterface, UpdaterInterface, RemoverInterface
34
{
35
    /**
36
     * @var LoggerInterface Logger
37
     */
38
    protected $logger;
39
40
    /**
41
     * @var Repository
42
     */
43
    private $repository;
44
45
    /**
46
     * @var LocationService Location service
47
     */
48
    private $locationService;
49
50
    /**
51
     * @var ContentService Content service
52
     */
53
    private $contentService;
54
55
    /**
56
     * @param Repository $repository
57
     */
58 14
    public function __construct(Repository $repository)
59
    {
60 14
        $this->repository = $repository;
61 14
        $this->locationService = $repository->getLocationService();
62 14
        $this->contentService = $repository->getContentService();
63 14
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68 33
    public function setLogger(LoggerInterface $logger)
69 12
    {
70 33
        $this->logger = $logger;
71 33
    }
72
73
    /**
74
     * Attempts to load Location based on id or remoteId.
75
     * Returns false if not found.
76
     *
77
     * @param ValueObject $object
78
     * @param bool        $throwException
79
     *
80
     * @return Location|false
81
     *
82
     * @throws NotFoundException
83
     */
84 12
    public function find(ValueObject $object, $throwException = false)
85
    {
86
        try {
87 12
            if (isset($object->data['remote_id'])) {
88 11
                $location = $this->locationService->loadLocationByRemoteId($object->data['remote_id']);
89 12
            } elseif ($object->getProperty('id')) {
90 3
                $location = $this->locationService->loadLocation($object->getProperty('id'));
91 3
            }
92 12
        } catch (NotFoundException $notFound) {
93 2
            $exception = $notFound;
94
        }
95
96 12
        if (!isset($location)) {
97 4
            if (isset($exception) && $throwException) {
98 1
                throw $exception;
99
            }
100
101 4
            return false;
102
        }
103
104 11
        return isset($location) ? $location : false;
105
    }
106
107
    /**
108
     * Shortcut to find, mainly for locating parents.
109
     *
110
     * @param int  $id
111
     * @param bool $throwException
112
     *
113
     * @return Location|false
114
     */
115 2
    public function findById($id, $throwException = false)
116
    {
117 2
        return $this->find(new ValueObject([], ['id' => $id]), $throwException);
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123 5
    public function create(ObjectInterface $object)
124
    {
125 5
        if (!$object instanceof LocationObject) {
126 1
            throw new UnsupportedObjectOperationException(LocationObject::class, get_class($object));
127
        }
128
129 4
        $contentInfo = $this->repository->getContentService()->loadContentInfo($object->data['content_id']);
130
131 4
        $locationCreateStruct = $this->locationService->newLocationCreateStruct($object->data['parent_location_id']);
132
133 4
        $object->getMapper()->getNewLocationCreateStruct($locationCreateStruct);
134
135 4
        $location = $this->locationService->createLocation($contentInfo, $locationCreateStruct);
136
137 4
        if ($this->logger) {
138 4
            $this->logger->info(sprintf('Created location %s on content id %s, with parent location id %s.', $location->id, $contentInfo->id, $location->parentLocationId));
139 4
        }
140
141 4
        $object->getMapper()->locationToObject($location);
142
143 4
        return $object;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149 11
    public function update(ObjectInterface $object)
150
    {
151 11
        if (!$object instanceof LocationObject) {
152 1
            throw new UnsupportedObjectOperationException(LocationObject::class, get_class($object));
153
        }
154
155 11
        $location = $this->find($object, true);
156
157
        // Move if parent_location_id differs.
158 11
        if (isset($object->data['parent_location_id'])) {
159 11
            if ($object->data['parent_location_id'] !== $location->parentLocationId) {
160 2
                $parentLocation = $this->findById($object->data['parent_location_id'], true);
161 2
                $this->locationService->moveSubtree($location, $parentLocation);
0 ignored issues
show
Security Bug introduced by
It seems like $location defined by $this->find($object, true) on line 155 can also be of type false; however, eZ\Publish\API\Repositor...nService::moveSubtree() does only seem to accept object<eZ\Publish\API\Re...alues\Content\Location>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
Security Bug introduced by
It seems like $parentLocation defined by $this->findById($object-...nt_location_id'], true) on line 160 can also be of type false; however, eZ\Publish\API\Repositor...nService::moveSubtree() does only seem to accept object<eZ\Publish\API\Re...alues\Content\Location>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
162 2
            }
163 11
        }
164
165 11
        $locationUpdateStruct = $this->locationService->newLocationUpdateStruct();
166
167 11
        $object->getMapper()->getNewLocationUpdateStruct($locationUpdateStruct);
168
169 11
        $location = $this->locationService->updateLocation($location, $locationUpdateStruct);
0 ignored issues
show
Security Bug introduced by
It seems like $location can also be of type false; however, eZ\Publish\API\Repositor...rvice::updateLocation() does only seem to accept object<eZ\Publish\API\Re...alues\Content\Location>, did you maybe forget to handle an error condition?
Loading history...
170
171 11
        if ($this->logger) {
172 11
            $this->logger->info(sprintf('Updated location %s with parent location id %s.', $location->id, $location->parentLocationId));
173 11
        }
174
175 11
        $object->getMapper()->locationToObject($location);
176
177 11
        return $object;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183 12 View Code Duplication
    public function createOrUpdate(ObjectInterface $object)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
184
    {
185 12
        if (!$object instanceof LocationObject) {
186 1
            throw new UnsupportedObjectOperationException(LocationObject::class, get_class($object));
187
        }
188
189 12
        if ($this->find($object)) {
190 11
            return $this->update($object);
191
        } else {
192 4
            return $this->create($object);
193
        }
194
    }
195
196
    /**
197
     * Creates/updates/deletes locations in ContentObject->parent_locations.
198
     *
199
     * @param ContentObject $object
200
     */
201 15
    public function syncronizeLocationsFromContentObject(ContentObject $object)
202
    {
203
        /** @var LocationObject[] $parentLocations */
204 15
        $parentLocations = $object->getProperty('parent_locations');
205 15
        if (is_array($parentLocations) && count($parentLocations) > 0) {
206 2
            $addOrUpdate = [];
207 2
            foreach ($parentLocations as $parentLocation) {
208 2
                $addOrUpdate[$parentLocation->data['parent_location_id']] = $parentLocation;
209 2
            }
210
211
            // Filter which Locations should be created/updated and deleted.
212 2
            $delible = $this->filterLocationsToBeDeleted($object, $addOrUpdate);
213
214
            // Create or update locations, and attach to Content
215 2
            foreach ($addOrUpdate as $parentLocation) {
216 2
                $parentLocation->data['content_id'] = $object->getProperty('content_info')->id;
217 2
                $object->addParentLocation(
218 2
                    $this->createOrUpdate($parentLocation)
219 2
                );
220 2
            }
221
222
            // Lastly delete, cannot delete first because Content cannot have zero locations.
223 2
            foreach ($delible as $delete) {
224 1
                $this->locationService->deleteLocation($delete);
225 2
            }
226 2
        }
227 15
    }
228
229
    /**
230
     * @param ContentObject    $object
231
     * @param LocationObject[] $locationsToKeep
232
     *
233
     * @return Location[]
234
     */
235 2
    private function filterLocationsToBeDeleted(ContentObject $object, $locationsToKeep)
236
    {
237 2
        $toBeDeleted = [];
238
239 2
        foreach ($this->locationService->loadLocations($object->getProperty('content_info')) as $existingLocation) {
240 2
            if (!array_key_exists($existingLocation->parentLocationId, $locationsToKeep)) {
241 1
                $toBeDeleted[] = $existingLocation;
242 1
            }
243 2
        }
244
245 2
        return $toBeDeleted;
246
    }
247
248
    /**
249
     * {@inheritdoc}
250
     */
251 4 View Code Duplication
    public function remove(ObjectInterface $object)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
252
    {
253 4
        if (!$object instanceof LocationObject) {
254 1
            throw new UnsupportedObjectOperationException(LocationObject::class, get_class($object));
255
        }
256
257 1
        if ($location = $this->find($object)) {
258 3
            $this->locationService->deleteLocation($location);
259 1
        }
260
261 1
        return true;
262
    }
263
264
    /**
265
     * Hides a location.
266
     *
267
     * @param Location $location
268
     *
269
     * @return Location
270
     */
271 2
    public function hide(Location $location)
272
    {
273 2
        return $this->locationService->hideLocation($location);
274
    }
275
276
    /**
277
     * Un-hides a location.
278
     *
279
     * @param Location $location
280
     *
281
     * @return Location
282
     */
283 1
    public function unHide(Location $location)
284
    {
285 1
        return $this->locationService->unhideLocation($location);
286
    }
287
288
    /**
289
     * Toggles location visibility.
290
     *
291
     * @param Location $location
292
     *
293
     * @return Location
294
     */
295 2
    public function toggleVisibility(Location $location)
296
    {
297 2
        if ($location->hidden) {
298 1
            return $this->unHide($location);
299
        }
300
301 2
        return $this->hide($location);
302
    }
303
}
304