vendor/contao/core-bundle/src/HttpKernel/ModelArgumentResolver.php line 43

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of Contao.
  5. *
  6. * (c) Leo Feyer
  7. *
  8. * @license LGPL-3.0-or-later
  9. */
  10. namespace Contao\CoreBundle\HttpKernel;
  11. use Contao\CoreBundle\Framework\ContaoFramework;
  12. use Contao\CoreBundle\Routing\ScopeMatcher;
  13. use Contao\Model;
  14. use Contao\PageModel;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
  17. use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
  18. class ModelArgumentResolver implements ArgumentValueResolverInterface
  19. {
  20. private ContaoFramework $framework;
  21. private ScopeMatcher $scopeMatcher;
  22. /**
  23. * @internal
  24. */
  25. public function __construct(ContaoFramework $framework, ScopeMatcher $scopeMatcher)
  26. {
  27. $this->framework = $framework;
  28. $this->scopeMatcher = $scopeMatcher;
  29. }
  30. public function supports(Request $request, ArgumentMetadata $argument): bool
  31. {
  32. if (!$this->scopeMatcher->isContaoRequest($request)) {
  33. return false;
  34. }
  35. $this->framework->initialize();
  36. if (!is_a($argument->getType(), Model::class, true)) {
  37. return false;
  38. }
  39. if (!$argument->isNullable() && null === $this->fetchModel($request, $argument)) {
  40. return false;
  41. }
  42. return true;
  43. }
  44. public function resolve(Request $request, ArgumentMetadata $argument): \Generator
  45. {
  46. yield $this->fetchModel($request, $argument);
  47. }
  48. private function fetchModel(Request $request, ArgumentMetadata $argument): ?Model
  49. {
  50. $name = $this->getArgumentName($request, $argument);
  51. if (null === $name) {
  52. return null;
  53. }
  54. /** @var class-string<Model> $type */
  55. $type = $argument->getType();
  56. $value = $request->attributes->get($name);
  57. if ($type && $value instanceof $type) {
  58. return $value;
  59. }
  60. // Special handling for pageModel that could be globally registered
  61. if (
  62. isset($GLOBALS['objPage'])
  63. && $GLOBALS['objPage'] instanceof PageModel
  64. && (int) $GLOBALS['objPage']->id === (int) $value
  65. && is_a($type, PageModel::class, true)
  66. ) {
  67. return $GLOBALS['objPage'];
  68. }
  69. /** @var Model $model */
  70. $model = $this->framework->getAdapter($type);
  71. return $model->findByPk((int) $value);
  72. }
  73. /**
  74. * Returns the argument name from the model class.
  75. */
  76. private function getArgumentName(Request $request, ArgumentMetadata $argument): ?string
  77. {
  78. if ($request->attributes->has($argument->getName())) {
  79. return $argument->getName();
  80. }
  81. $className = lcfirst($this->stripNamespace($argument->getType()));
  82. if ($request->attributes->has($className)) {
  83. return $className;
  84. }
  85. return null;
  86. }
  87. /**
  88. * Strips the namespace from a class name.
  89. */
  90. private function stripNamespace(string $fqcn): string
  91. {
  92. if (false !== ($pos = strrpos($fqcn, '\\'))) {
  93. return substr($fqcn, $pos + 1);
  94. }
  95. return $fqcn;
  96. }
  97. }