]> Raphaël G. Git Repositories - userbundle/blob - Controller/AbstractController.php
Add checker, limit, page and security members
[userbundle] / Controller / AbstractController.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys UserBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
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 Rapsys\UserBundle\Controller;
13
14 use Doctrine\ORM\EntityManagerInterface;
15 use Doctrine\Persistence\ManagerRegistry;
16 use Psr\Log\LoggerInterface;
17 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController;
18 use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
19 use Symfony\Bundle\SecurityBundle\Security;
20 use Symfony\Component\DependencyInjection\ContainerInterface;
21 use Symfony\Component\HttpFoundation\Request;
22 use Symfony\Component\HttpFoundation\RequestStack;
23 use Symfony\Component\HttpFoundation\Response;
24 use Symfony\Component\Mailer\MailerInterface;
25 use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
26 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
27 use Symfony\Component\Routing\RouterInterface;
28 use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
29 use Symfony\Component\Security\Core\User\UserInterface;
30 use Symfony\Contracts\Service\ServiceSubscriberInterface;
31 use Symfony\Contracts\Translation\TranslatorInterface;
32 use Twig\Environment;
33
34 use Rapsys\PackBundle\Util\SluggerUtil;
35
36 use Rapsys\UserBundle\RapsysUserBundle;
37
38 /**
39 * Provides common features needed in controllers.
40 *
41 * {@inheritdoc}
42 */
43 abstract class AbstractController extends BaseAbstractController implements ServiceSubscriberInterface {
44 /**
45 * Config array
46 */
47 protected array $config;
48
49 /**
50 * Context array
51 */
52 protected array $context;
53
54 /**
55 * Limit integer
56 */
57 protected int $limit;
58
59 /**
60 * Locale string
61 */
62 protected string $locale;
63
64 /**
65 * Page integer
66 */
67 protected int $page;
68
69 /**
70 * AuthorizationCheckerInterface instance
71 */
72 protected AuthorizationCheckerInterface $checker;
73
74 /**
75 * ManagerRegistry instance
76 */
77 protected ManagerRegistry $doctrine;
78
79 /**
80 * UserPasswordHasherInterface instance
81 */
82 protected UserPasswordHasherInterface $hasher;
83
84 /**
85 * LoggerInterface instance
86 */
87 protected LoggerInterface $logger;
88
89 /**
90 * MailerInterface instance
91 */
92 protected MailerInterface $mailer;
93
94 /**
95 * EntityManagerInterface instance
96 */
97 protected EntityManagerInterface $manager;
98
99 /**
100 * Request instance
101 */
102 protected Request $request;
103
104 /**
105 * Router instance
106 */
107 protected RouterInterface $router;
108
109 /**
110 * Security instance
111 */
112 protected Security $security;
113
114 /**
115 * Slugger util instance
116 */
117 protected SluggerUtil $slugger;
118
119 /**
120 * Translator instance
121 */
122 protected TranslatorInterface $translator;
123
124 /**
125 * Twig\Environment instance
126 */
127 protected Environment $twig;
128
129 /**
130 * Abstract constructor
131 *
132 * @param AuthorizationCheckerInterface $checker The checker instance
133 * @param ContainerInterface $container The container instance
134 * @param ManagerRegistry $doctrine The doctrine instance
135 * @param UserPasswordHasherInterface $hasher The password hasher instance
136 * @param LoggerInterface $logger The logger instance
137 * @param MailerInterface $mailer The mailer instance
138 * @param EntityManagerInterface $manager The manager instance
139 * @param RouterInterface $router The router instance
140 * @param Security $security The security instance
141 * @param SluggerUtil $slugger The slugger instance
142 * @param RequestStack $stack The stack instance
143 * @param TranslatorInterface $translator The translator instance
144 * @param Environment $twig The twig environment instance
145 * @param integer $limit The page limit
146 */
147 public function __construct(AuthorizationCheckerInterface $checker, ContainerInterface $container, ManagerRegistry $doctrine, UserPasswordHasherInterface $hasher, LoggerInterface $logger, MailerInterface $mailer, EntityManagerInterface $manager, RouterInterface $router, Security $security, SluggerUtil $slugger, RequestStack $stack, TranslatorInterface $translator, Environment $twig, int $limit = 5) {
148 //Retrieve config
149 $this->config = $container->getParameter(RapsysUserBundle::getAlias());
150
151 //Set checker
152 $this->checker = $checker;
153
154 //Set container
155 $this->container = $container;
156
157 //Set doctrine
158 $this->doctrine = $doctrine;
159
160 //Set hasher
161 $this->hasher = $hasher;
162
163 //Set logger
164 $this->logger = $logger;
165
166 //Set limit
167 $this->limit = $limit;
168
169 //Set mailer
170 $this->mailer = $mailer;
171
172 //Set manager
173 $this->manager = $manager;
174
175 //Set router
176 $this->router = $router;
177
178 //Set security
179 $this->security = $security;
180
181 //Set slugger
182 $this->slugger = $slugger;
183
184 //Set translator
185 $this->translator = $translator;
186
187 //Set twig
188 $this->twig = $twig;
189
190 //Get current request
191 $this->request = $stack->getCurrentRequest();
192
193 //Get current page
194 $this->page = (int) $this->request->query->get('page');
195
196 //With negative page
197 if ($this->page < 0) {
198 $this->page = 0;
199 }
200
201 //Get current locale
202 $this->locale = $this->request->getLocale();
203
204 //Set translate array
205 $translates = [];
206
207 //Look for keys to translate
208 if (!empty($this->config['translate'])) {
209 //Iterate on keys to translate
210 foreach($this->config['translate'] as $translate) {
211 //Set tmp
212 $tmp = null;
213
214 //Iterate on keys
215 foreach(array_reverse(explode('.', $translate)) as $curkey) {
216 $tmp = array_combine([$curkey], [$tmp]);
217 }
218
219 //Append tree
220 $translates = array_replace_recursive($translates, $tmp);
221 }
222 }
223
224 //Inject every requested route in view and mail context
225 foreach($this->config as $tag => $current) {
226 //Look for entry with route subkey
227 if (!empty($current['route'])) {
228 //Generate url for both view and mail
229 foreach(['view', 'mail'] as $view) {
230 //Check that context key is usable
231 if (isset($current[$view]['context']) && is_array($current[$view]['context'])) {
232 //Merge with global context
233 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config['context'], $this->config[$tag][$view]['context']);
234
235 //Process every routes
236 foreach($current['route'] as $route => $key) {
237 //With confirm route
238 if ($route == 'confirm') {
239 //Skip route as it requires some parameters
240 continue;
241 }
242
243 //Set value
244 $value = $this->router->generate(
245 $this->config['route'][$route]['name'],
246 $this->config['route'][$route]['context'],
247 //Generate absolute url for mails
248 $view=='mail'?UrlGeneratorInterface::ABSOLUTE_URL:UrlGeneratorInterface::ABSOLUTE_PATH
249 );
250
251 //Multi level key
252 if (strpos($key, '.') !== false) {
253 //Set tmp
254 $tmp = $value;
255
256 //Iterate on key
257 foreach(array_reverse(explode('.', $key)) as $curkey) {
258 $tmp = array_combine([$curkey], [$tmp]);
259 }
260
261 //Set value
262 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
263 //Single level key
264 } else {
265 //Set value
266 $this->config[$tag][$view]['context'][$key] = $value;
267 }
268 }
269
270 //Look for successful intersections
271 if (!empty(array_intersect_key($translates, $this->config[$tag][$view]['context']))) {
272 //Iterate on keys to translate
273 foreach($this->config['translate'] as $translate) {
274 //Set keys
275 $keys = explode('.', $translate);
276
277 //Set tmp
278 $tmp = $this->config[$tag][$view]['context'];
279
280 //Iterate on keys
281 foreach($keys as $curkey) {
282 //Without child key
283 if (!isset($tmp[$curkey])) {
284 //Skip to next key
285 continue(2);
286 }
287
288 //Get child key
289 $tmp = $tmp[$curkey];
290 }
291
292 //Translate tmp value
293 $tmp = $this->translator->trans($tmp);
294
295 //Iterate on keys
296 foreach(array_reverse($keys) as $curkey) {
297 //Set parent key
298 $tmp = array_combine([$curkey], [$tmp]);
299 }
300
301 //Set value
302 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
303 }
304 }
305
306 //With view context
307 if ($view == 'view') {
308 //Get context path
309 $pathInfo = $this->router->getContext()->getPathInfo();
310
311 //Iterate on locales excluding current one
312 foreach(($locales = array_keys($this->config['languages'])) as $locale) {
313 //Set titles
314 $titles = [];
315
316 //Iterate on other locales
317 foreach(array_diff($locales, [$locale]) as $other) {
318 $titles[$other] = $this->translator->trans($this->config['languages'][$locale], [], null, $other);
319 }
320
321 //Retrieve route matching path
322 $route = $this->router->match($pathInfo);
323
324 //Get route name
325 $name = $route['_route'];
326
327 //Unset route name
328 unset($route['_route']);
329
330 //With current locale
331 if ($locale == $this->locale) {
332 //Set locale locales context
333 $this->config[$tag][$view]['context']['head']['canonical'] = $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL);
334 } else {
335 //Set locale locales context
336 $this->config[$tag][$view]['context']['head']['alternates'][$locale] = [
337 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL),
338 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route),
339 'title' => implode('/', $titles),
340 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale)
341 ];
342 }
343
344 //Add shorter locale
345 if (empty($this->config[$tag][$view]['context']['head']['alternates'][$slocale = substr($locale, 0, 2)])) {
346 //Add shorter locale
347 $this->config[$tag][$view]['context']['head']['alternates'][$slocale] = [
348 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL),
349 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route),
350 'title' => implode('/', $titles),
351 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale)
352 ];
353 }
354 }
355 }
356 }
357 }
358 }
359 }
360 }
361
362 /**
363 * Renders a view
364 *
365 * {@inheritdoc}
366 */
367 protected function render(string $view, array $parameters = [], Response $response = null): Response {
368 //Create response when null
369 $response ??= new Response();
370
371 //With empty head locale
372 if (empty($parameters['head']['locale'])) {
373 //Set head locale
374 $parameters['head']['locale'] = $this->locale;
375 }
376
377 //With empty head title and section
378 if (empty($parameters['head']['title']) && !empty($parameters['section'])) {
379 //Set head title
380 $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['section'], $parameters['head']['site']]);
381 //With empty head title
382 } elseif (empty($parameters['head']['title'])) {
383 //Set head title
384 $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['head']['site']]);
385 }
386
387 //Call twig render method
388 $content = $this->twig->render($view, $parameters);
389
390 //Invalidate OK response on invalid form
391 if (200 === $response->getStatusCode()) {
392 foreach ($parameters as $v) {
393 if ($v instanceof FormInterface && $v->isSubmitted() && !$v->isValid()) {
394 $response->setStatusCode(422);
395 break;
396 }
397 }
398 }
399
400 //Store content in response
401 $response->setContent($content);
402
403 //Return response
404 return $response;
405 }
406
407 /**
408 * {@inheritdoc}
409 *
410 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
411 */
412 public static function getSubscribedServices(): array {
413 //Return subscribed services
414 return [
415 'doctrine' => ManagerRegistry::class,
416 'doctrine.orm.default_entity_manager' => EntityManagerInterface::class,
417 'logger' => LoggerInterface::class,
418 'mailer.mailer' => MailerInterface::class,
419 'rapsys_pack.slugger_util' => SluggerUtil::class,
420 'request_stack' => RequestStack::class,
421 'router' => RouterInterface::class,
422 'security.authorization_checker' => AuthorizationCheckerInterface::class,
423 'security' => Security::class,
424 'security.user_password_hasher' => UserPasswordHasherInterface::class,
425 'service_container' => ContainerInterface::class,
426 'translator' => TranslatorInterface::class
427 ];
428 }
429 }