]> Raphaël G. Git Repositories - userbundle/blob - Controller/AbstractController.php
8750a20d2c6e85c01dbe8865350d80ff6761e3d4
[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
17 use Psr\Container\ContainerInterface;
18 use Psr\Log\LoggerInterface;
19
20 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController;
21 use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
22 use Symfony\Bundle\SecurityBundle\Security;
23 use Symfony\Component\Form\FormFactoryInterface;
24 use Symfony\Component\HttpFoundation\Request;
25 use Symfony\Component\HttpFoundation\RequestStack;
26 use Symfony\Component\HttpFoundation\Response;
27 use Symfony\Component\Mailer\MailerInterface;
28 use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
29 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
30 use Symfony\Component\Routing\RouterInterface;
31 use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
32 use Symfony\Component\Security\Core\User\UserInterface;
33 use Symfony\Contracts\Cache\CacheInterface;
34 use Symfony\Contracts\Service\ServiceSubscriberInterface;
35 use Symfony\Contracts\Translation\TranslatorInterface;
36
37 use Twig\Environment;
38
39 use Rapsys\PackBundle\Util\SluggerUtil;
40
41 use Rapsys\UserBundle\RapsysUserBundle;
42
43 /**
44 * Provides common features needed in controllers.
45 *
46 * {@inheritdoc}
47 */
48 abstract class AbstractController extends BaseAbstractController implements ServiceSubscriberInterface {
49 /**
50 * Config array
51 */
52 protected array $config;
53
54 /**
55 * Context array
56 */
57 protected array $context;
58
59 /**
60 * Locale string
61 */
62 protected string $locale;
63
64 /**
65 * Page integer
66 */
67 protected int $page;
68
69 /**
70 * Request instance
71 */
72 protected Request $request;
73
74 /**
75 * Abstract constructor
76 *
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
93 */
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) {
95 //Retrieve config
96 $this->config = $container->getParameter(RapsysUserBundle::getAlias());
97
98 //Get current request
99 $this->request = $stack->getCurrentRequest();
100
101 //Get current page
102 $this->page = (int) $this->request->query->get('page');
103
104 //With negative page
105 if ($this->page < 0) {
106 $this->page = 0;
107 }
108
109 //Get current locale
110 $this->locale = $this->request->getLocale();
111
112 //Set translate array
113 $translates = [];
114
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) {
119 //Set tmp
120 $tmp = null;
121
122 //Iterate on keys
123 foreach(array_reverse(explode('.', $translate)) as $curkey) {
124 $tmp = array_combine([$curkey], [$tmp]);
125 }
126
127 //Append tree
128 $translates = array_replace_recursive($translates, $tmp);
129 }
130 }
131
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']);
142
143 //Process every routes
144 foreach($current['route'] as $route => $key) {
145 //With confirm route
146 if ($route == 'confirm') {
147 //Skip route as it requires some parameters
148 continue;
149 }
150
151 //Set value
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
157 );
158
159 //Multi level key
160 if (strpos($key, '.') !== false) {
161 //Set tmp
162 $tmp = $value;
163
164 //Iterate on key
165 foreach(array_reverse(explode('.', $key)) as $curkey) {
166 $tmp = array_combine([$curkey], [$tmp]);
167 }
168
169 //Set value
170 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
171 //Single level key
172 } else {
173 //Set value
174 $this->config[$tag][$view]['context'][$key] = $value;
175 }
176 }
177
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) {
182 //Set keys
183 $keys = explode('.', $translate);
184
185 //Set tmp
186 $tmp = $this->config[$tag][$view]['context'];
187
188 //Iterate on keys
189 foreach($keys as $curkey) {
190 //Without child key
191 if (!isset($tmp[$curkey])) {
192 //Skip to next key
193 continue(2);
194 }
195
196 //Get child key
197 $tmp = $tmp[$curkey];
198 }
199
200 //Translate tmp value
201 $tmp = $this->translator->trans($tmp);
202
203 //Iterate on keys
204 foreach(array_reverse($keys) as $curkey) {
205 //Set parent key
206 $tmp = array_combine([$curkey], [$tmp]);
207 }
208
209 //Set value
210 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
211 }
212 }
213
214 //With view context
215 if ($view == 'view') {
216 //Get context path
217 $pathInfo = $this->router->getContext()->getPathInfo();
218
219 //Iterate on locales excluding current one
220 foreach(($locales = array_keys($this->config['default']['languages'])) as $locale) {
221 //Set titles
222 $titles = [];
223
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);
227 }
228
229 //Retrieve route matching path
230 $route = $this->router->match($pathInfo);
231
232 //Get route name
233 $name = $route['_route'];
234
235 //Unset route name
236 unset($route['_route']);
237
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);
242 } else {
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)
249 ];
250 }
251
252 //Add shorter locale
253 if (empty($this->config[$tag][$view]['context']['alternates'][$slocale = substr($locale, 0, 2)])) {
254 //Add shorter locale
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)
260 ];
261 }
262 }
263 }
264 }
265 }
266 }
267 }
268 }
269
270 /**
271 * Renders a view
272 *
273 * {@inheritdoc}
274 */
275 protected function render(string $view, array $parameters = [], Response $response = null): Response {
276 //Create response when null
277 $response ??= new Response();
278
279 //With empty head locale
280 if (empty($parameters['locale'])) {
281 //Set head locale
282 $parameters['locale'] = $this->locale;
283 }
284
285 /*TODO: XXX: to drop, we have title => [ 'page' => XXX, section => XXX, site => XXX ]
286 //With empty head title and section
287 if (empty($parameters['head']['title']) && !empty($parameters['section'])) {
288 //Set head title
289 $parameters['title'] = implode(' - ', [$parameters['title'], $parameters['section'], $parameters['head']['site']]);
290 //With empty head title
291 } elseif (empty($parameters['head']['title'])) {
292 //Set head title
293 $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['head']['site']]);
294 }*/
295
296 //Call twig render method
297 $content = $this->twig->render($view, $parameters);
298
299 //Invalidate OK response on invalid form
300 if (200 === $response->getStatusCode()) {
301 foreach ($parameters as $v) {
302 if ($v instanceof FormInterface && $v->isSubmitted() && !$v->isValid()) {
303 $response->setStatusCode(422);
304 break;
305 }
306 }
307 }
308
309 //Store content in response
310 $response->setContent($content);
311
312 //Return response
313 return $response;
314 }
315
316 /**
317 * {@inheritdoc}
318 *
319 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
320 */
321 public static function getSubscribedServices(): array {
322 //Return subscribed services
323 return [
324 'doctrine' => ManagerRegistry::class,
325 'doctrine.orm.default_entity_manager' => EntityManagerInterface::class,
326 'form.factory' => FormFactoryInterface::class,
327 'logger' => LoggerInterface::class,
328 'mailer.mailer' => MailerInterface::class,
329 'rapsys_pack.slugger_util' => SluggerUtil::class,
330 'request_stack' => RequestStack::class,
331 'router' => RouterInterface::class,
332 'security.authorization_checker' => AuthorizationCheckerInterface::class,
333 'security.helper' => Security::class,
334 'security.user_password_hasher' => UserPasswordHasherInterface::class,
335 'service_container' => ContainerInterface::class,
336 'translator' => TranslatorInterface::class,
337 'twig' => Environment::class,
338 'user.cache' => CacheInterface::class
339 ];
340 }
341 }