accountSwitcher = $accountSwitcher; $this->entityTypeBundleInfo = $bundleInfo; $this->entityTypeManager = $entityTypeManager; $this->root = $root; $this->serializer = $serializer; $this->eventDispatcher = $eventDispatcher; $this->setPath($path); } /** * Gets a hash of the content entity definitions on the site. * * @param string $requestedTypeName * If specified, the types hash will only contain the key for that type. * * @return array * A machine-name-indexed hash of entity type definitions. If $typeName was * not specified, the definitions are returned for all entity types. */ public function contentEntityTypes(string $requestedTypeName = NULL) { $definitions = $this->entityTypeManager->getDefinitions(); $entityTypes = []; foreach ($definitions as $machine => $type) { $class = $type->getClass(); $implements = class_implements($class); if (isset($implements[static::CONTENT_INTERFACE])) { $entityTypes[$machine] = $type; } } if (!empty($requestedTypeName)) { $entityTypes = [$requestedTypeName => $entityTypes[$requestedTypeName]]; } return $entityTypes; } /** * Load entities for a given entity bundle. * * @param \Drupal\Core\Entity\ContentEntityTypeInterface $type * The entity type object. * @param \Drupal\Core\Entity\ContentEntityStorageInterface $storage * The entity storage for the entity type. * * @return array * A hash of entities by id. */ protected function loadMultiBundleEntities(ContentEntityTypeInterface $type, ContentEntityStorageInterface $storage ) { $bundleNames = array_keys($this->entityTypeBundleInfo->getBundleInfo($type->id())); $key = $type->getKey('bundle'); $entities = []; foreach ($bundleNames as $bundleName) { $bundleEntities = $storage->loadByProperties([$key => $bundleName]); $entities[$bundleName] = $bundleEntities; } return $entities; } /** * Dump entities to YAML files. * * @param string $requestedTypeName * Optional. The name of the entity type for which to export entities. If * absent, all entities in all types are dumped. * * @see https://www.drupal.org/node/218104 */ public function dump($requestedTypeName = NULL) { $this->accountSwitcher->switchTo(new UserSession(['uid' => 1])); $entityTypes = $this->contentEntityTypes($requestedTypeName); foreach ($entityTypes as $entityTypeName => $entityType) { $storage = $this->entityTypeManager->getStorage($entityTypeName); $entities = $entityType->hasKey('bundle') ? $this->loadMultiBundleEntities($entityType, $storage) : [$entityTypeName => $storage->loadMultiple()]; foreach ($entities as $bundleName => $bundleEntities) { // Allow adding data to entities before exporting, like term parents. $eventPre = new DumperEvent($storage, $bundleName, $bundleEntities); $this->eventDispatcher->dispatch(DumperEvents::PRE_DUMP, $eventPre); $this->dumpEntities($entityTypeName, $bundleName, $bundleEntities); // Allow extra work after exporting, like copying files. $eventPost = new DumperEvent($storage, $bundleName, $bundleEntities); $this->eventDispatcher->dispatch(DumperEvents::POST_DUMP, $eventPost); } } $this->accountSwitcher->switchBack(); } /** * Generate import YAML for entities. * * @param string $entityTypeName * The entity type. * @param string $bundleName * The bundle name. * @param array $entities * The entities. */ public function dumpEntities(string $entityTypeName, string $bundleName, array $entities) { $array = $this->toArray($entities); $path = $this->prepareDestination($entityTypeName, $bundleName); file_put_contents($path, Yaml::dump($array, static::INLINE_DEPTH, 2)); } /** * Prepare the dump destination directory and return the file name within it. * * @param string $entityTypeName * The type of the entities to dump. * @param string $bundleName * The bundle of the entities to dump. * * @return string * The path of the dump file. */ protected function prepareDestination(string $entityTypeName, string $bundleName): string { $importPath = $this->importPath; $dir = "$importPath/$entityTypeName"; if (!file_exists($dir)) { mkdir($dir, 0777, TRUE); } $path = "${dir}/${bundleName}.yml"; return $path; } /** * Store the absolute path to the import directory. * * @param string $path * The Drupal-root-relative import path. * * @throws \InvalidArgumentException * If the directory does not exist. */ public function setPath(string $path) { $completePath = $this->root . '/' . $path; $real = realpath($completePath); if (!is_dir($real)) { drupal_set_message("Non-existent base dump directory: $completePath.", "error"); throw new \InvalidArgumentException("Non-existent base dump directory: $completePath."); } $this->importPath = $real; } /** * Like NormalizerInterface::normalize(), but for an array. * * @param array $entities * The entities to convert to arrays. * * @return mixed * The array representing the entities. */ protected function toArray(array $entities): array { $json_options = []; $json = $this->serializer->serialize($entities, 'json', $json_options); $hash = json_decode($json, TRUE); return $hash; } }