TranslatorController   A
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 343
Duplicated Lines 9.91 %

Coupling/Cohesion

Components 1
Dependencies 19

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 39
lcom 1
cbo 19
dl 34
loc 343
ccs 0
cts 198
cp 0
rs 9.28
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A indexAction() 0 23 3
B addAction() 17 49 7
B editAction() 17 57 10
A uploadFileAction() 0 23 4
A exportAction() 0 4 1
A editSearchAction() 0 20 2
A deleteAction() 0 12 3
A setAdminListConfigurator() 0 4 1
A getAdminListConfigurator() 0 10 2
B inlineEditAction() 0 44 6

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Kunstmaan\TranslatorBundle\Controller;
4
5
use Doctrine\ORM\EntityManager;
6
use Kunstmaan\AdminBundle\FlashMessages\FlashTypes;
7
use Kunstmaan\AdminListBundle\AdminList\AdminList;
8
use Kunstmaan\AdminListBundle\AdminList\Configurator\AbstractAdminListConfigurator;
9
use Kunstmaan\AdminListBundle\Controller\AdminListController;
10
use Kunstmaan\TranslatorBundle\AdminList\TranslationAdminListConfigurator;
11
use Kunstmaan\TranslatorBundle\Entity\Translation;
12
use Kunstmaan\TranslatorBundle\Form\TranslationAdminType;
13
use Kunstmaan\TranslatorBundle\Form\TranslationsFileUploadType;
14
use Symfony\Component\Routing\Annotation\Route;
15
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
16
use Symfony\Component\Form\FormBuilderInterface;
17
use Symfony\Component\Form\FormError;
18
use Symfony\Component\HttpFoundation\JsonResponse;
19
use Symfony\Component\HttpFoundation\RedirectResponse;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
23
use Symfony\Component\Translation\TranslatorInterface;
24
25
class TranslatorController extends AdminListController
26
{
27
    /**
28
     * @var AbstractAdminListConfigurator
29
     */
30
    private $adminListConfigurator;
31
32
    /**
33
     * @Route("/", name="KunstmaanTranslatorBundle_settings_translations")
34
     * @Template("@KunstmaanTranslator/Translator/list.html.twig")
35
     *
36
     * @param \Symfony\Component\HttpFoundation\Request $request
37
     *
38
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,AdminList|A...tAdminListConfigurator>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
39
     */
40
    public function indexAction(Request $request)
41
    {
42
        $configurator = $this->getAdminListConfigurator();
43
44
        /* @var AdminList $adminList */
45
        $adminList = $this->container->get('kunstmaan_adminlist.factory')->createList($configurator);
46
        $adminList->bindRequest($request);
47
48
        $cacheFresh = $this->container->get('kunstmaan_translator.service.translator.cache_validator')->isCacheFresh();
49
        $debugMode = $this->container->getParameter('kuma_translator.debug') === true;
50
51
        if (!$cacheFresh && !$debugMode) {
52
            $this->addFlash(
53
                FlashTypes::INFO,
54
                $this->container->get('translator')->trans('settings.translator.not_live_warning')
55
            );
56
        }
57
58
        return [
59
            'adminlist' => $adminList,
60
            'adminlistconfigurator' => $configurator,
61
        ];
62
    }
63
64
    /**
65
     * @param Request $request
66
     * @param string  $keyword
67
     * @param string  $domain
68
     * @param string  $locale
69
     *
70
     * @return array|RedirectResponse
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use RedirectResponse|array<s...tAdminListConfigurator>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
71
     *
72
     * @throws \Doctrine\ORM\OptimisticLockException
73
     *
74
     * @Route("/add", name="KunstmaanTranslatorBundle_settings_translations_add", methods={"GET", "POST"})
75
     * @Template("@KunstmaanTranslator/Translator/addTranslation.html.twig")
76
     */
77
    public function addAction(Request $request, $keyword = '', $domain = '', $locale = '')
78
    {
79
        /* @var EntityManager $em */
80
        $em = $this->getDoctrine()->getManager();
81
        $configurator = $this->getAdminListConfigurator();
82
        $translator = $this->container->get('translator');
83
84
        $translation = new \Kunstmaan\TranslatorBundle\Model\Translation();
85
        $managedLocales = $this->container->getParameter('kuma_translator.managed_locales');
86
        foreach ($managedLocales as $managedLocale) {
87
            $translation->addText($managedLocale, '');
88
        }
89
90
        $form = $this->createForm(TranslationAdminType::class, $translation, ['csrf_token_id' => 'add']);
91
        if ($request->getMethod() === Request::METHOD_POST) {
92
            $form->handleRequest($request);
93
94
            // Fetch form data
95
            $data = $form->getData();
96
            if (!$em->getRepository(Translation::class)->isUnique($data)) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method isUnique() does only exist in the following implementations of said interface: Kunstmaan\TranslatorBund...y\TranslationRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
97
                $error = new FormError($translator->trans('translator.translation_not_unique'));
98
                $form->get('domain')->addError($error);
99
                $form->get('keyword')->addError($error);
100
            }
101
102 View Code Duplication
            if ($form->isSubmitted() && $form->isValid()) {
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...
103
                // Create translation
104
                $em->getRepository(Translation::class)->createTranslations($data);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createTranslations() does only exist in the following implementations of said interface: Kunstmaan\TranslatorBund...y\TranslationRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
105
                $em->flush();
106
107
                $this->addFlash(
108
                    FlashTypes::SUCCESS,
109
                    $this->container->get('translator')->trans('settings.translator.succesful_added')
110
                );
111
112
                $indexUrl = $configurator->getIndexUrl();
113
114
                return new RedirectResponse($this->generateUrl(
115
                    $indexUrl['path'],
116
                    isset($indexUrl['params']) ? $indexUrl['params'] : []
117
                ));
118
            }
119
        }
120
121
        return [
122
            'form' => $form->createView(),
123
            'adminlistconfigurator' => $configurator,
124
        ];
125
    }
