Completed
Push — EZP-30821 ( 252180...e4376f )
by
unknown
19:44
created

URLService::findUrls()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 26

Duplication

Lines 6
Ratio 23.08 %

Importance

Changes 0
Metric Value
cc 7
nc 5
nop 1
dl 6
loc 26
rs 8.5706
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Repository;
8
9
use DateTime;
10
use Exception;
11
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
12
use eZ\Publish\API\Repository\PermissionResolver;
13
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
14
use eZ\Publish\API\Repository\URLService as URLServiceInterface;
15
use eZ\Publish\API\Repository\Values\Content\Query;
16
use eZ\Publish\API\Repository\Values\Content\Query\Criterion as ContentCriterion;
17
use eZ\Publish\API\Repository\Values\URL\SearchResult;
18
use eZ\Publish\API\Repository\Values\URL\URL;
19
use eZ\Publish\API\Repository\Values\URL\URLQuery;
20
use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct;
21
use eZ\Publish\API\Repository\Values\URL\UsageSearchResult;
22
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
23
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
24
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
25
use eZ\Publish\SPI\Persistence\URL\Handler as URLHandler;
26
use eZ\Publish\SPI\Persistence\URL\URL as SPIUrl;
27
use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct as SPIUrlUpdateStruct;
28
29
class URLService implements URLServiceInterface
30
{
31
    /** @var \eZ\Publish\Core\Repository\Repository */
32
    protected $repository;
33
34
    /** @var \eZ\Publish\SPI\Persistence\URL\Handler */
35
    protected $urlHandler;
36
37
    /** \eZ\Publish\API\Repository\PermissionResolver */
38
    private $permissionResolver;
39
40
    /**
41
     * @param \eZ\Publish\API\Repository\Repository $repository
42
     * @param \eZ\Publish\SPI\Persistence\URL\Handler $urlHandler
43
     * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver
44
     */
45
    public function __construct(
46
        RepositoryInterface $repository,
47
        URLHandler $urlHandler,
48
        PermissionResolver $permissionResolver
49
    ) {
50
        $this->repository = $repository;
0 ignored issues
show
Documentation Bug introduced by
$repository is of type object<eZ\Publish\API\Repository\Repository>, but the property $repository was declared to be of type object<eZ\Publish\Core\Repository\Repository>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
51
        $this->urlHandler = $urlHandler;
52
        $this->permissionResolver = $permissionResolver;
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function findUrls(URLQuery $query)
59
    {
60
        if ($this->repository->hasAccess('url', 'view') === false) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead. Check if user has access to a given module / function. Low level function, use canUser instead if you have objects to check against.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
61
            throw new UnauthorizedException('url', 'view');
62
        }
63
64 View Code Duplication
        if ($query->offset !== null && !is_numeric($query->offset)) {
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...
65
            throw new InvalidArgumentValue('offset', $query->offset);
66
        }
67
68 View Code Duplication
        if ($query->limit !== null && !is_numeric($query->limit)) {
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...
69
            throw new InvalidArgumentValue('limit', $query->limit);
70
        }
71
72
        $results = $this->urlHandler->find($query);
73
74
        $items = [];
75
        foreach ($results['items'] as $url) {
76
            $items[] = $this->buildDomainObject($url);
77
        }
78
79
        return new SearchResult([
80
            'totalCount' => $results['count'],
81
            'items' => $items,
82
        ]);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 View Code Duplication
    public function updateUrl(URL $url, URLUpdateStruct $struct)
89
    {
90
        if (!$this->permissionResolver->canUser('url', 'update', $url)) {
91
            throw new UnauthorizedException('url', 'update');
92
        }
93
94
        if (!$this->isUnique($url->id, $struct->url)) {
95
            throw new InvalidArgumentException('struct', 'url already exists');
96
        }
97
98
        $updateStruct = $this->buildUpdateStruct($this->loadById($url->id), $struct);
99
100
        $this->repository->beginTransaction();
101
        try {
102
            $this->urlHandler->updateUrl($url->id, $updateStruct);
103
            $this->repository->commit();
104
        } catch (Exception $e) {
105
            $this->repository->rollback();
106
            throw $e;
107
        }
108
109
        return $this->loadById($url->id);
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 View Code Duplication
    public function loadById($id)
116
    {
117
        $url = $this->buildDomainObject(
118
            $this->urlHandler->loadById($id)
119
        );
120
121
        if (!$this->permissionResolver->canUser('url', 'view', $url)) {
122
            throw new UnauthorizedException('url', 'view');
123
        }
124
125
        return $url;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 View Code Duplication
    public function loadByUrl($url)
132
    {
133
        $apiUrl = $this->buildDomainObject(
134
            $this->urlHandler->loadByUrl($url)
135
        );
136
137
        if (!$this->permissionResolver->canUser('url', 'view', $apiUrl)) {
138
            throw new UnauthorizedException('url', 'view');
139
        }
140
141
        return $apiUrl;
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147
    public function createUpdateStruct()
148
    {
149
        return new URLUpdateStruct();
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155
    public function findUsages(URL $url, $offset = 0, $limit = -1)
156
    {
157
        $contentIds = $this->urlHandler->findUsages($url->id);
158
        if (empty($contentIds)) {
159
            return new UsageSearchResult();
160
        }
161
162
        $query = new Query();
163
        $query->filter = new ContentCriterion\LogicalAnd([
164
            new ContentCriterion\ContentId($contentIds),
165
            new ContentCriterion\Visibility(ContentCriterion\Visibility::VISIBLE),
166
        ]);
167
168
        $query->offset = $offset;
169
        if ($limit > -1) {
170
            $query->limit = $limit;
171
        }
172
173
        $searchResults = $this->repository->getSearchService()->findContentInfo($query);
174
175
        $usageResults = new UsageSearchResult();
176
        $usageResults->totalCount = $searchResults->totalCount;
177
        foreach ($searchResults->searchHits as $hit) {
178
            $usageResults->items[] = $hit->valueObject;
179
        }
180
181
        return $usageResults;
182
    }
183
184
    /**
185
     * Builds domain object from ValueObject returned by Persistence API.
186
     *
187
     * @param \eZ\Publish\SPI\Persistence\URL\URL $data
188
     *
189
     * @return \eZ\Publish\API\Repository\Values\URL\URL
190
     */
191
    protected function buildDomainObject(SPIUrl $data)
192
    {
193
        return new URL([
194
            'id' => $data->id,
195
            'url' => $data->url,
196
            'isValid' => $data->isValid,
197
            'lastChecked' => $this->createDateTime($data->lastChecked),
198
            'created' => $this->createDateTime($data->created),
199
            'modified' => $this->createDateTime($data->modified),
200
        ]);
201
    }
202
203
    /**
204
     * Builds SPI update structure used by Persistence API.
205
     *
206
     * @param \eZ\Publish\API\Repository\Values\URL\URL $url
207
     * @param \eZ\Publish\API\Repository\Values\URL\URLUpdateStruct $data
208
     *
209
     * @return \eZ\Publish\SPI\Persistence\URL\URLUpdateStruct
210
     */
211
    protected function buildUpdateStruct(URL $url, URLUpdateStruct $data)
212
    {
213
        $updateStruct = new SPIUrlUpdateStruct();
214
215
        if ($data->url !== null && $url->url !== $data->url) {
216
            $updateStruct->url = $data->url;
217
            // Reset URL validity
218
            $updateStruct->lastChecked = 0;
219
            $updateStruct->isValid = true;
220
        } else {
221
            $updateStruct->url = $url->url;
222
223
            if ($data->lastChecked !== null) {
224
                $updateStruct->lastChecked = $data->lastChecked->getTimestamp();
225
            } elseif ($data->lastChecked !== null) {
226
                $updateStruct->lastChecked = $url->lastChecked->getTimestamp();
227
            } else {
228
                $updateStruct->lastChecked = 0;
229
            }
230
231
            if ($data->isValid !== null) {
232
                $updateStruct->isValid = $data->isValid;
233
            } else {
234
                $updateStruct->isValid = $url->isValid;
235
            }
236
        }
237
238
        return $updateStruct;
239
    }
240
241
    /**
242
     * Check if URL is unique.
243
     *
244
     * @param int $id
245
     * @param string $url
246
     *
247
     * @return bool
248
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
249
     */
250
    protected function isUnique($id, $url)
251
    {
252
        try {
253
            return $this->loadByUrl($url)->id === $id;
254
        } catch (NotFoundException $e) {
255
            return true;
256
        }
257
    }
258
259
    private function createDateTime($timestamp)
260
    {
261
        if ($timestamp > 0) {
262
            return new DateTime("@{$timestamp}");
263
        }
264
265
        return null;
266
    }
267
}
268