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

LocationManager::find()   C

Complexity

Conditions 8
Paths 20

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 8.0291

Importance

Changes 5
Bugs 0 Features 1
Metric Value
c 5
b 0
f 1
dl 0
loc 22
ccs 12
cts 13
cp 0.9231
rs 6.6037
cc 8
eloc 13
nc 20
nop 2
crap 8.0291
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 13
    public function setLogger(LoggerInterface $logger)
69 10
    {
70 13
        $this->logger = $logger;
71 13
    }
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 10
    public function find(ValueObject $object, $throwException = false)
85
    {
86
        try {
87 10
            if (isset($object->data['remote_id'])) {
88 9
                $location = $this->locationService->loadLocationByRemoteId($object->data['remote_id']);
89 10
            } elseif ($object->getProperty('id')) {
90 2
                $location = $this->locationService->loadLocation($object->getProperty('id'));
91 2
            }
92 10
        } catch (NotFoundException $notFound) {
93 2
            $exception = $notFound;
94
        }
95
96 10
        if (!isset($location)) {
97 4
            if (isset($exception) && $throwException) {
98
                throw $exception;
99
            }
100
101 4
            return false;
102
        }
103
104 9
        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 4
    public function create(ObjectInterface $object)
124
    {
125 4
        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 2
            $this->logger->info(sprintf('Created location %s on content id %s, with parent location id %s.', $location->id, $contentInfo->id, $location->parentLocationId));
139 2
        }
140
141 4
        $object->getMapper()->locationToObject($location);
142
143 4
        return $object;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149 9
    public function update(ObjectInterface $object)
150
    {
151 9
        if (!$object instanceof LocationObject) {
152 1
            throw new UnsupportedObjectOperationException(LocationObject::class, get_class($object));
153
        }
154
155 9
        $location = $this->find($object, true);
156
157
        // Move if parent_location_id differs.
158 9
        if (isset($object->data['parent_location_id'])) {
159 9
            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 9
        }
164
165 9
        $locationUpdateStruct = $this->locationService->newLocationUpdateStruct();
166
167 9
        $object->getMapper()->getNewLocationUpdateStruct($locationUpdateStruct);
168
169 9
        $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 9
        if ($this->logger) {
172 3
            $this->logger->info(sprintf('Updated location %s with parent location id %s.', $location->id, $location->parentLocationId));
173 3
        }
174
175 9
        $object->getMapper()->locationToObject($location);
176
177 9
        return $object;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183 10 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 10
        if (!$object instanceof LocationObject) {
186 1
            throw new UnsupportedObjectOperationException(LocationObject::class, get_class($object));
187
        }
188
189 10
        if ($this->find($object)) {
190 9
            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 13
    public function syncronizeLocationsFromContentObject(ContentObject $object)
202
    {
203
        /** @var LocationObject[] $parentLocations */
204 13
        $parentLocations = $object->getProperty('parent_locations');
205 13
        if (is_array($parentLocations) && count($parentLocations) > 0) {
206
207 2
            $addOrUpdate = [];
208 2
            foreach ($parentLocations as $parentLocation) {
209 2
                $addOrUpdate[$parentLocation->data['parent_location_id']] = $parentLocation;
210 2
            }
211
212
            // Filter which Locations should be created/updated and deleted.
213 2
            $delible = $this->filterLocationsToBeDeleted($object, $addOrUpdate);
214
215
            // Create or update locations, and attach to Content
216 2
            foreach ($addOrUpdate as $parentLocation) {
217 2
                $parentLocation->data['content_id'] = $object->getProperty('content_info')->id;
218 2
                $object->addParentLocation(
219 2
                    $this->createOrUpdate($parentLocation)
220 2
                );
221 2
            }
222
223
            // Lastly delete, cannot delete first because Content cannot have zero locations.
224 2
            foreach ($delible as $delete) {
225 1
                $this->locationService->deleteLocation($delete);
226 2
            }
227 2
        }
228 13
    }
229
230
    /**
231
     * @param ContentObject $object
232
     * @param LocationObject[] $locationsToKeep
233
     *
234
     * @return Location[]
235
     */
236 2
    private function filterLocationsToBeDeleted(ContentObject $object, $locationsToKeep)
237
    {
238 2
        $toBeDeleted = [];
239
240 2
        foreach ($this->locationService->loadLocations($object->getProperty('content_info')) as $existingLocation) {
241 2
            if (!array_key_exists($existingLocation->parentLocationId, $locationsToKeep)) {
242 1
                $toBeDeleted[] = $existingLocation;
243 1
            }
244 2
        }
245
246 2
        return $toBeDeleted;
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252 3 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...
253 2
    {
254 2
        if (!$object instanceof LocationObject) {
255 1
            throw new UnsupportedObjectOperationException(LocationObject::class, get_class($object));
256
        }
257
258 3
        if ($location = $this->find($object)) {
259 1
            $this->locationService->deleteLocation($location);
260 1
        }
261
262 1
        return true;
263
    }
264
265
    /**
266
     * Hides a location.
267
     *
268
     * @param Location $location
269
     *
270
     * @return Location
271
     */
272 1
    public function hide(Location $location)
273
    {
274 1
        return $this->locationService->hideLocation($location);
275
    }
276
277
    /**
278
     * Un-hides a location.
279
     *
280
     * @param Location $location
281
     *
282
     * @return Location
283
     */
284
    public function unHide(Location $location)
285
    {
286
        return $this->locationService->unhideLocation($location);
287
    }
288
289
    /**
290
     * Toggles location visibility.
291
     *
292
     * @param Location $location
293
     *
294
     * @return Location
295
     */
296 1
    public function toggleVisibility(Location $location)
297
    {
298 1
        if ($location->hidden) {
299
            return $this->unHide($location);
300
        }
301
302 1
        return $this->hide($location);
303
    }
304
}
305