126
127
    /**
128
     * The edit action
129
     *
130
     * @Route("/{id}/edit", requirements={"id" = "\d+"}, name="KunstmaanTranslatorBundle_settings_translations_edit", methods={"GET", "POST"})
131
     * @Template("@KunstmaanTranslator/Translator/editTranslation.html.twig")
132
     *
133
     * @param \Symfony\Component\HttpFoundation\Request $request
134
     * @param $id
135
     *
136
     * @throws \InvalidArgumentException
137
     *
138
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use RedirectResponse|array<s...tAdminListConfigurator>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
139
     */
140
    public function editAction(Request $request, $id)
141
    {
142
        $em = $this->getDoctrine()->getManager();
143
        $configurator = $this->getAdminListConfigurator();
144
145
        $translations = $em->getRepository(Translation::class)->findBy(['translationId' => $id]);
146
        if (\count($translations) < 1) {
147
            throw new \InvalidArgumentException('No existing translations found for this id');
148
        }
149
150
        $translation = new \Kunstmaan\TranslatorBundle\Model\Translation();
151
        $translation->setDomain($translations[0]->getDomain());
152
        $translation->setKeyword($translations[0]->getKeyword());
153
        $locales = $this->container->getParameter('kuma_translator.managed_locales');
154
        foreach ($locales as $locale) {
155
            $found = false;
156
            foreach ($translations as $t) {
157
                if ($locale == $t->getLocale()) {
158
                    $translation->addText($locale, $t->getText(), $t->getId());
159
                    $found = true;
160
                }
161
            }
162
            if (!$found) {
163
                $translation->addText($locale, '');
164
            }
165
        }
166
167
        $form = $this->createForm(TranslationAdminType::class, $translation, ['intention' => 'edit']);
168
169
        if ($request->getMethod() === Request::METHOD_POST) {
170
            $form->handleRequest($request);
171
172 View Code Duplication
            if ($form->isSubmitted() && $form->isValid()) {
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...
173
                // Update translations
174
                $em->getRepository(Translation::class)->updateTranslations($translation, $id);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method updateTranslations() does only exist in the following implementations of said interface: Kunstmaan\TranslatorBund...y\TranslationRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
175
                $em->flush();
176
177
                $this->addFlash(
178
                    FlashTypes::SUCCESS,
179
                    $this->container->get('translator')->trans('settings.translator.succesful_edited')
180
                );
181
182
                $indexUrl = $configurator->getIndexUrl();
183
184
                return new RedirectResponse($this->generateUrl(
185
                    $indexUrl['path'],
186
                    isset($indexUrl['params']) ? $indexUrl['params'] : []
187
                ));
188
            }
189
        }
190
191
        return [
192
            'form' => $form->createView(),
193
            'translation' => $translation,
194
            'adminlistconfigurator' => $configurator,
195
        ];
196
    }
