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
;
17 use Rapsys\PackBundle\Util\SluggerUtil
;
19 use Rapsys\UserBundle\RapsysUserBundle
;
21 use Psr\Container\ContainerInterface
;
22 use Psr\Log\LoggerInterface
;
24 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController
as BaseAbstractController
;
25 use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait
;
26 use Symfony\Bundle\SecurityBundle\Security
;
27 use Symfony\Component\Form\FormFactoryInterface
;
28 use Symfony\Component\HttpFoundation\Request
;
29 use Symfony\Component\HttpFoundation\RequestStack
;
30 use Symfony\Component\HttpFoundation\Response
;
31 use Symfony\Component\Mailer\MailerInterface
;
32 use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface
;
33 use Symfony\Component\Routing\Generator\UrlGeneratorInterface
;
34 use Symfony\Component\Routing\RouterInterface
;
35 use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface
;
36 use Symfony\Component\Security\Core\User\UserInterface
;
37 use Symfony\Contracts\Cache\CacheInterface
;
38 use Symfony\Contracts\Service\ServiceSubscriberInterface
;
39 use Symfony\Contracts\Translation\TranslatorInterface
;
46 * Provides common features needed in controllers.
48 abstract class AbstractController
extends BaseAbstractController
implements ServiceSubscriberInterface
{
52 protected array $config;
57 protected array $context;
62 protected string $locale;
72 protected Request
$request;
75 * Abstract constructor
77 * @param CacheInterface $cache The cache instance
78 * @param AuthorizationCheckerInterface $checker The checker instance
79 * @param ContainerInterface $container The container instance
80 * @param ManagerRegistry $doctrine The doctrine instance
81 * @param FormFactoryInterface $factory The factory instance
82 * @param UserPasswordHasherInterface $hasher The password hasher instance
83 * @param LoggerInterface $logger The logger instance
84 * @param MailerInterface $mailer The mailer instance
85 * @param EntityManagerInterface $manager The manager instance
86 * @param RouterInterface $router The router instance
87 * @param Security $security The security instance
88 * @param SluggerUtil $slugger The slugger instance
89 * @param RequestStack $stack The stack instance
90 * @param TranslatorInterface $translator The translator instance
91 * @param Environment $twig The twig environment instance
92 * @param integer $limit The page limit
94 public function __construct(protected CacheInterface
$cache, protected AuthorizationCheckerInterface
$checker, protected ContainerInterface
$container, protected ManagerRegistry
$doctrine, protected FormFactoryInterface
$factory, protected UserPasswordHasherInterface
$hasher, protected LoggerInterface
$logger, protected MailerInterface
$mailer, protected EntityManagerInterface
$manager, protected RouterInterface
$router, protected Security
$security, protected SluggerUtil
$slugger, protected RequestStack
$stack, protected TranslatorInterface
$translator, protected Environment
$twig, protected int $limit = 5) {
96 $this->config
= $container->getParameter(RapsysUserBundle
::getAlias());
99 $this->request
= $stack->getCurrentRequest();
102 $this->page
= (int) $this->request
->query
->get('page');
105 if ($this->page
< 0) {
110 $this->locale
= $this->request
->getLocale();
112 //Set translate array
115 //Look for keys to translate
116 if (!empty($this->config
['translate'])) {
117 //Iterate on keys to translate
118 foreach($this->config
['translate'] as $translate) {
123 foreach(array_reverse(explode('.', $translate)) as $curkey) {
124 $tmp = array_combine([$curkey], [$tmp]);
128 $translates = array_replace_recursive($translates, $tmp);
132 //Inject every requested route in view and mail context
133 foreach($this->config
as $tag => $current) {
134 //Look for entry with route subkey
135 if (!empty($current['route'])) {
136 //Generate url for both view and mail
137 foreach(['view', 'mail'] as $view) {
138 //Check that context key is usable
139 if (isset($current[$view]['context']) && is_array($current[$view]['context'])) {
140 //Merge with global context
141 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
['context'], $this->config
[$tag][$view]['context']);
143 //Process every routes
144 foreach($current['route'] as $route => $key) {
146 if ($route == 'confirm') {
147 //Skip route as it requires some parameters
152 $value = $this->router
->generate(
153 $this->config
['route'][$route]['name'],
154 $this->config
['route'][$route]['context'],
155 //Generate absolute url for mails
156 $view=='mail'?UrlGeneratorInterface
::ABSOLUTE_URL
:UrlGeneratorInterface
::ABSOLUTE_PATH
160 if (strpos($key, '.') !== false) {
165 foreach(array_reverse(explode('.', $key)) as $curkey) {
166 $tmp = array_combine([$curkey], [$tmp]);
170 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
[$tag][$view]['context'], $tmp);
174 $this->config
[$tag][$view]['context'][$key] = $value;
178 //Look for successful intersections
179 if (!empty(array_intersect_key($translates, $this->config
[$tag][$view]['context']))) {
180 //Iterate on keys to translate
181 foreach($this->config
['translate'] as $translate) {
183 $keys = explode('.', $translate);
186 $tmp = $this->config
[$tag][$view]['context'];
189 foreach($keys as $curkey) {
191 if (!isset($tmp[$curkey])) {
197 $tmp = $tmp[$curkey];
200 //Translate tmp value
201 $tmp = $this->translator
->trans($tmp);
204 foreach(array_reverse($keys) as $curkey) {
206 $tmp = array_combine([$curkey], [$tmp]);
210 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
[$tag][$view]['context'], $tmp);
215 if ($view == 'view') {
217 $pathInfo = $this->router
->getContext()->getPathInfo();
219 //Iterate on locales excluding current one
220 foreach(($locales = array_keys($this->config
['default']['languages'])) as $locale) {
224 //Iterate on other locales
225 foreach(array_diff($locales, [$locale]) as $other) {
226 $titles[$other] = $this->translator
->trans($this->config
['default']['languages'][$locale], [], null, $other);
229 //Retrieve route matching path
230 $route = $this->router
->match($pathInfo);
233 $name = $route['_route'];
236 unset($route['_route']);
238 //With current locale
239 if ($locale == $this->locale
) {
240 //Set locale locales context
241 $this->config
[$tag][$view]['context']['canonical'] = $this->router
->generate($name, ['_locale' => $locale]+
$route, UrlGeneratorInterface
::ABSOLUTE_URL
);
243 //Set locale locales context
244 $this->config
[$tag][$view]['context']['alternates'][$locale] = [
245 'absolute' => $this->router
->generate($name, ['_locale' => $locale]+
$route, UrlGeneratorInterface
::ABSOLUTE_URL
),
246 'relative' => $this->router
->generate($name, ['_locale' => $locale]+
$route),
247 'title' => implode('/', $titles),
248 'translated' => $this->translator
->trans($this->config
['default']['languages'][$locale], [], null, $locale)
253 if (empty($this->config
[$tag][$view]['context']['alternates'][$slocale = substr($locale, 0, 2)])) {
255 $this->config
[$tag][$view]['context']['alternates'][$slocale] = [
256 'absolute' => $this->router
->generate($name, ['_locale' => $locale]+
$route, UrlGeneratorInterface
::ABSOLUTE_URL
),
257 'relative' => $this->router
->generate($name, ['_locale' => $locale]+
$route),
258 'title' => implode('/', $titles),
259 'translated' => $this->translator
->trans($this->config
['default']['languages'][$locale], [], null, $locale)
275 protected function render(string $view, array $parameters = [], Response
$response = null): Response
{
276 //Create response when null
277 $response ??= new Response();
279 //With empty head locale
280 if (empty($parameters['locale'])) {
282 $parameters['locale'] = $this->locale
;
285 //Call twig render method
286 $content = $this->twig
->render($view, $parameters);
288 //Invalidate OK response on invalid form
289 if (200 === $response->getStatusCode()) {
290 foreach ($parameters as $v) {
291 if ($v instanceof FormInterface
&& $v->isSubmitted() && !$v->isValid()) {
292 $response->setStatusCode(422);
298 //Store content in response
299 $response->setContent($content);
308 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
310 public static function getSubscribedServices(): array {
311 //Return subscribed services
313 'doctrine' => ManagerRegistry
::class,
314 'doctrine.orm.default_entity_manager' => EntityManagerInterface
::class,
315 'form.factory' => FormFactoryInterface
::class,
316 'logger' => LoggerInterface
::class,
317 'mailer.mailer' => MailerInterface
::class,
318 'rapsys_pack.slugger_util' => SluggerUtil
::class,
319 'request_stack' => RequestStack
::class,
320 'router' => RouterInterface
::class,
321 'security.authorization_checker' => AuthorizationCheckerInterface
::class,
322 'security.helper' => Security
::class,
323 'security.user_password_hasher' => UserPasswordHasherInterface
::class,
324 'service_container' => ContainerInterface
::class,
325 'translator' => TranslatorInterface
::class,
326 'twig' => Environment
::class,
327 'user.cache' => CacheInterface
::class