Completed
Push — master ( b220cf...77a24b )
by Simonas
104:16 queued 39:36
created

TranslationManager::addObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 5
Bugs 2 Features 3
Metric Value
c 5
b 2
f 3
dl 0
loc 16
ccs 0
cts 12
cp 0
rs 9.4286
cc 1
eloc 11
nc 1
nop 2
crap 2
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\TranslationsBundle\Translation;
13
14
use Elasticsearch\Common\Exceptions\Missing404Exception;
15
use ONGR\ElasticsearchBundle\Result\ObjectIterator;
16
use ONGR\ElasticsearchBundle\Result\Result;
17
use ONGR\ElasticsearchDSL\Query\ExistsQuery;
18
use ONGR\ElasticsearchDSL\Query\TermsQuery;
19
use ONGR\ElasticsearchBundle\Service\Repository;
20
use ONGR\TranslationsBundle\Event\Events;
21
use ONGR\TranslationsBundle\Event\TranslationEditMessageEvent;
22
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
23
use Symfony\Component\HttpFoundation\Request;
24
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
25
use Symfony\Component\PropertyAccess\PropertyAccess;
26
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
27
28
/**
29
 * Handles translation objects by http requests.
30
 */
31
class TranslationManager
32
{
33
    /**
34
     * @var Repository
35
     */
36
    private $repository;
37
38
    /**
39
     * @var PropertyAccessorInterface
40
     */
41
    private $accessor;
42
43
    /**
44
     * @var EventDispatcherInterface
45
     */
46
    private $dispatcher;
47
48
    /**
49
     * @param Repository               $repository
50
     * @param EventDispatcherInterface $dispatcher
51
     */
52
    public function __construct(Repository $repository, EventDispatcherInterface $dispatcher)
53
    {
54
        $this->repository = $repository;
55
        $this->dispatcher = $dispatcher;
56
    }
57
58
    /**
59
     * Adds object to translations.
60
     *
61
     * @param Request $request
62
     */
63 View Code Duplication
    public function add(Request $request)
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...
64
    {
65
        $content = $this->parseJsonContent($request);
66
        $document = $this->getTranslation($content['id']);
67
        $this->addObject($document, $content);
68
        $this->commitTranslation($document);
69
    }
70
71
    /**
72
     * Edits object from translation.
73
     *
74
     * @param Request $request Http request object.
75
     */
76
    public function edit(Request $request)
77
    {
78
        $content = $this->parseJsonContent($request);
79
        $document = $this->getTranslation($content['id']);
80
81
        if ($content['name'] == 'messages') {
82
            $this->dispatcher->dispatch(Events::ADD_HISTORY, new TranslationEditMessageEvent($request, $document));
83
        }
84
        $this->editObject($document, $content);
85
        $this->commitTranslation($document);
86
    }
87
88
    /**
89
     * Removes object from translations.
90
     *
91
     * @param Request $request Http request object.
92
     */
93 View Code Duplication
    public function delete(Request $request)
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...
94
    {
95
        $content = $this->parseJsonContent($request);
96
        $document = $this->getTranslation($content['id']);
97
        $this->deleteObject($document, $content);
98
        $this->commitTranslation($document);
99
    }
100
101
    /**
102
     * Returns specific values from objects.
103
     *
104
     * @param Request $request Http request object.
105
     *
106
     * @return array
107
     */
108
    public function get(Request $request)
109
    {
110
        $content = $this->parseJsonContent($request);
111
112
        $search = $this
113
            ->repository
114
            ->createSearch()
115
            ->addFilter(new ExistsQuery($content['name']));
116
117
        if (array_key_exists('properties', $content)) {
118
            foreach ($content['properties'] as $property) {
119
                $search->setSource($content['name'] . '.' . $property);
120
            }
121
        }
122
123
        if (array_key_exists('findBy', $content)) {
124
            foreach ($content['findBy'] as $field => $value) {
125
                $search->addQuery(
126
                    new TermsQuery($content['name'] . '.' . $field, is_array($value) ? $value : [$value]),
127
                    'must'
128
                );
129
            }
130
        }
131
132
        return $this->repository->execute($search, Result::RESULTS_ARRAY);
133
    }
134
135
    /**
136
     * Adds object to translation.
137
     *
138
     * @param object $document
139
     * @param array  $options
140
     */
141
    private function addObject($document, $options)
142
    {
143
        $accessor = $this->getAccessor();
144
        $objects = $accessor->getValue($document, $options['name']);
145
146
        $meta = $this->repository->getManager()->getMetadataCollector()
147
            ->getBundleMapping('ONGRTranslationsBundle:Translation');
148
        $objectClass = reset($meta)['aliases'][$options['name']]['namespace'];
149
150
        $object = new $objectClass();
151
        $this->setObjectProperties($object, $options['properties']);
152
153
        $objects[] = $object;
154
        $this->updateTimestamp($object);
155
        $this->updateTimestamp($document);
156
    }
157
158
    /**
159
     * Removes message from document based on options.
160
     *
161
     * @param object $document
162
     * @param array  $options
163
     */
164
    private function deleteObject($document, $options)
165
    {
166
        $accessor = $this->getAccessor();
167
        $objects = $accessor->getValue($document, $options['name']);
168
169
        $key = $this->findObject($objects, $options['findBy']);
170
171
        if ($key >= 0) {
172
            unset($objects[$key]);
173
            $accessor->setValue($document, $options['name'], $objects);
174
        }
175
    }
176
177
    /**
178
     * Edits message from document based on options.
179
     *
180
     * @param object $document
181
     * @param array  $options
182
     */
183
    private function editObject($document, $options)
184
    {
185
        $accessor = $this->getAccessor();
186
        $objects = $accessor->getValue($document, $options['name']);
187
188
        if ($objects === null) {
189
            $this->addObject($document, $options);
190
        } else {
191
            $key = $this->findObject($objects, $options['findBy']);
192
193
            if ($key < 0) {
194
                $this->addObject($document, $options);
195
            } else {
196
                $this->setObjectProperties($objects[$key], $options['properties']);
197
                $this->updateTimestamp($objects[$key]);
198
                $this->updateTimestamp($document);
199
                $accessor->setValue($document, $options['name'], $objects);
200
            }
201
        }
202
    }
203
204
    /**
205
     * Finds object by property and its value from iterator and returns key.
206
     *
207
     * @param \Iterator $objects
208
     * @param array     $options
209
     *
210
     * @return int
211
     */
212
    private function findObject($objects, $options)
213
    {
214
        foreach ($objects as $key => $object) {
215
            $fit = true;
216
217
            foreach ($options as $property => $value) {
218
                if ($this->getAccessor()->getValue($object, $property) !== $value) {
219
                    $fit = false;
220
                    break;
221
                }
222
            }
223
224
            if ($fit) {
225
                return $key;
226
            }
227
        }
228
229
        return -1;
230
    }
231
232
    /**
233
     * Parses http request content from json to array.
234
     *
235
     * @param Request $request Http request object.
236
     *
237
     * @return array
238
     *
239
     * @throws BadRequestHttpException
240
     */
241 View Code Duplication
    private function parseJsonContent(Request $request)
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...
242
    {
243
        $content = json_decode($request->getContent(), true);
244
245
        if (empty($content)) {
246
            throw new BadRequestHttpException('No content found.');
247
        }
248
249
        return $content;
250
    }
251
252
    /**
253
     * Commits document into elasticsearch client.
254
     *
255
     * @param object $document
256
     */
257
    private function commitTranslation($document)
258
    {
259
        $this->repository->getManager()->persist($document);
260
        $this->repository->getManager()->commit();
261
    }
262
263
    /**
264
     * Returns translation from elasticsearch.
265
     *
266
     * @param string $id
267
     *
268
     * @return object
269
     *
270
     * @throws BadRequestHttpException
271
     */
272
    private function getTranslation($id)
273
    {
274
        try {
275
            $document = $this->repository->find($id);
276
        } catch (Missing404Exception $e) {
277
            $document = null;
278
        }
279
280
        if ($document === null) {
281
            throw new BadRequestHttpException('Invalid translation Id.');
282
        }
283
284
        return $document;
285
    }
286
287
    /**
288
     * Returns property accessor instance.
289
     *
290
     * @return PropertyAccessorInterface
291
     */
292
    private function getAccessor()
293
    {
294
        if (!$this->accessor) {
295
            $this->accessor = PropertyAccess::createPropertyAccessorBuilder()
296
                ->enableExceptionOnInvalidIndex()
297
                ->enableMagicCall()
298
                ->getPropertyAccessor();
299
        }
300
301
        return $this->accessor;
302
    }
303
304
    /**
305
     * Sets `updated_at` property.
306
     *
307
     * @param object $object
308
     */
309
    private function updateTimestamp($object)
310
    {
311
        $accessor = $this->getAccessor();
312
313
        if ($accessor->isWritable($object, 'updated_at')) {
314
            $accessor->setValue($object, 'updated_at', new \DateTime());
315
        }
316
    }
317
318
    /**
319
     * Sets object properties into provided object.
320
     *
321
     * @param object $object     Object to set properties into.
322
     * @param array  $properties Array of properties to set.
323
     */
324
    private function setObjectProperties($object, $properties)
325
    {
326
        foreach ($properties as $property => $value) {
327
            $this->getAccessor()->setValue($object, $property, $value);
328
        }
329
    }
330
}
331