197
198
    /**
199
     * @Route("upload", name="KunstmaanTranslatorBundle_settings_translations_upload", methods={"GET", "POST"})
200
     * @Template("@KunstmaanTranslator/Translator/addTranslation.html.twig")
201
     *
202
     * @param Request $request
203
     *
204
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,\Symfony\Co...tAdminListConfigurator>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
205
     */
206
    public function uploadFileAction(Request $request)
207
    {
208
        /** @var FormBuilderInterface $uploadForm */
209
        $form = $this->createForm(TranslationsFileUploadType::class);
210
        $configurator = $this->getAdminListConfigurator();
211
212
        if (Request::METHOD_POST === $request->getMethod()) {
213
            $form->handleRequest($request);
214
            if ($form->isSubmitted() && $form->isValid()) {
215
                $locales = $this->getParameter('kuma_translator.managed_locales');
216
                $data = $form->getData();
217
                $file = $data['file'];
218
                $force = $data['force'];
219
                $imported = $this->container->get('kunstmaan_translator.service.importer.importer')->importFromSpreadsheet($file, $locales, $force);
220
                $this->addFlash(FlashTypes::SUCCESS, sprintf('Translation imported: %d', $imported));
221
            }
222
        }
223
224
        return [
225
            'form' => $form->createView(),
226
            'adminlistconfigurator' => $configurator,
227
        ];
228
    }
229
230
    /**
231
     * The export action
232
     *
233
     * @param string $_format
234
     *
235
     * @Route("/export.{_format}", requirements={"_format" = "csv|ods|xlsx"}, name="KunstmaanTranslatorBundle_settings_translations_export", methods={"GET", "POST"})
236
     *
237
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be Response?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
238
     */
239
    public function exportAction(Request $request, $_format)
240
    {
241
        return parent::doExportAction($this->getAdminListConfigurator(), $_format, $request);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (doExportAction() instead of exportAction()). Are you sure this is correct? If so, you might want to change this to $this->doExportAction().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
242
    }
243
244
    /**
245
     * @param $domain
246
     * @param $locale
247
     * @param $keyword
248
     *
249
     * @return RedirectResponse
250
     */
251
    public function editSearchAction($domain, $locale, $keyword)
252
    {
253
        $configurator = $this->getAdminListConfigurator();
254
        $em = $this->getDoctrine()->getManager();
255
        $translation = $em->getRepository(Translation::class)->findOneBy(
256
            ['domain' => $domain, 'keyword' => $keyword, 'locale' => $locale]
257
        );
258
259
        if ($translation === null) {
260
            $addUrl = $configurator->getAddUrlFor(
261
                ['domain' => $domain, 'keyword' => $keyword, 'locale' => $locale]
262
            );
263
264
            return new RedirectResponse($this->generateUrl($addUrl['path'], $addUrl['params']));
0 ignored issues
show
Documentation introduced by
$addUrl['path'] is of type array<string,string|arra...ing","params":"array"}>, but the function expects a string.

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...
265
        }
266
267
        $editUrl = $configurator->getEditUrlFor(['id' => $translation->getId()]);
268
269
        return new RedirectResponse($this->generateUrl($editUrl['path'], $editUrl['params']));
270
    }
