/** * Queries the associated storage whether the entity data should be saved or not * * @see Storage::shouldBeSaved() * * @param string $entityName Determines the storage * @param array $data Data passed to VersionPress\Storages\Storage::shouldBeSaved() * @return bool */ public function shouldBeSaved($entityName, $data) { $storage = $this->storageFactory->getStorage($entityName); if ($storage === null) { return false; } return $storage->shouldBeSaved($data); }
/** * @test * @testdox Factory creates right storages */ public function factoryCreatesRightStorages() { $storages = ['post' => DirectoryStorage::class, 'comment' => DirectoryStorage::class, 'option' => DirectoryStorage::class, 'term' => DirectoryStorage::class, 'termmeta' => MetaEntityStorage::class, 'term_taxonomy' => DirectoryStorage::class, 'user' => DirectoryStorage::class, 'usermeta' => MetaEntityStorage::class, 'postmeta' => MetaEntityStorage::class]; /** @var \wpdb $wpdbStub */ $wpdbStub = new \stdClass(); $wpdbStub->prefix = 'prefix_'; $database = new Database($wpdbStub); $changeInfoFactory = $this->getMockBuilder(ChangeInfoFactory::class)->disableOriginalConstructor()->getMock(); $tableSchemaStorage = $this->getMockBuilder(TableSchemaStorage::class)->disableOriginalConstructor()->getMock(); $factory = new StorageFactory(__DIR__ . '/vpdb', new DbSchemaInfo([__DIR__ . '/../../.versionpress/schema.yml'], 'wp_', PHP_INT_MAX), $database, [], $changeInfoFactory, $tableSchemaStorage); foreach ($storages as $entityName => $expectedClass) { $this->assertInstanceOf($expectedClass, $factory->getStorage($entityName)); } }
/** * Calls Git `add -A` on files that are related to the given $changeInfo. * The "exchange format" is an array documented in {@see TrackedChangedInfo::getChangedFiles()}. * * @param TrackedChangeInfo|ChangeInfoEnvelope $changeInfo */ private function stageRelatedFiles($changeInfo) { if ($changeInfo instanceof ChangeInfoEnvelope) { /** @var TrackedChangeInfo $subChangeInfo */ foreach ($changeInfo->getChangeInfoList() as $subChangeInfo) { $this->stageRelatedFiles($subChangeInfo); } return; } $changes = $changeInfo->getChangedFiles(); foreach ($changes as $change) { if ($change["type"] === "storage-file") { $entityName = $change["entity"]; $entityId = $change["id"]; $parentId = $change["parent-id"]; $path = $this->storageFactory->getStorage($entityName)->getEntityFilename($entityId, $parentId); } elseif ($change["type"] === "all-storage-files") { $entityName = $change["entity"]; $path = $this->storageFactory->getStorage($entityName)->getPathCommonToAllEntities(); } elseif ($change["type"] === "path") { $path = $change["path"]; } else { continue; } $this->repository->stageAll($path); } }
/** * For standard entities just checks the storage. * For child entities (like postmeta) loads all entities and checks them. * * @param $referencedEntityName * @param $referencedEntityId * @param $maybeParentId * @return bool */ private function entityExists($referencedEntityName, $referencedEntityId, $maybeParentId) { if (!$this->dbSchemaInfo->isChildEntity($referencedEntityName)) { return $this->storageFactory->getStorage($referencedEntityName)->exists($referencedEntityId, null); } // Optimalization for child entities saved within their parents if ($this->storageFactory->getStorage($referencedEntityName)->exists($referencedEntityId, $maybeParentId)) { return true; } $allEntities = $this->storageFactory->getStorage($referencedEntityName)->loadAll(); return isset($allEntities[$referencedEntityId]); }
private function saveMnReferences($referenceDetails) { $junctionTable = $referenceDetails['junction-table']; $sourceEntity = $referenceDetails['source-entity']; $targetEntity = $referenceDetails['target-entity']; $sourceColumn = $referenceDetails['source-column']; $targetColumn = $referenceDetails['target-column']; $storage = $this->storageFactory->getStorage($junctionTable); $dbRows = $this->getEntitiesFromDatabase($junctionTable); foreach ($dbRows as $row) { $reference = ["vp_{$sourceEntity}" => $this->idCache[$sourceEntity][intval($row[$sourceColumn])], "vp_{$targetEntity}" => $this->idCache[$targetEntity][intval($row[$targetColumn])]]; $storage->save($reference); } }
/** * Saves entities of type identified by $entityName to their appropriate storage * (chosen by factory). * * @param string $entityName */ private function saveEntitiesOfTypeToStorage($entityName) { $storage = $this->storageFactory->getStorage($entityName); $entities = $this->getEntitiesFromDatabase($entityName); $entities = $this->replaceForeignKeysWithReferencesInAllEntities($entityName, $entities); $entities = array_values(array_filter($entities, function ($entity) use($storage) { return $storage->shouldBeSaved($entity); })); $urlReplacer = $this->urlReplacer; $entities = $this->extendEntitiesWithVpids($entityName, $entities); $entities = array_map(function ($entity) use($urlReplacer) { return $urlReplacer->replace($entity); }, $entities); $entities = $this->doEntitySpecificActions($entityName, $entities); $storage->prepareStorage(); if (!$this->dbSchema->isChildEntity($entityName)) { $this->saveStandardEntities($storage, $entities); } else { // meta entities $entityInfo = $this->dbSchema->getEntityInfo($entityName); $parentReference = "vp_" . $entityInfo->parentReference; $this->saveMetaEntities($storage, $entities, $parentReference); } }
private function getStorage($synchronizerName) { return $this->storageFactory->getStorage($synchronizerName); }
/** * @param $entityName */ private static function assertEntitiesEqualDatabase($entityName) { $storage = self::$storageFactory->getStorage($entityName); $entityInfo = self::$schemaInfo->getEntityInfo($entityName); $allDbEntities = self::selectAll(self::$schemaInfo->getPrefixedTableName($entityName)); $idMap = self::getVpIdMap(); $allDbEntities = self::identifyEntities($entityName, $allDbEntities, $idMap); $allDbEntities = self::replaceForeignKeys($entityName, $allDbEntities, $idMap); $dbEntities = array_filter($allDbEntities, [$storage, 'shouldBeSaved']); $urlReplacer = new AbsoluteUrlReplacer(self::$testConfig->testSite->url); $storageEntities = array_map(function ($entity) use($urlReplacer) { return $urlReplacer->restore($entity); }, $storage->loadAll()); $countOfentitiesInDb = count($dbEntities); $countOfentitiesInStorage = count($storageEntities); if ($countOfentitiesInDb !== $countOfentitiesInStorage) { if ($countOfentitiesInStorage > $countOfentitiesInDb) { $problematicEntities = self::findMissingEntities($entityName, $storageEntities, $dbEntities); } else { $problematicEntities = self::findExceedingEntities($entityName, $storageEntities, $dbEntities); } throw new \PHPUnit_Framework_AssertionFailedError("Different count of synchronized entities ({$entityName}): DB = {$countOfentitiesInDb}, " . "storage = {$countOfentitiesInStorage}\nProblematic entities: " . join(", ", $problematicEntities)); } foreach ($dbEntities as $dbEntity) { $id = $dbEntity[$entityInfo->vpidColumnName]; $storageEntity = $storageEntities[$id]; $dbEntity = self::$shortcodesReplacer->replaceShortcodesInEntity($entityName, $dbEntity); foreach ($dbEntity as $column => $value) { if ($entityInfo->idColumnName === $column || isset($entityInfo->getIgnoredColumns()[$column])) { continue; } if (!isset($storageEntity[$column])) { throw new \PHPUnit_Framework_AssertionFailedError("{$entityName}[{$column}] with value = {$value}, ID = {$id} not found in storage"); } if (is_string($storageEntity[$column])) { $storageEntity[$column] = str_replace("\r\n", "\n", $storageEntity[$column]); } if (is_string($value)) { $value = str_replace("\r\n", "\n", $value); } if ($storageEntity[$column] != $value) { throw new \PHPUnit_Framework_AssertionFailedError("Different values ({$entityName}[{$column}]: {$id}): DB = {$value}, storage = {$storageEntity[$column]}"); } } } $missingReferences = []; $exceedingReferences = []; foreach ($entityInfo->mnReferences as $reference => $targetEntity) { if ($entityInfo->isVirtualReference($reference)) { continue; } $referenceDetails = ReferenceUtils::getMnReferenceDetails(self::$schemaInfo, $entityName, $reference); $sourceColumn = $referenceDetails['source-column']; $targetColumn = $referenceDetails['target-column']; $junctionTable = $referenceDetails['junction-table']; $prefixedJunctionTable = self::$schemaInfo->getPrefixedTableName($junctionTable); $prefixedVpIdTable = self::$schemaInfo->getPrefixedTableName('vp_id'); $sourceTable = self::$schemaInfo->getTableName($referenceDetails['source-entity']); $targetTable = self::$schemaInfo->getTableName($referenceDetails['target-entity']); $junctionTableContent = self::fetchAll("SELECT HEX(s_vp_id.vp_id), HEX(t_vp_id.vp_id) FROM {$prefixedJunctionTable} j\n JOIN {$prefixedVpIdTable} s_vp_id ON j.{$sourceColumn} = s_vp_id.id AND s_vp_id.`table`='{$sourceTable}'\n JOIN {$prefixedVpIdTable} t_vp_id ON j.{$targetColumn} = t_vp_id.id AND t_vp_id.`table` = '{$targetTable}'", MYSQLI_NUM); $checkedReferences = []; $missingReferences[$junctionTable] = []; foreach ($storageEntities as $storageEntity) { if (!isset($storageEntity["vp_{$targetEntity}"])) { continue; } foreach ($storageEntity["vp_{$targetEntity}"] as $referenceVpId) { if (!ArrayUtils::any($junctionTableContent, function ($junctionRow) use($storageEntity, $referenceVpId) { return $junctionRow[0] === $storageEntity['vp_id'] && $junctionRow[1] === $referenceVpId; })) { $missingReferences[$junctionTable][] = [$sourceColumn => $storageEntity['vp_id'], $targetColumn => $referenceVpId]; } $checkedReferences[] = [$storageEntity['vp_id'], $referenceVpId]; } } $exceedingReferences[$junctionTable] = array_map(function ($pair) use($sourceColumn, $targetColumn) { return [$sourceColumn => $pair[0], $targetColumn => $pair[1]]; }, array_filter($junctionTableContent, function ($pair) use($checkedReferences) { foreach ($checkedReferences as $reference) { if ($reference[0] === $pair[0] && $reference[1] === $pair[1]) { return false; } } return true; })); } self::reportResultOfMnReferenceCheck($missingReferences, "Missing"); self::reportResultOfMnReferenceCheck($exceedingReferences, "Exceeding"); }