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

LocationManager::update()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5.0042

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 30
ccs 17
cts 18
cp 0.9444
rs 8.439
cc 5
eloc 15
nc 7
nop 1
crap 5.0042
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\Repository\Values\ContentObject;
22
use Transfer\EzPlatform\Repository\Values\LocationObject;
23
use Transfer\EzPlatform\Repository\Manager\Type\CreatorInterface;
24
use Transfer\EzPlatform\Repository\Manager\Type\RemoverInterface;
25
use Transfer\EzPlatform\Repository\Manager\Type\UpdaterInterface;
26
27
/**
28
 * Location manager.
29
 *
30
 * @internal
31
 */
32
class LocationManager implements LoggerAwareInterface, CreatorInterface, UpdaterInterface, RemoverInterface
33
{
34
    /**
35
     * @var LoggerInterface Logger
36
     */
37
    protected $logger;
38
39
    /**
40
     * @var Repository
41
     */
42
    private $repository;
43
44
    /**
45
     * @var LocationService Location service
46
     */
47
    private $locationService;
48
49
    /**
50
     * @var ContentService Content service
51
     */
52
    private $contentService;
53
54
    /**
55
     * @param Repository $repository
56
     */
57 10
    public function __construct(Repository $repository)
58
    {
59 10
        $this->repository = $repository;
60 10
        $this->locationService = $repository->getLocationService();
61 10
        $this->contentService = $repository->getContentService();
62 10
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 10
    public function setLogger(LoggerInterface $logger)
68
    {
69 10
        $this->logger = $logger;
70 10
    }
71
72
    /**
73
     * Attempts to load Location based on id or remoteId.
74
     * Returns false if not found.
75
     *
76
     * @param ValueObject $object
77
     * @param bool        $throwException
78
     *
79
     * @return Location|false
80
     *
81
     * @throws NotFoundException
82
     */
83 4
    public function find(ValueObject $object, $throwException = false)
84
    {
85
        try {
86 4
            if (isset($object->data['remote_id'])) {
87 3
                $location = $this->locationService->loadLocationByRemoteId($object->data['remote_id']);
88 4
            } elseif ($object->getProperty('id')) {
89 2
                $location = $this->locationService->loadLocation($object->getProperty('id'));
90 2
            }
91 4
        } catch (NotFoundException $notFound) {
92 1
            $exception = $notFound;
93
        }
94
95 4
        if (!isset($location)) {
96 2
            if (isset($exception) && $throwException) {
97
                throw $exception;
98
            }
99
100 2
            return false;
101
        }
102
103 3
        return isset($location) ? $location : false;
104
    }
105
106
    /**
107
     * Shortcut to find, mainly for locating parents.
108
     *
109
     * @param int  $id
110
     * @param bool $throwException
111
     *
112
     * @return Location|false
113
     */
114 2
    public function findById($id, $throwException = false)
115
    {
116 2
        return $this->find(new ValueObject([], ['id' => $id]), $throwException);
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122 2
    public function create(ObjectInterface $object)
123
    {
124 2
        if (!$object instanceof LocationObject) {
125
            return;
126
        }
127
128 2
        $contentInfo = $this->repository->getContentService()->loadContentInfo($object->data['content_id']);
129
130 2
        $locationCreateStruct = $this->locationService->newLocationCreateStruct($object->data['parent_location_id']);
131
132 2
        $object->getMapper()->getNewLocationCreateStruct($locationCreateStruct);
133
134 2
        $location = $this->locationService->createLocation($contentInfo, $locationCreateStruct);
135
136 2
        if ($this->logger) {
137 1
            $this->logger->info(sprintf('Created location %s on content id %s, with parent location id %s.', $location->id, $contentInfo->id, $location->parentLocationId));
138 1
        }
139
140 2
        $object->getMapper()->locationToObject($location);
141
142 2
        return $object;
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 3
    public function update(ObjectInterface $object)
149
    {
150 3
        if (!$object instanceof LocationObject) {
151
            return;
152
        }
153
154 3
        $location = $this->find($object, true);
155
156
        // Move if parent_location_id differs.
157 3
        if (isset($object->data['parent_location_id'])) {
158 3
            if ($object->data['parent_location_id'] !== $location->parentLocationId) {
159 2
                $parentLocation = $this->findById($object->data['parent_location_id'], true);
160 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 154 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 159 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...
161 2
            }
162 3
        }
163
164 3
        $locationUpdateStruct = $this->locationService->newLocationUpdateStruct();
165
166 3
        $object->getMapper()->getNewLocationUpdateStruct($locationUpdateStruct);
167
168 3
        $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...
169
170 3
        if ($this->logger) {
171 2
            $this->logger->info(sprintf('Updated location %s with parent location id %s.', $location->id, $location->parentLocationId));
172 2
        }
173
174 3
        $object->getMapper()->locationToObject($location);
175
176 3
        return $object;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182 4
    public function createOrUpdate(ObjectInterface $object)
183
    {
184 4
        if (!$object instanceof LocationObject) {
185
            return;
186
        }
187
188 4
        if ($this->find($object)) {
189 3
            return $this->update($object);
190
        } else {
191 2
            return $this->create($object);
192
        }
193
    }
194
195
    /**
196
     * Creates/updates/deletes locations in ContentObject->parent_locations.
197
     *
198
     * @param ContentObject $object
199
     */
200 6
    public function syncronizeLocationsFromContentObject(ContentObject $object)
201
    {
202
        /** @var LocationObject[] $locationObjects */
203 6
        $locationObjects = $object->getProperty('parent_locations');
204 6
        if (is_array($locationObjects) && count($locationObjects) > 0) {
205 1
            $addOrUpdate = [];
206 1
            foreach ($locationObjects as $locationObject) {
207 1
                $addOrUpdate[$locationObject->data['parent_location_id']] = $locationObject;
208 1
            }
209
210 1
            $existingLocations = [];
211 1
            $deleteThese = [];
212 1
            foreach ($this->locationService->loadLocations($object->getProperty('content_info')) as $existingLocation) {
213 1
                if (!array_key_exists($existingLocation->parentLocationId, $addOrUpdate)) {
214 1
                    $deleteThese[] = $existingLocation;
215 1
                } else {
216
                    $existingLocations[$existingLocation->parentLocationId] = $existingLocation;
217
                }
218 1
            }
219
220 1
            foreach ($addOrUpdate as $locationObject) {
221 1
                if (!array_key_exists($locationObject->data['parent_location_id'], $existingLocations)) {
222
                    // create or update
223 1
                    $locationObject->data['content_id'] = $object->getProperty('content_info')->id;
224 1
                    $locationObject = $this->createOrUpdate($locationObject);
225 1
                    $object->addParentLocation($locationObject);
0 ignored issues
show
Bug introduced by
It seems like $locationObject defined by $this->createOrUpdate($locationObject) on line 224 can be null; however, Transfer\EzPlatform\Repo...ct::addParentLocation() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
226 1
                }
227 1
            }
228
229 1
            foreach ($deleteThese as $delete) {
230 1
                $this->locationService->deleteLocation($delete);
231 1
            }
232 1
        }
233 6
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238
    public function remove(ObjectInterface $object)
239
    {
240
        if (!$object instanceof LocationObject) {
241
            return;
242
        }
243
244
        if ($location = $this->find($object)) {
245
            $this->locationService->deleteLocation($location);
246
        }
247
248
        return true;
249
    }
250
251
    /**
252
     * Hides a location.
253
     *
254
     * @param Location $location
255
     *
256
     * @return Location
257
     */
258
    public function hide(Location $location)
259
    {
260
        return $this->locationService->hideLocation($location);
261
    }
262
263
    /**
264
     * Un-hides a location.
265
     *
266
     * @param Location $location
267
     *
268
     * @return Location
269
     */
270
    public function unHide(Location $location)
271
    {
272
        return $this->locationService->unhideLocation($location);
273
    }
274
275
    /**
276
     * Toggles location visibility.
277
     *
278
     * @param Location $location
279
     *
280
     * @return Location
281
     */
282
    public function toggleVisibility(Location $location)
283
    {
284
        if ($location->hidden) {
285
            return $this->unHide($location);
286
        }
287
288
        return $this->hide($location);
289
    }
290
}
291