class Terms in Hook Update Deploy Tools 7
Public methods for dealing with Vocabularies.
Hierarchy
- class \HookUpdateDeployTools\Terms implements ExportInterface, ImportInterface
Expanded class hierarchy of Terms
File
- src/
Terms.php, line 8
Namespace
HookUpdateDeployToolsView source
class Terms implements ImportInterface, ExportInterface {
/**
* Perform the unique steps necessary to import terms items from export files.
*
* @param string|array $vocabulary_term_names
* The unique identifier(s) of the vocabulary terms to import, created from
* the "Vocabulary name|Term name" - use the human name not machine_name.
*/
public static function import($vocabulary_term_names) {
$t = get_t();
$completed = array();
$vocabulary_term_names = (array) $vocabulary_term_names;
$total_requested = count($vocabulary_term_names);
try {
Check::notEmpty('$vocabulary_term_names', $vocabulary_term_names);
self::canImport();
foreach ($vocabulary_term_names as $key => $vocabulary_term_name) {
// Get the elements out of the vocabulary term name mashup.
$vocabulary_name = self::getVocabularyName($vocabulary_term_name);
$term_name = self::getTermName($vocabulary_term_name);
$filename = self::normalizeFileName($vocabulary_term_name);
// If the file is there, process it.
if (HudtInternal::canReadFile($filename, 'term')) {
// Read the file.
$file_contents = HudtInternal::readFileToString($filename, 'term');
eval('$term_import = ' . $file_contents . ';');
if (!is_object($term_import)) {
if (empty($errors)) {
$errors = 'Term build error on eval().';
}
$message = 'Unable to get a term from the import. Errors: @errors';
throw new HudtException($message, array(
'@errors' => $errors,
), WATCHDOG_ERROR);
}
$error_msg = '';
$result = self::importOne($term_import, $vocabulary_name, $term_name);
// No Exceptions so far, so it must be a success.
$message = '@operation: @vterm - successful.';
global $base_url;
$link = "{$base_url}/{$result['edit_link']}";
$vars = array(
'@operation' => $result['operation'],
'@vterm' => "{$vocabulary_name}:{$term_name}",
);
Message::make($message, $vars, WATCHDOG_INFO, 1, $link);
$completed["{$vocabulary_name}:{$term_name}"] = $result['operation'];
}
}
} catch (\Exception $e) {
$vars = array(
'!error' => method_exists($e, 'logMessage') ? $e
->logMessage() : $e
->getMessage(),
);
if (!method_exists($e, 'logMessage')) {
// Not logged yet, so log it.
$message = 'Term import denied because: !error';
Message::make($message, $vars, WATCHDOG_ERROR);
}
// Output a summary before shutting this down.
$done = HudtInternal::getSummary($completed, $total_requested, 'Imported');
Message::make($done, array(), FALSE, 1);
throw new HudtException('Caught Exception: Update aborted! !error', $vars, WATCHDOG_ERROR, FALSE);
}
$done = HudtInternal::getSummary($completed, $total_requested, 'Imported Terms');
return $done;
}
/**
* Verifies that that import can be used based on available module.
*
* @return bool
* TRUE If the import can be run.
*
* @throws \DrupalUpdateException if it can not be run.
*/
public static function canImport() {
Check::canUse('taxonomy');
return TRUE;
}
/**
* Checks to see if Terms can be exported.
*
* @return bool
* TRUE if can be exported.
*/
public static function canExport() {
// Uses drupal_var_export which needs to be included.
$file = DRUPAL_ROOT . '/includes/utility.inc';
require_once $file;
Check::canUse('taxonomy');
return TRUE;
}
/**
* Delete a Term.
*
* @param string $term_name
* The human term name to be deleted.
* @param string $vocabulary_name
* The human or machine name of the Vocabulary the term resides in.
*
* @return string
* A string message to return to the hook_update_N if no exceptions.
*
* @throws HudtException
* Message throwing exception if criteria is deemed unfit to declare the
* term deletion a success.
*/
public static function delete($term_name, $vocabulary_name) {
try {
// Make sure we can use taxonomy and call the functions needed.
Check::canUse('taxonomy');
Check::canCall('taxonomy_term_delete');
Check::notEmpty('$term_name', $term_name);
Check::notEmpty('$vocabulary_name', $vocabulary_name);
$vars = array(
'@term_name' => $term_name,
'@vocabulary_name' => $vocabulary_name,
);
// Does it exist? If it does, we can delete it.
$term = self::loadByName($term_name, $vocabulary_name, FALSE);
if (empty($term)) {
// The Term does not exist. Skip the delete.
$vars['@exists_text'] = "does not exist";
$vars['@action_taken'] = "so was not deleted. Skipping Terms::delete";
}
else {
// The Term does exist.
$vars['@tid'] = $term->tid;
$vars['@exists_text'] = "exists";
// Delete the Term.
$delete_status = taxonomy_term_delete($term->tid);
$vars['@deleted_status'] = $delete_status;
// Was it deleted?
if ($delete_status === SAVED_DELETED) {
$vars['@action_taken'] = "was deleted";
// Deleted, but verify it stayed deleted.
// The results are static cached, so may mislead us with old info.
$term = self::loadByName($term_name, $vocabulary_name, FALSE);
if (!empty($term)) {
// Something went wrong. The Term did not stay deleted.
// Throw exception.
$message = "Deleting the Term '@vocabulary_name:@term_name' did no go as expected. It @action_taken but still @exists_text.";
throw new HudtException($message, $vars, WATCHDOG_ERROR, TRUE);
}
}
else {
// Failed to delete, throw an exception.
$message = "Deleting the Term '@vocabulary_name:@term_name' did not go as expected. Status:'@deleted_status'";
throw new HudtException($message, $vars, WATCHDOG_ERROR, TRUE);
}
}
} catch (\Exception $e) {
$vars['!error'] = method_exists($e, 'logMessage') ? $e
->logMessage() : $e
->getMessage();
if (!method_exists($e, 'logMessage')) {
// Not logged yet, so log it.
$message = "Terms::delete Term '@vocabulary_name:@term_name' failed because: !error";
Message::make($message, $vars, WATCHDOG_ERROR);
}
throw new HudtException('Caught Exception: Update aborted! !error', $vars, WATCHDOG_ERROR, FALSE);
}
$return_msg = Message::make("Term @vocabulary_name:@term_name(@tid) @exists_text @action_taken.", $vars, WATCHDOG_INFO, 1);
return $return_msg;
}
/**
* Gets the Term Name from the $vocabulary_term_names. Strict.
*
* @param string $vocabulary_term_name
* A string made up of "Vocabulary Name|Term Name" (not machine names).
*
* @return string
* The Term Name.
*/
private static function getTermName($vocabulary_term_name) {
$parts = explode('|', $vocabulary_term_name);
$term_name = !empty($parts[1]) ? $parts[1] : '';
Check::notEmpty('$term_name', $term_name);
return $term_name;
}
/**
* Gets the Vocabulary Name from the $vocabulary_term_names. Strict.
*
* @param string $vocabulary_term_name
* A string made up of "Vocabulary Name|Term Name" (not machine names).
*
* @return string
* The Vocabulary Name.
*/
private static function getVocabularyName($vocabulary_term_name) {
$parts = explode('|', $vocabulary_term_name);
$vocabulary_name = !empty($parts[0]) ? $parts[0] : '';
Check::notEmpty('$vocabulary_name', $vocabulary_name);
return $vocabulary_name;
}
/**
* Normalizes a path name to be the filename. Overrides HudtInternal method.
*
* @param string $quasi_name
* A path to normalize and create a filename from.
*
* @return string
* A string resembling a filename with hyphens and .txt.
*/
private static function normalizeFileName($quasi_name) {
$items = array(
// Remove spaces.
' ' => 'sSs',
// Convert to separator.
'|' => 'zZz',
);
$file_name = str_replace(array_keys($items), array_values($items), $quasi_name);
$file_name = "{$file_name}.txt";
return $file_name;
}
/**
* Normalizes unique string that identifies the vocabulary and term names.
*
* @param string $vocabulary_name
* The name of the vocabulary.
* @param string $term_name
* The name of the term.
*
* @return string
* A string resembling a machine name with underscores.
*/
private static function normalizeVocTermName($vocabulary_name, $term_name) {
$items = array(
// Remove spaces.
' ' => 'sSs',
);
$vocabulary_name = str_replace(array_keys($items), array_values($items), $vocabulary_name);
$term_name = str_replace(array_keys($items), array_values($items), $term_name);
return "{$vocabulary_name}zZz{$term_name}";
}
/**
* Validated Updates/Imports one term from the contents of an import file.
*
* @param string $term_import
* The term object to import.
* @param string $vocabulary_name
* The name of the vocabulary to import/update.
* @param string $term_name
* The name of the term to import/update
*
* @return array
* Contains the elements page, operation, and edit_link.
*
* @throws HudtException
* In the event of something that fails the import.
*/
private static function importOne($term_import, $vocabulary_name, $term_name) {
// Determine if the term exists in that vocabulary.
$term_existing = Terms::loadByName($term_name, $vocabulary_name);
$msg_vars = array(
'@vocabulary_name' => $vocabulary_name,
'@term_name' => $term_name,
);
self::unpackParents($term_import);
if (!empty($term_existing)) {
// A term already exists. Update it.
$operation = t('Updated');
$op = 'update';
$saved_term = self::updateExistingTerm($term_import, $term_existing);
}
else {
// No term exists in this Vocabulary. Create It.
$operation = t('Created');
$op = 'create';
$saved_term = self::createNewTerm($term_import);
}
$msg_vars['@operation'] = $operation;
// Begin validation.
// Case race. First to evaluate TRUE wins.
switch (TRUE) {
case empty($saved_term->tid):
// Save did not complete. No tid granted.
$message = '@operation of @language: @vocabulary_name : @vocabulary_term failed: The saved term ended up with no tid.';
$valid = FALSE;
break;
case $saved_term->name !== $term_import->name:
// Simple validation check to see if the saved title matches.
$msg_vars['@intended_title'] = $term_import->title;
$msg_vars['@saved_title'] = $saved_term->title;
$message = '@operation failure: The term names do not match. Intended title: @intended_title Saved Title: @saved_title';
$valid = FALSE;
break;
// @TODO Consider other Term properties that could be validated without
// leading to false negatives.
default:
// Passed all the validations, likely it is valid.
$valid = TRUE;
}
if (!$valid) {
// Validation failed so perform rollback.
self::rollbackImport($op, $saved_term);
throw new HudtException($message, $msg_vars, WATCHDOG_ERROR, TRUE);
}
$return = array(
'term' => $saved_term,
'operation' => "{$operation}: term/{$saved_term->tid}",
'edit_link' => "taxonomy/term/{$saved_term->tid}/edit",
);
return $return;
}
/**
* Rolls back a term to its original state or deletes a creation.
*
* @param string $op
* The crud op that was performed.
* @param object $term
* The term's original state to roll back to.
*/
private static function rollbackImport($op, $term) {
$variables = array(
'@tid' => $term->tid,
'@name' => $term->name,
'@op' => $op,
);
if ($op === 'create') {
// Op was a create, so delete the Term if there was one created.
if (!empty($term->tid)) {
// The presence of tid indicates one was created, so delete it.
taxonomy_term_delete($term->tid);
$msg = "Term @name(@tid) was @opd but failed validation and was deleted.";
}
else {
// Create was attempted but not completed. Nothing to roll back.
$msg = "The term:@name failed @op and had nothing to roll back.";
}
}
else {
// Op was an update, so attempt to swap in the original term.
if (!empty($term->original)) {
// The original values are available, so re-set them.
taxonomy_term_save($term->original);
$msg = "Term @name(@tid) @opd but failed validation, The term was rolled back to its previous state. Verify manually.";
}
else {
$msg = "Term @name(@tid) @opd but failed validation. An original copy was not available to roll back. Verify manually";
}
}
Message::make($msg, $variables, WATCHDOG_INFO, 2);
}
/**
* Create a Term from the imported object.
*
* @param object $term
* The Term object from the import file.
*
* @return object
* The resulting Term from term_save, broken free of reference to $term.
*/
private static function createNewTerm($term) {
$saved_term = clone $term;
// Remove the tid as it will be assigned one upon save.
unset($saved_term->tid);
// Need to lookup the vocabulary since vid may be different.
$vocabulary = Vocabularies::loadByMachineName($saved_term->vocabulary_machine_name, TRUE);
$saved_term->vid = $vocabulary->vid;
// @TODO Need to add handling for field collections.
// @TODO Need to add handling for entity references.
taxonomy_term_save($saved_term);
return $saved_term;
}
/**
* Update an existing term from the imported object.
*
* @param object $term_import
* The Term object from the import file.
* @param object $term_existing
* The Term object for the existing Term.
*
* @return object
* The Term from term_save, broken free of reference to $term_import.
*/
private static function updateExistingTerm($term_import, $term_existing) {
$saved_term = clone $term_import;
$saved_term->tid = $term_existing->tid;
// @TODO Need to add handling for field collections.
// @TODO Need to add handling for entity reference.
// Entity reference works as long as the entity being referenced exists and
// has the same entity id as the one being referenced.
taxonomy_term_save($saved_term);
return $saved_term;
}
/**
* Load a term by name.
*
* @param string $term_name
* The human readable name of a Term.
* @param string $vocabulary_name
* The human readable or machine name of a Vocabulary.
* @param bool $strict
* Flag to cause exception to be thrown if not able to load.
*
* @return mixed
* (object) term object if found.
* (bool) FALSE.
*
* @throws HudtException
* In the event of something that fails the import.
*/
public static function loadByName($term_name, $vocabulary_name, $strict = FALSE) {
Check::canUse('taxonomy');
Check::canCall('taxonomy_get_term_by_name');
$variables = array(
'@name' => $term_name,
'@vocab' => $vocabulary_name,
);
// Convert vocabulary name to machine name.
$vocabulary = Vocabularies::loadByName($vocabulary_name, FALSE);
$vocabulary = !empty($vocabulary) ? $vocabulary : Vocabularies::loadByMachineName($vocabulary_name, FALSE);
if (empty($vocabulary)) {
// The Vocabulary was not found, throw exception, call this a failure.
// Even if not $strict this should throw an exception because you need a
// vocabulary to look for a term.,
$message = "There is no Vocabulary '@vocab'. It could not be loaded.";
throw new HudtException($message, $variables, WATCHDOG_ERROR, TRUE);
}
$term = array_shift(taxonomy_get_term_by_name($term_name, $vocabulary->machine_name));
if (!empty($term)) {
return $term;
}
else {
if ($strict) {
// The Term was not found, throw exception, call this a failure.
$message = "There is no Term '@name'in @vocab. It could not be loaded.";
throw new HudtException($message, $variables, WATCHDOG_ERROR, TRUE);
}
else {
return FALSE;
}
}
}
/**
* Exports a single term based on its tid. (Typically called from Drush).
*
* @param string $tid
* The tid of the Term to export.
*
* @return string
* The URI of the item exported, or a failure message.
*/
public static function export($tid) {
$t = get_t();
try {
Check::notEmpty('tid', $tid);
Check::isNumeric('tid', $tid);
self::canExport();
$msg_return = '';
// Load the term if it exists.
$term = taxonomy_term_load($tid);
Check::notEmpty('term', $term);
Check::notEmpty('term name', $term->name);
Check::notEmpty('vocabulary_machine_name', $term->vocabulary_machine_name);
// Get the vocabulary name.
$vocabulary = Vocabularies::loadByMachineName($term->vocabulary_machine_name, TRUE);
Check::notEmpty('vocabulary_name', $vocabulary->name);
$voc_term_name = self::normalizeVocTermName($vocabulary->name, $term->name);
$file_name = self::normalizeFileName($voc_term_name);
$storage_path = HudtInternal::getStoragePath('term');
$file_uri = DRUPAL_ROOT . '/' . $storage_path . $file_name;
// Add parents. These will be unpacked at import.
$term->parents = taxonomy_get_parents($tid);
// Get pathauto path.
if (!empty($term->path) && isset($term->path['pathauto']) && $term->path['pathauto'] === '0') {
// Lookup the alias and store it.
$alias = drupal_get_path_alias("taxonomy/term/{$tid}");
$term->path['alias'] = $alias;
}
// Made it this far, it exists, so export it.
$export_contents = drupal_var_export($term);
// Save the file.
$msg_return = HudtInternal::writeFile($file_uri, $export_contents);
} catch (\Exception $e) {
// Any errors from this command do not need to be watchdog logged.
$e->logIt = FALSE;
$vars = array(
'!error' => method_exists($e, 'logMessage') ? $e
->logMessage() : $e
->getMessage(),
);
$msg_error = $t("Caught exception: !error", $vars);
}
if (!empty($msg_error)) {
drush_log($msg_error, 'error');
}
return !empty($msg_return) ? $msg_return : $msg_error;
}
/**
* Unpacks import ->parents and creates ->parent for importing any parents.
*
* @param object $term_import
* The term object from the import file.
*
* @throws HudtException
* In the event that the parent does not exist.
*/
private static function unpackParents(&$term_import) {
$new_parents = array();
if (!empty($term_import->parents)) {
// We have parents to deal with.
foreach ($term_import->parents as $parent) {
// Look up the parent in this environment to get the tid.
$local_parent = self::loadByName($parent->name, $parent->vocabulary_machine_name);
if (empty($local_parent)) {
// The parent does not exist locally, log an error.
$variables = array(
'@vocab_name' => $term_import->vocabulary_machine_name,
'@term_name' => $term_import->name,
'@parent_name' => $parent->name,
);
$message = "Import of @vocab_name:@term_name failed because the parent:@parent_name does not exist.";
throw new HudtException($message, $variables, WATCHDOG_ERROR, TRUE);
}
else {
// The parent exists locally, so use its tid.
$new_parents[] = $local_parent->tid;
}
}
}
if (!empty($new_parents)) {
// Add the local parents.
$term_import->parent = $new_parents;
}
else {
// Needs to be array(0) in order to set <root> as the parent.
$term_import->parent = array(
0,
);
}
unset($term_import->parents);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Terms:: |
public static | function |
Checks to see if Terms can be exported. Overrides ExportInterface:: |
|
Terms:: |
public static | function |
Verifies that that import can be used based on available module. Overrides ImportInterface:: |
|
Terms:: |
private static | function | Create a Term from the imported object. | |
Terms:: |
public static | function | Delete a Term. | |
Terms:: |
public static | function |
Exports a single term based on its tid. (Typically called from Drush). Overrides ExportInterface:: |
|
Terms:: |
private static | function | Gets the Term Name from the $vocabulary_term_names. Strict. | |
Terms:: |
private static | function | Gets the Vocabulary Name from the $vocabulary_term_names. Strict. | |
Terms:: |
public static | function |
Perform the unique steps necessary to import terms items from export files. Overrides ImportInterface:: |
|
Terms:: |
private static | function | Validated Updates/Imports one term from the contents of an import file. | |
Terms:: |
public static | function | Load a term by name. | |
Terms:: |
private static | function | Normalizes a path name to be the filename. Overrides HudtInternal method. | |
Terms:: |
private static | function | Normalizes unique string that identifies the vocabulary and term names. | |
Terms:: |
private static | function | Rolls back a term to its original state or deletes a creation. | |
Terms:: |
private static | function | Unpacks import ->parents and creates ->parent for importing any parents. | |
Terms:: |
private static | function | Update an existing term from the imported object. |