]> Raphaël G. Git Repositories - userbundle/blob - Controller/AbstractController.php
Add cache and form factory
[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\Log\LoggerInterface;
18
19 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController;
20 use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
21 use Symfony\Bundle\SecurityBundle\Security;
22 use Symfony\Component\DependencyInjection\ContainerInterface;
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 * Abstract constructor
71 *
72 * @param CacheInterface $cache The cache instance
73 * @param AuthorizationCheckerInterface $checker The checker instance
74 * @param ContainerInterface $container The container instance
75 * @param ManagerRegistry $doctrine The doctrine instance
76 * @param FormFactoryInterface $factory The factory instance
77 * @param UserPasswordHasherInterface $hasher The password hasher instance
78 * @param LoggerInterface $logger The logger instance
79 * @param MailerInterface $mailer The mailer instance
80 * @param EntityManagerInterface $manager The manager instance
81 * @param RouterInterface $router The router instance
82 * @param Security $security The security instance
83 * @param SluggerUtil $slugger The slugger instance
84 * @param RequestStack $stack The stack instance
85 * @param TranslatorInterface $translator The translator instance
86 * @param Environment $twig The twig environment instance
87 * @param integer $limit The page limit
88 */
89 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) {
90 //Retrieve config
91 $this->config = $container->getParameter(RapsysUserBundle::getAlias());
92
93 //Get current request
94 $this->request = $stack->getCurrentRequest();
95
96 //Get current page
97 $this->page = (int) $this->request->query->get('page');
98
99 //With negative page
100 if ($this->page < 0) {
101 $this->page = 0;
102 }
103
104 //Get current locale
105 $this->locale = $this->request->getLocale();
106
107 //Set translate array
108 $translates = [];
109
110 //Look for keys to translate
111 if (!empty($this->config['translate'])) {
112 //Iterate on keys to translate
113 foreach($this->config['translate'] as $translate) {
114 //Set tmp
115 $tmp = null;
116
117 //Iterate on keys
118 foreach(array_reverse(explode('.', $translate)) as $curkey) {
119 $tmp = array_combine([$curkey], [$tmp]);
120 }
121
122 //Append tree
123 $translates = array_replace_recursive($translates, $tmp);
124 }
125 }
126
127 //Inject every requested route in view and mail context
128 foreach($this->config as $tag => $current) {
129 //Look for entry with route subkey
130 if (!empty($current['route'])) {
131 //Generate url for both view and mail
132 foreach(['view', 'mail'] as $view) {
133 //Check that context key is usable
134 if (isset($current[$view]['context']) && is_array($current[$view]['context'])) {
135 //Merge with global context
136 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config['context'], $this->config[$tag][$view]['context']);
137
138 //Process every routes
139 foreach($current['route'] as $route => $key) {
140 //With confirm route
141 if ($route == 'confirm') {
142 //Skip route as it requires some parameters
143 continue;
144 }
145
146 //Set value
147 $value = $this->router->generate(
148 $this->config['route'][$route]['name'],
149 $this->config['route'][$route]['context'],
150 //Generate absolute url for mails
151 $view=='mail'?UrlGeneratorInterface::ABSOLUTE_URL:UrlGeneratorInterface::ABSOLUTE_PATH
152 );
153
154 //Multi level key
155 if (strpos($key, '.') !== false) {
156 //Set tmp
157 $tmp = $value;
158
159 //Iterate on key
160 foreach(array_reverse(explode('.', $key)) as $curkey) {
161 $tmp = array_combine([$curkey], [$tmp]);
162 }
163
164 //Set value
165 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
166 //Single level key
167 } else {
168 //Set value
169 $this->config[$tag][$view]['context'][$key] = $value;
170 }
171 }
172
173 //Look for successful intersections
174 if (!empty(array_intersect_key($translates, $this->config[$tag][$view]['context']))) {
175 //Iterate on keys to translate
176 foreach($this->config['translate'] as $translate) {
177 //Set keys
178 $keys = explode('.', $translate);
179
180 //Set tmp
181 $tmp = $this->config[$tag][$view]['context'];
182
183 //Iterate on keys
184 foreach($keys as $curkey) {
185 //Without child key
186 if (!isset($tmp[$curkey])) {
187 //Skip to next key
188 continue(2);
189 }
190
191 //Get child key
192 $tmp = $tmp[$curkey];
193 }
194
195 //Translate tmp value
196 $tmp = $this->translator->trans($tmp);
197
198 //Iterate on keys
199 foreach(array_reverse($keys) as $curkey) {
200 //Set parent key
201 $tmp = array_combine([$curkey], [$tmp]);
202 }
203
204 //Set value
205 $this->config[$tag][$view]['context'] = array_replace_recursive($this->config[$tag][$view]['context'], $tmp);
206 }
207 }
208
209 //With view context
210 if ($view == 'view') {
211 //Get context path
212 $pathInfo = $this->router->getContext()->getPathInfo();
213
214 //Iterate on locales excluding current one
215 foreach(($locales = array_keys($this->config['languages'])) as $locale) {
216 //Set titles
217 $titles = [];
218
219 //Iterate on other locales
220 foreach(array_diff($locales, [$locale]) as $other) {
221 $titles[$other] = $this->translator->trans($this->config['languages'][$locale], [], null, $other);
222 }
223
224 //Retrieve route matching path
225 $route = $this->router->match($pathInfo);
226
227 //Get route name
228 $name = $route['_route'];
229
230 //Unset route name
231 unset($route['_route']);
232
233 //With current locale
234 if ($locale == $this->locale) {
235 //Set locale locales context
236 $this->config[$tag][$view]['context']['head']['canonical'] = $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL);
237 } else {
238 //Set locale locales context
239 $this->config[$tag][$view]['context']['head']['alternates'][$locale] = [
240 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL),
241 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route),
242 'title' => implode('/', $titles),
243 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale)
244 ];
245 }
246
247 //Add shorter locale
248 if (empty($this->config[$tag][$view]['context']['head']['alternates'][$slocale = substr($locale, 0, 2)])) {
249 //Add shorter locale
250 $this->config[$tag][$view]['context']['head']['alternates'][$slocale] = [
251 'absolute' => $this->router->generate($name, ['_locale' => $locale]+$route, UrlGeneratorInterface::ABSOLUTE_URL),
252 'relative' => $this->router->generate($name, ['_locale' => $locale]+$route),
253 'title' => implode('/', $titles),
254 'translated' => $this->translator->trans($this->config['languages'][$locale], [], null, $locale)
255 ];
256 }
257 }
258 }
259 }
260 }
261 }
262 }
263 }
264
265 /**
266 * Renders a view
267 *
268 * {@inheritdoc}
269 */
270 protected function render(string $view, array $parameters = [], Response $response = null): Response {
271 //Create response when null
272 $response ??= new Response();
273
274 //With empty head locale
275 if (empty($parameters['head']['locale'])) {
276 //Set head locale
277 $parameters['head']['locale'] = $this->locale;
278 }
279
280 //With empty head title and section
281 if (empty($parameters['head']['title']) && !empty($parameters['section'])) {
282 //Set head title
283 $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['section'], $parameters['head']['site']]);
284 //With empty head title
285 } elseif (empty($parameters['head']['title'])) {
286 //Set head title
287 $parameters['head']['title'] = implode(' - ', [$parameters['title'], $parameters['head']['site']]);
288 }
289
290 //Call twig render method
291 $content = $this->twig->render($view, $parameters);
292
293 //Invalidate OK response on invalid form
294 if (200 === $response->getStatusCode()) {
295 foreach ($parameters as $v) {
296 if ($v instanceof FormInterface && $v->isSubmitted() && !$v->isValid()) {
297 $response->setStatusCode(422);
298 break;
299 }
300 }
301 }
302
303 //Store content in response
304 $response->setContent($content);
305
306 //Return response
307 return $response;
308 }
309
310 /**
311 * {@inheritdoc}
312 *
313 * @see vendor/symfony/framework-bundle/Controller/AbstractController.php
314 */
315 public static function getSubscribedServices(): array {
316 //Return subscribed services
317 return [
318 'doctrine' => ManagerRegistry::class,
319 'doctrine.orm.default_entity_manager' => EntityManagerInterface::class,
320 'form.factory' => FormFactoryInterface::class,
321 'logger' => LoggerInterface::class,
322 'mailer.mailer' => MailerInterface::class,
323 'rapsys_pack.slugger_util' => SluggerUtil::class,
324 'request_stack' => RequestStack::class,
325 'router' => RouterInterface::class,
326 'security.authorization_checker' => AuthorizationCheckerInterface::class,
327 'security.helper' => Security::class,
328 'security.user_password_hasher' => UserPasswordHasherInterface::class,
329 'service_container' => ContainerInterface::class,
330 'translator' => TranslatorInterface::class,
331 'twig' => Environment::class,
332 'user.cache' => CacheInterface::class
333 ];
334 }
335 }