271
272
    /**
273
     * @param $id
274
     *
275
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
276
     *
277
     * @throws NotFoundHttpException
278
     *
279
     * @Route("/{id}/delete", requirements={"id" = "\d+"}, name="KunstmaanTranslatorBundle_settings_translations_delete", methods={"GET", "POST"})
280
     */
281
    public function deleteAction(Request $request, $id)
282
    {
283
        /* @var EntityManager $em */
284
        $em = $this->getDoctrine()->getManager();
285
286
        $indexUrl = $this->getAdminListConfigurator()->getIndexUrl();
287
        if ($request->isMethod('POST')) {
288
            $em->getRepository(Translation::class)->removeTranslations($id);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method removeTranslations() does only exist in the following implementations of said interface: Kunstmaan\TranslatorBund...y\TranslationRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
289
        }
290
291
        return new RedirectResponse($this->generateUrl($indexUrl['path'], isset($indexUrl['params']) ? $indexUrl['params'] : []));
292
    }
293
294
    /**
295
     * @param $adminListConfigurator
296
     */
297
    public function setAdminListConfigurator($adminListConfigurator)
298
    {
299
        $this->adminListConfigurator = $adminListConfigurator;
300
    }
301
302
    /**
303
     * @return AbstractAdminListConfigurator
304
     */
305
    public function getAdminListConfigurator()
306
    {
307
        $locales = $this->container->getParameter('kuma_translator.managed_locales');
308
309
        if (!isset($this->adminListConfigurator)) {
310
            $this->adminListConfigurator = new TranslationAdminListConfigurator($this->getDoctrine()->getConnection(), $locales);
311
        }
312
313
        return $this->adminListConfigurator;
314
    }
315
316
    /**
317
     * @param Request $request
318
     *
319
     * @return JsonResponse|Response
320
     *
321
     * @Route("/inline-edit", name="KunstmaanTranslatorBundle_settings_translations_inline_edit", methods={"POST"})
322
     */
323
    public function inlineEditAction(Request $request)
324
    {
325
        $values = $request->request->all();
326
327
        $adminListConfigurator = $this->getAdminListConfigurator();
328
        if (!$adminListConfigurator->canEditInline($values)) {
0 ignored issues
show
Bug introduced by
The method canEditInline() does not exist on Kunstmaan\AdminListBundl...ctAdminListConfigurator. Did you maybe mean canEdit()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
329
            throw $this->createAccessDeniedException('Not allowed to edit this translation');
330
        }
331
332
        $id = isset($values['pk']) ? (int) $values['pk'] : 0;
333
        $em = $this->getDoctrine()->getManager();
334
        /**
335
         * @var TranslatorInterface
336
         */
337
        $translator = $this->container->get('translator');
338
339
        try {
340
            if ($id !== 0) {
341
                // Find existing translation
342
                $translation = $em->getRepository(Translation::class)->find($id);
343
344
                if (\is_null($translation)) {
345
                    return new Response($translator->trans('translator.translator.invalid_translation'), 500);
346
                }
347
            } else {
348
                // Create new translation
349
                $translation = new Translation();
350
                $translation->setDomain($values['domain']);
351
                $translation->setKeyword($values['keyword']);
352
                $translation->setLocale($values['locale']);
353
                $translation->setTranslationId($values['translationId']);
354
            }
355
            $translation->setText($values['value']);
356
            $em->persist($translation);
357
            $em->flush();
358
359
            return new JsonResponse([
360
                'success' => true,
361
                'uid' => $translation->getId(),
362
            ], 200);
363
        } catch (\Exception $e) {
364
            return new Response($translator->trans('translator.translator.fatal_error_occurred'), 500);
365
        }
366
    }
367
}
368