vendor/mvo/contao-group-widget/src/Group/Registry.php line 44

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * @author  Moritz Vondano
  5.  * @license MIT
  6.  */
  7. namespace Mvo\ContaoGroupWidget\Group;
  8. use Doctrine\DBAL\Connection;
  9. use Mvo\ContaoGroupWidget\Storage\NullStorage;
  10. use Mvo\ContaoGroupWidget\Storage\StorageFactoryInterface;
  11. use Mvo\ContaoGroupWidget\Storage\StorageInterface;
  12. use Symfony\Component\HttpFoundation\RequestStack;
  13. use Terminal42\DcMultilingualBundle\Driver;
  14. use Twig\Environment;
  15. /**
  16.  * Group factory methods.
  17.  *
  18.  * @final
  19.  */
  20. class Registry
  21. {
  22.     private Environment $twig;
  23.     private RequestStack $requestStack;
  24.     private Connection $connection;
  25.     /**
  26.      * @var array<string, StorageFactoryInterface>
  27.      */
  28.     private array $storageFactories = [];
  29.     /**
  30.      * @var array<string, array<string,Group>>
  31.      */
  32.     private array $groupCache = [];
  33.     /**
  34.      * @internal
  35.      */
  36.     public function __construct(Environment $twigRequestStack $requestStackConnection $connection\IteratorAggregate $storageFactories)
  37.     {
  38.         $this->twig $twig;
  39.         $this->requestStack $requestStack;
  40.         $this->connection $connection;
  41.         /** @var StorageFactoryInterface $factory */
  42.         foreach ($storageFactories->getIterator() as $factory) {
  43.             $this->storageFactories[$factory::getName()] = $factory;
  44.         }
  45.     }
  46.     /**
  47.      * Creates and returns a group. The same instance will be returned if
  48.      * called with identical arguments.
  49.      */
  50.     public function getGroup(string $tableint $rowIdstring $name): Group
  51.     {
  52.         if (null !== ($group $this->groupCache[$cacheKey $this->getCacheKey($table$rowId)][$name] ?? null)) {
  53.             return $group;
  54.         }
  55.         if (null === $group $this->handleDcMultilingual($table$rowId$name)) {
  56.             $group $this->createGroup($table$rowId$name);
  57.         }
  58.         return $this->groupCache[$cacheKey][$name] = $group;
  59.     }
  60.     public function getInitializedGroups(string $tableint $rowId): array
  61.     {
  62.         return array_values($this->groupCache[$this->getCacheKey($table$rowId)] ?? []);
  63.     }
  64.     /**
  65.      * @return array<string>
  66.      */
  67.     public function getGroupFields(string $table): array
  68.     {
  69.         return array_keys(
  70.             array_filter(
  71.                 array_filter($GLOBALS['TL_DCA'][$table]['fields'] ?? []),
  72.                 static fn (array $definition): bool => 'group' === ($definition['inputType'] ?? null)
  73.             )
  74.         );
  75.     }
  76.     private function createGroup(string $tableint $rowIdstring $nameStorageInterface $storage null): Group
  77.     {
  78.         $group = new Group($this->twig$table$rowId$name);
  79.         $group->setStorage($storage ?? $this->createStorage($table$name$group));
  80.         return $group;
  81.     }
  82.     private function createStorage(string $table$nameGroup $group): StorageInterface
  83.     {
  84.         $storageType $GLOBALS['TL_DCA'][$table]['fields'][$name]['storage'] ?? 'serialized';
  85.         $storageFactory $this->storageFactories[$storageType] ?? null;
  86.         if (null === $storageFactory) {
  87.             throw new \InvalidArgumentException("Invalid definition for group '$name': Unknown storage type '$storageType'.");
  88.         }
  89.         return $storageFactory->create($group);
  90.     }
  91.     private function getCacheKey(string $tableint $rowId): string
  92.     {
  93.         return $table."\x0".$rowId;
  94.     }
  95.     /**
  96.      * DC Multilingual stores translations in their own rows. In order to be
  97.      * compatible, we need to adjust the target $rowId in case a translated
  98.      * version was selected.
  99.      */
  100.     private function handleDcMultilingual(string $tableint $rowIdstring $name): ?Group
  101.     {
  102.         if (($GLOBALS['TL_DCA'][$table]['config']['dataContainer'] ?? '') !== Driver::class) {
  103.             return null;
  104.         }
  105.         $language $this->requestStack
  106.             ->getSession()
  107.             ->getBag('contao_backend')
  108.             ->get("dc_multilingual:$table:$rowId")
  109.         ;
  110.         if (null === $language) {
  111.             return null;
  112.         }
  113.         $pidColumn $dca['config']['langPid'] ?? 'langPid';
  114.         $languageColumn $dca['config']['langColumnName'] ?? 'language';
  115.         $result $this->connection->fetchOne(
  116.             sprintf(
  117.                 'SELECT id FROM %s WHERE %s=? AND %s=?',
  118.                 $this->connection->quoteIdentifier($table),
  119.                 $this->connection->quoteIdentifier($pidColumn),
  120.                 $this->connection->quoteIdentifier($languageColumn),
  121.             ),
  122.             [$rowId$language]
  123.         );
  124.         if ($result) {
  125.             return $this->createGroup($table, (int) $result$name);
  126.         }
  127.         // In case we do not have a record yet, we create a group with an empty
  128.         // dummy storage that does not persist anything - otherwise the parent
  129.         // entries would show up. As soon as the record gets saved/the page is
  130.         // reloaded, we do have a record and the real storage kicks in
  131.         // persisting the posted values.
  132.         return $this->createGroup($table$rowId$name, new NullStorage());
  133.     }
  134. }