Completed
Push — EZP-30821 ( 252180 )
by
unknown
150:25 queued 129:39
created

URLService::findUrls()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 27

Duplication

Lines 6
Ratio 22.22 %

Importance

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