Completed
Push — master ( d1535f...93b502 )
by Leny
06:44
created

LinkExtension   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 250
Duplicated Lines 6.4 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 36
c 5
b 0
f 1
lcom 1
cbo 10
dl 16
loc 250
rs 8.8

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 1
A getFunctions() 0 9 1
C victoireLinkUrl() 0 51 13
C victoireLink() 8 49 11
B victoireMenuLink() 8 23 5
A victoireBusinessLink() 0 23 2
A addAttr() 0 11 2
A getName() 0 4 1

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 Victoire\Bundle\WidgetBundle\Twig;
4
5
use Doctrine\ORM\EntityManager;
6
use Symfony\Bundle\FrameworkBundle\Routing\Router;
7
use Symfony\Component\HttpFoundation\RequestStack;
8
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
9
use Victoire\Bundle\BusinessEntityBundle\Helper\BusinessEntityHelper;
10
use Victoire\Bundle\BusinessPageBundle\Helper\BusinessPageHelper;
11
use Victoire\Bundle\PageBundle\Helper\PageHelper;
12
use Victoire\Bundle\ViewReferenceBundle\ViewReference\ViewReference;
13
14
/**
15
 * Twig extension for rendering a link.
16
 */
17
class LinkExtension extends \Twig_Extension
18
{
19
    protected $router;
20
    protected $analytics;
21
    protected $businessEntityHelper; // @victoire_business_page.business_entity_helper
22
    protected $BusinessPageHelper; // @victoire_business_page.business_page_helper
23
    protected $pageHelper;
24
    protected $em; // @doctrine.orm.entity_manager
25
26
    public function __construct(
27
        Router $router,
28
        RequestStack $requestStack,
29
        $analytics,
30
        BusinessEntityHelper $businessEntityHelper,
31
        BusinessPageHelper $BusinessPageHelper,
32
        PageHelper $pageHelper,
33
        EntityManager $em
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $em. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
34
    ) {
35
        $this->router = $router;
36
        $this->request = $requestStack->getCurrentRequest();
0 ignored issues
show
Bug introduced by
The property request does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
37
        $this->analytics = $analytics;
38
        $this->businessEntityHelper = $businessEntityHelper;
39
        $this->BusinessPageHelper = $BusinessPageHelper;
40
        $this->pageHelper = $pageHelper;
41
        $this->em = $em;
42
    }
43
44
    /**
45
     * Returns a list of functions to add to the existing list.
46
     *
47
     * @return \Twig_SimpleFunction[] An array of functions
48
     */
49
    public function getFunctions()
50
    {
51
        return [
52
            new \Twig_SimpleFunction('vic_link_url', [$this, 'victoireLinkUrl']),
53
            new \Twig_SimpleFunction('vic_link', [$this, 'victoireLink'], ['is_safe' => ['html']]),
54
            new \Twig_SimpleFunction('vic_menu_link', [$this, 'victoireMenuLink'], ['is_safe' => ['html']]),
55
            new \Twig_SimpleFunction('vic_business_link', [$this, 'victoireBusinessLink'], ['is_safe' => ['html']]),
56
        ];
57
    }
58
59
    /**
60
     * Generate the complete link (with the a tag).
61
     *
62
     * @param array  $parameters   The link parameters (go to LinkTrait to have the list)
63
     * @param string $avoidRefresh Do we have to refresh or not ?
64
     * @param array  $url          Fallback url
65
     *
66
     * @return string
67
     */
68
    public function victoireLinkUrl($parameters, $avoidRefresh = true, $url = '#')
69
    {
70
        $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH;
71
72
        $viewReference = isset($parameters['viewReference']) ? $parameters['viewReference'] : null;
73
        switch ($parameters['linkType']) {
74
            case 'viewReference':
75
                if ($viewReference instanceof ViewReference) {
76
                    $viewReference = $viewReference->getId();
77
                }
78
79
                if (isset($viewReferencePage) && $viewReferencePage) {
0 ignored issues
show
Bug introduced by
The variable $viewReferencePage does not exist. Did you mean $viewReference?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
80
                    $page = $viewReferencePage;
81
                } else {
82
                    $page = $this->pageHelper->findPageByParameters(['id' => $viewReference]);
83
                }
84
85
                $linkUrl = $this->router->generate(
86
                    'victoire_core_page_show', [
87
                        '_locale' => $page->getLocale(),
88
                        'url'     => $page->getReference()->getUrl(),
89
                    ],
90
                    $referenceType
91
                );
92
                if ($this->request->getRequestUri() != $linkUrl || !$avoidRefresh) {
93
                    $url = $linkUrl;
94
                }
95
                break;
96
            case 'route':
97
                $url = $this->router->generate($parameters['route'], $parameters['routeParameters'], $referenceType);
98
                break;
99
            case 'attachedWidget':
100
                $attachedWidget = $parameters['attachedWidget'];
101
                //fallback when a widget is deleted cascading the relation as null (widget_id = null)
102
                if ($attachedWidget && method_exists($attachedWidget->getView(), 'getUrl')) {
103
104
                    //create base url
105
                    $url = $this->router->generate('victoire_core_page_show', ['_locale' => $attachedWidget->getView()->getLocale(), 'url' => $attachedWidget->getView()->getUrl()], $referenceType);
106
107
                    //If widget in the same view
108
                    if (rtrim($this->request->getRequestUri(), '/') == rtrim($url, '/')) {
109
                        $url = '';
110
                    }
111
                    //Add anchor part
112
                    $url .= '#vic-widget-'.$attachedWidget->getId().'-container-anchor';
113
                }
114
                break;
115
        }
116
117
        return $url;
118
    }
119
120
    /**
121
     * Generate the complete link (with the a tag).
122
     *
123
     * @param array  $parameters The link parameters (go to LinkTrait to have the list)
124
     * @param string $label      link label
125
     * @param array  $attr       custom attributes
126
     *
127
     * @return string
128
     */
129
    public function victoireLink($parameters, $label, $attr = [], $currentClass = 'active', $url = '#')
130
    {
131
        $referenceLink = UrlGeneratorInterface::ABSOLUTE_PATH;
132
        $analyticsTrackCode = $parameters['analyticsTrackCode'];
133
        $attachedWidget = $parameters['attachedWidget'];
134
135
        if ($parameters['linkType'] == 'attachedWidget' && $attachedWidget && method_exists($attachedWidget->getView(), 'getUrl')) {
136
            $viewUrl = $this->router->generate('victoire_core_page_show', ['_locale' => $attachedWidget->getView()->getLocale(), 'url' => $attachedWidget->getView()->getUrl()], $referenceLink);
137
            if (rtrim($this->request->getRequestUri(), '/') == rtrim($viewUrl, '/')) {
138
                $attr['data-scroll'] = 'smooth';
139
            }
140
        }
141
142
        //Avoid to refresh page if not needed
143
        if ($this->request->getRequestUri() == $this->victoireLinkUrl($parameters, false)) {
144
            $this->addAttr('class', $currentClass, $attr);
145
        }
146
147
        //Build the target attribute
148
        if ($parameters['target'] == 'ajax-modal') {
149
            $attr['data-toggle'] = 'ajax-modal';
150
        } elseif ($parameters['target'] == '') {
151
            $attr['target'] = '_parent';
152
        } else {
153
            $attr['target'] = $parameters['target'];
154
        }
155
156
        //Add the analytics tracking code attribute
157
        if (isset($analyticsTrackCode)) {
158
            $this->addAttr('onclick', $analyticsTrackCode, $attr);
159
        }
160
161
        //Assemble and prepare attributes
162
        $attributes = [];
163 View Code Duplication
        foreach ($attr as $key => $_attr) {
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...
164
            if (is_array($_attr)) {
165
                $attr = implode($_attr, ' ');
166
            } else {
167
                $attr = $_attr;
168
            }
169
            $attributes[] = $key.'="'.$attr.'"';
170
        }
171
172
        $url = $this->victoireLinkUrl($parameters, true, $url);
173
        //Creates a new twig environment
174
        $twigEnv = new \Twig_Environment(new \Twig_Loader_String());
0 ignored issues
show
Deprecated Code introduced by
The class Twig_Loader_String has been deprecated with message: since 1.18.1 (to be removed in 2.0)

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
175
176
        return $twigEnv->render('{{ link|raw }}', ['link' => '<a href="'.$url.'" '.implode($attributes, ' ').'>'.$label.'</a>']);
177
    }
178
179
    /**
180
     * Generate the complete menu link item (with the li tag).
181
     *
182
     * @param array  $parameters The link parameters (go to LinkTrait to have the list)
183
     * @param string $label      link label
184
     * @param array  $attr       custom attributes
185
     *
186
     * @return string
187
     */
188
    public function victoireMenuLink($parameters, $label, $attr = [])
189
    {
190
        $linkAttr = [];
191
        //is the link is active
192
        if ($this->request->getRequestUri() == $this->victoireLinkUrl($parameters, false)) {
193
            if (!isset($attr['class'])) {
194
                $linkAttr['class'] = '';
195
            }
196
            $linkAttr['class'] .= 'active'; //avoid to refresh page when not needed
197
        }
198
199
        $linkAttributes = [];
200 View Code Duplication
        foreach ($linkAttr as $key => $_attr) {
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...
201
            if (is_array($_attr)) {
202
                $linkAttr = implode($_attr, ' ');
203
            } else {
204
                $linkAttr = $_attr;
205
            }
206
            $linkAttributes[] = $key.'="'.$linkAttr.'"';
207
        }
208
209
        return '<li '.implode($linkAttributes, ' ').'>'.$this->victoireLink($parameters, $label, $attr, false, '#top').'</li>';
0 ignored issues
show
Documentation introduced by
false is of type boolean, 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...
210
    }
211
212
    public function victoireBusinessLink($businessEntityInstance, $templateId = null, $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH)
213
    {
214
        if (!$templateId) {
215
            $templateId = $this->BusinessPageHelper
216
                ->guessBestPatternIdForEntity(new \ReflectionClass($businessEntityInstance), $businessEntityInstance->getId(), $this->em);
217
        }
218
219
        $page = $this->pageHelper->findPageByParameters([
220
            'templateId' => $templateId,
221
            'entityId'   => $businessEntityInstance->getId(),
222
        ]);
223
224
        $parameters = [
225
            'linkType'        => 'route',
226
            'route'           => 'victoire_core_page_show',
227
            'routeParameters' => [
228
                'url' => $page->getReference()->getUrl(),
229
            ],
230
            'referenceType' => $referenceType,
231
        ];
232
233
        return $this->victoireLinkUrl($parameters);
234
    }
235
236
    /**
237
     * Add a given attribute to given attributes.
238
     *
239
     * @param string $label
240
     * @param string $value
241
     * @param array  $attr  The current attributes array
242
     *
243
     * @return LinkExtension
244
     **/
245
    protected function addAttr($label, $value, &$attr)
246
    {
247
        if (!isset($attr[$label])) {
248
            $attr[$label] = '';
249
        } else {
250
            $attr[$label] .= ' ';
251
        }
252
        $attr[$label] .= $value;
253
254
        return $this;
255
    }
256
257
    /**
258
     * Returns the name of the extension.
259
     *
260
     * @return string The extension name
261
     */
262
    public function getName()
263
    {
264
        return 'victoire_link_extension';
265
    }
266
}
267