1 <?php
declare(strict_types
=1);
4 * This file is part of the Rapsys UserBundle package.
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Rapsys\UserBundle\Controller
;
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
;
34 use Rapsys\PackBundle\Util\SluggerUtil
;
36 use Rapsys\UserBundle\RapsysUserBundle
;
39 * Provides common features needed in controllers.
43 abstract class AbstractController
extends BaseAbstractController
implements ServiceSubscriberInterface
{
47 protected array $config;
52 protected array $context;
62 protected string $locale;
70 * AuthorizationCheckerInterface instance
72 protected AuthorizationCheckerInterface
$checker;
75 * ManagerRegistry instance
77 protected ManagerRegistry
$doctrine;
80 * UserPasswordHasherInterface instance
82 protected UserPasswordHasherInterface
$hasher;
85 * LoggerInterface instance
87 protected LoggerInterface
$logger;
90 * MailerInterface instance
92 protected MailerInterface
$mailer;
95 * EntityManagerInterface instance
97 protected EntityManagerInterface
$manager;
102 protected Request
$request;
107 protected RouterInterface
$router;
112 protected Security
$security;
115 * Slugger util instance
117 protected SluggerUtil
$slugger;
120 * Translator instance
122 protected TranslatorInterface
$translator;
125 * Twig\Environment instance
127 protected Environment
$twig;
130 * Abstract constructor
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
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) {
149 $this->config
= $container->getParameter(RapsysUserBundle
::getAlias());
152 $this->checker
= $checker;
155 $this->container
= $container;
158 $this->doctrine
= $doctrine;
161 $this->hasher
= $hasher;
164 $this->logger
= $logger;
167 $this->limit
= $limit;
170 $this->mailer
= $mailer;
173 $this->manager
= $manager;
176 $this->router
= $router;
179 $this->security
= $security;
182 $this->slugger
= $slugger;
185 $this->translator
= $translator;
190 //Get current request
191 $this->request
= $stack->getCurrentRequest();
194 $this->page
= (int) $this->request
->query
->get('page');
197 if ($this->page
< 0) {
202 $this->locale
= $this->request
->getLocale();
204 //Set translate array
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) {
215 foreach(array_reverse(explode('.', $translate)) as $curkey) {
216 $tmp = array_combine([$curkey], [$tmp]);
220 $translates = array_replace_recursive($translates, $tmp);
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']);
235 //Process every routes
236 foreach($current['route'] as $route => $key) {
238 if ($route == 'confirm') {
239 //Skip route as it requires some parameters
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
252 if (strpos($key, '.') !== false) {
257 foreach(array_reverse(explode('.', $key)) as $curkey) {
258 $tmp = array_combine([$curkey], [$tmp]);
262 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
[$tag][$view]['context'], $tmp);
266 $this->config
[$tag][$view]['context'][$key] = $value;
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) {
275 $keys = explode('.', $translate);
278 $tmp = $this->config
[$tag][$view]['context'];
281 foreach($keys as $curkey) {
283 if (!isset($tmp[$curkey])) {
289 $tmp = $tmp[$curkey];
292 //Translate tmp value
293 $tmp = $this->translator
->trans($tmp);
296 foreach(array_reverse($keys) as $curkey) {
298 $tmp = array_combine([$curkey], [$tmp]);
302 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
[$tag][$view]['context'], $tmp);
307 if ($view == 'view') {
309 $pathInfo = $this->router
->getContext()->getPathInfo();
311 //Iterate on locales excluding current one
312 foreach(($locales = array_keys($this->config
['languages'])) as $locale) {
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);
321 //Retrieve route matching path
322 $route = $this->router
->match($pathInfo);
325 $name = $route['_route'];
328 unset($route['_route']);
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
);
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)
345 if (empty($this->config
[$tag][$view]['context']['head']['alternates'][$slocale = substr($locale, 0, 2)])) {
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)
367 protected function render(string $view, array $parameters = [], Response
$response = null): Response
{
368 //Create response when null
369 $response ??= new Response();
371 //With empty head locale
372 if (empty($parameters['head']['locale'])) {
374 $parameters['head']['locale'] = $this->locale
;
377 //With empty head title and section
378 if (empty($parameters['head']['title']) && !empty($parameters['section'])) {
380 $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['section'], $parameters['head']['site']]);
381 //With empty head title
382 } elseif (empty($parameters['head']['title'])) {
384 $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['head']['site']]);
387 //Call twig render method
388 $content = $this->twig
->render($view, $parameters);
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);
400 //Store content in response
401 $response->setContent($content);
410 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
412 public static function getSubscribedServices(): array {
413 //Return subscribed services
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