Completed
Push — master ( 3beb19...b220cf )
by Simonas
62:08
created

TranslationManager::addObject()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 4
Bugs 1 Features 3
Metric Value
c 4
b 1
f 3
dl 0
loc 25
ccs 0
cts 16
cp 0
rs 8.8571
cc 2
eloc 16
nc 2
nop 2
crap 6
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);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $this->getTranslation($content['id']) on line 66 can also be of type array; however, ONGR\TranslationsBundle\...ionManager::addObject() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
68
        $this->commitTranslation($document);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $this->getTranslation($content['id']) on line 66 can also be of type array; however, ONGR\TranslationsBundle\...er::commitTranslation() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
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));
0 ignored issues
show
Bug introduced by
It seems like $document defined by $this->getTranslation($content['id']) on line 79 can also be of type array; however, ONGR\TranslationsBundle\...ageEvent::__construct() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
83
        }
84
        $this->editObject($document, $content);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $this->getTranslation($content['id']) on line 79 can also be of type array; however, ONGR\TranslationsBundle\...onManager::editObject() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
85
        $this->commitTranslation($document);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $this->getTranslation($content['id']) on line 79 can also be of type array; however, ONGR\TranslationsBundle\...er::commitTranslation() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $this->getTranslation($content['id']) on line 96 can also be of type array; however, ONGR\TranslationsBundle\...Manager::deleteObject() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
98
        $this->commitTranslation($document);
0 ignored issues
show
Bug introduced by
It seems like $document defined by $this->getTranslation($content['id']) on line 96 can also be of type array; however, ONGR\TranslationsBundle\...er::commitTranslation() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
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
        $this->updateTimestamp($object);
154
155
        if ($objects instanceof ObjectIterator) {
156
            // TODO: refactor after ESB gives easy way to append object!
157
            $objects = iterator_to_array($objects);
158
            $objects[] = $object;
159
        } else {
160
            $objects = [$object];
161
        }
162
163
        $accessor->setValue($document, $options['name'], $objects);
164
        $this->updateTimestamp($document);
0 ignored issues
show
Bug introduced by
It seems like $document can also be of type array; however, ONGR\TranslationsBundle\...ager::updateTimestamp() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
165
    }
166
167
    /**
168
     * Removes message from document based on options.
169
     *
170
     * @param object $document
171
     * @param array  $options
172
     */
173
    private function deleteObject($document, $options)
174
    {
175
        $accessor = $this->getAccessor();
176
        $objects = $accessor->getValue($document, $options['name']);
177
        // TODO: refactor after ESB gives easy way to access object!
178
        $objects = iterator_to_array($objects);
179
180
        $key = $this->findObject($objects, $options['findBy']);
0 ignored issues
show
Documentation introduced by
$objects is of type array, but the function expects a object<Iterator>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
181
182
        if ($key >= 0) {
183
            unset($objects[$key]);
184
            $accessor->setValue($document, $options['name'], $objects);
185
        }
186
    }
187
188
    /**
189
     * Edits message from document based on options.
190
     *
191
     * @param object $document
192
     * @param array  $options
193
     */
194
    private function editObject($document, $options)
195
    {
196
        $accessor = $this->getAccessor();
197
        $objects = $accessor->getValue($document, $options['name']);
198
199
        // TODO: refactor after ESB gives easy way to access object!
200
        $objects = iterator_to_array($objects);
201
202
        if ($objects === null) {
203
            $this->addObject($document, $options);
204
        } else {
205
            $key = $this->findObject($objects, $options['findBy']);
0 ignored issues
show
Documentation introduced by
$objects is of type array, but the function expects a object<Iterator>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
206
207
            if ($key < 0) {
208
                $this->addObject($document, $options);
209
            } else {
210
                $this->setObjectProperties($objects[$key], $options['properties']);
211
                $this->updateTimestamp($objects[$key]);
212
                $this->updateTimestamp($document);
213
                $accessor->setValue($document, $options['name'], $objects);
214
            }
215
        }
216
    }
217
218
    /**
219
     * Finds object by property and its value from iterator and returns key.
220
     *
221
     * @param \Iterator $objects
222
     * @param array     $options
223
     *
224
     * @return int
225
     */
226
    private function findObject($objects, $options)
227
    {
228
        foreach ($objects as $key => $object) {
229
            $fit = true;
230
231
            foreach ($options as $property => $value) {
232
                if ($this->getAccessor()->getValue($object, $property) !== $value) {
233
                    $fit = false;
234
                    break;
235
                }
236
            }
237
238
            if ($fit) {
239
                return $key;
240
            }
241
        }
242
243
        return -1;
244
    }
245
246
    /**
247
     * Parses http request content from json to array.
248
     *
249
     * @param Request $request Http request object.
250
     *
251
     * @return array
252
     *
253
     * @throws BadRequestHttpException
254
     */
255 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...
256
    {
257
        $content = json_decode($request->getContent(), true);
258
259
        if (empty($content)) {
260
            throw new BadRequestHttpException('No content found.');
261
        }
262
263
        return $content;
264
    }
265
266
    /**
267
     * Commits document into elasticsearch client.
268
     *
269
     * @param object $document
270
     */
271
    private function commitTranslation($document)
272
    {
273
        $this->repository->getManager()->persist($document);
274
        $this->repository->getManager()->commit();
275
    }
276
277
    /**
278
     * Returns translation from elasticsearch.
279
     *
280
     * @param string $id
281
     *
282
     * @return object
283
     *
284
     * @throws BadRequestHttpException
285
     */
286
    private function getTranslation($id)
287
    {
288
        try {
289
            $document = $this->repository->find($id);
290
        } catch (Missing404Exception $e) {
291
            $document = null;
292
        }
293
294
        if ($document === null) {
295
            throw new BadRequestHttpException('Invalid translation Id.');
296
        }
297
298
        return $document;
299
    }
300
301
    /**
302
     * Returns property accessor instance.
303
     *
304
     * @return PropertyAccessorInterface
305
     */
306
    private function getAccessor()
307
    {
308
        if (!$this->accessor) {
309
            $this->accessor = PropertyAccess::createPropertyAccessorBuilder()
310
                ->enableExceptionOnInvalidIndex()
311
                ->enableMagicCall()
312
                ->getPropertyAccessor();
313
        }
314
315
        return $this->accessor;
316
    }
317
318
    /**
319
     * Sets `updated_at` property.
320
     *
321
     * @param object $object
322
     */
323
    private function updateTimestamp($object)
324
    {
325
        $accessor = $this->getAccessor();
326
327
        if ($accessor->isWritable($object, 'updated_at')) {
328
            $accessor->setValue($object, 'updated_at', new \DateTime());
329
        }
330
    }
331
332
    /**
333
     * Sets object properties into provided object.
334
     *
335
     * @param object $object     Object to set properties into.
336
     * @param array  $properties Array of properties to set.
337
     */
338
    private function setObjectProperties($object, $properties)
339
    {
340
        foreach ($properties as $property => $value) {
341
            $this->getAccessor()->setValue($object, $property, $value);
342
        }
343
    }
344
}
345