Completed
Push — master ( 39043c...2277fd )
by Yonel Ceruto
08:17 queued 31s
created

BreadcrumbsBuilder::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the BreadcrumbsBundle.
5
 *
6
 * (c) Yonel Ceruto <[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 Yceruto\Bundle\BreadcrumbsBundle;
13
14
use Symfony\Bundle\FrameworkBundle\Routing\Router;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\RequestStack;
17
use Symfony\Component\Routing\Matcher\TraceableUrlMatcher;
18
19
/**
20
 * This builder works in 2 modes:
21
 *
22
 *  * 2.3 compatibility mode where you must call setRequest whenever the Request changes.
23
 *  * 2.4+ mode where you must pass a RequestStack instance in the constructor.
24
 *
25
 * @author Yonel Ceruto <[email protected]>
26
 */
27
class BreadcrumbsBuilder
28
{
29
    /**
30
     * @var RequestStack
31
     */
32
    private $requestStack;
33
34
    /**
35
     * @var Request
36
     */
37
    private $request;
38
39
    /**
40
     * @var Router
41
     */
42
    private $router;
43
44
    /**
45
     * @var TraceableUrlMatcher
46
     */
47
    private $matcher;
48
49
    public function __construct(Router $router, RequestStack $requestStack = null)
50
    {
51
        $this->router = $router;
52
        $this->requestStack = $requestStack;
53
    }
54
55
    /**
56
     * BC with SF 2.3
57
     *
58
     * @param Request|null $request
59
     */
60
    public function setRequest(Request $request = null)
61
    {
62
        $this->request = $request;
63
    }
64
65
    /**
66
     * Create a empty breadcrumb
67
     *
68
     * @return Breadcrumbs
69
     */
70
    public function create()
71
    {
72
        return new Breadcrumbs();
73
    }
74
75
    /**
76
     * Create a breadcrumb through current request path
77
     *
78
     * @return Breadcrumbs
79
     */
80
    public function createFromRequest()
81
    {
82
        if (empty($this->matcher)) {
83
            $this->matcher = new TraceableUrlMatcher($this->router->getRouteCollection(), $this->router->getContext());
84
        }
85
86
        $breadcrumbs = new Breadcrumbs();
87
88
        $parent = null;
89
        $paths = $this->getBreadcrumbsPaths();
90
        foreach ($paths as $path) {
91
            if ($node = $this->createBreadcrumbsNode($path, $parent)) {
92
                $breadcrumbs->addNode($node);
93
                $parent = $path;
94
            }
95
        }
96
97
        return $breadcrumbs;
98
    }
99
100
    /**
101
     * Get all breadcrumbs paths from current request path
102
     *
103
     * @return array of string
104
     */
105
    private function getBreadcrumbsPaths()
106
    {
107
        $parts = array();
108
        $pathInfo = trim($this->getRequest()->getPathInfo(), '/');
109
110
        if ($pathInfo) {
111
            $parts = explode('/', $pathInfo);
112
        }
113
114
        array_unshift($parts, '/');
115
116
        $path = '';
117
        $paths = array();
118
        foreach ($parts as $part) {
119
            $path .= $part;
120
            $paths[] = $path;
121
122
            if ('/' !== $part) {
123
                $path .= '/';
124
                $paths[] = $path;
125
            }
126
        }
127
128
        return $paths;
129
    }
130
131
    /**
132
     * Create a breadcrumbs node from path
133
     *
134
     * @param string $path
135
     * @param string $parent
136
     *
137
     * @return BreadcrumbsNode|bool
138
     */
139
    private function createBreadcrumbsNode($path, $parent)
140
    {
141
        // use $baseUrl for no prod environments e.g dev 'app_dev.php'
142
        $baseUrl = $this->getRequest()->getBaseUrl();
143
144
        $traces = $this->matcher->getTraces($path);
145
        foreach ($traces as $trace) {
146
            if (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) {
147
                $label = $this->getLabel($path, $parent, $trace['name']);
148
149
                $node = new BreadcrumbsNode();
150
                $node->setLabel($label);
151
                $node->setPath($baseUrl.$path);
152
153
                return $node;
154
            }
155
        }
156
157
        return false;
158
    }
159
160
    private function getRequest()
161
    {
162
        return $this->requestStack ? $this->requestStack->getCurrentRequest() : $this->request;
163
    }
164
165
    /**
166
     * Get label
167
     *
168
     * @param $path
169
     * @param $parent
170
     * @param $name
171
     *
172
     * @return string
173
     */
174
    private function getLabel($path, $parent, $name)
175
    {
176
        $route = $this->router->getRouteCollection()->get($name);
177
178
        // get label through settings
179
        $label = $route->getDefault('breadcrumbs_label');
180
181
        if (empty($label)) {
182
            // get label through path
183
            $compiledRoute = $route->compile();
184
            $vars = $compiledRoute->getVariables();
185
186
            if (empty($vars)) {
187
                $label = substr($path, strlen($parent));
188
            } elseif (preg_match($compiledRoute->getRegex(), $path, $match)) {
189
                $label = $match[end($vars)];
190
            }
191
            $label = trim(preg_replace('[\W|_]', ' ', $label));
192
193
            if (is_numeric($label)) {
194
                $label = null;
195
            }
196
        }
197
198
        if (empty($label)) {
199
            // get label through route name
200
            $label = 'breadcrumbs.'.$name;
201
202
            return $label;
203
        }
204
205
        return $label;
206
    }
207
}
208