Thanks to @Alireza Tabatabaeian for this. I'm simply posting my code that I used based on that answer.
To the taxonomy that I wanted to apply the constraint, I added a field_term_ref_author entity reference to the user.
mymodule.php
function MYMOUDLE_taxonomy_term_presave(TermInterface $term) {
$vocabulary = $term->bundle();
switch ($vocabulary) {
case 'my_vocab_type':
if ($term->isNew()) {
// Set the author (via custom field).
$current_user = \Drupal::currentUser()->id();
$term->set('field_term_ref_author', $current_user);
}
break;
function MYMODULE_entity_base_field_info_alter(&$fields, $entity_type) {
if ($entity_type->id() === 'taxonomy_term') {
if (isset($fields['name'])) {
$fields['name']->addConstraint('TermNameUniquePerUser');
}
}
}
mymodule/src/Plugin/Validation/Constraint/TermNameUniquePerUser.php
<?php
namespace Drupal\MYMODULE\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Checks that the submitted value is unique to the user.
*
* @Constraint(
* id = "TermNameUniquePerUser",
* label = @Translation("Term Name Unique Per User", context = "Validation"),
* type = "string"
* )
*/
class TermNameUniquePerUser extends Constraint {
// The message that will be shown if the value is not unique.
public $notUnique = 'You already created a term named %value.';
}
mymodule/src/Plugin/Validation/Constraint/TermNameUniquePerUserValidator.php
<?php
namespace Drupal\MYMODULE\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the TermNameUniquePerUser constraint.
*/
class TermNameUniquePerUserValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate($items, Constraint $constraint) {
foreach ($items as $item) {
// Next check if the value is unique.
if (!$this->isUnique($item->value)) {
$this->context->addViolation($constraint->notUnique, ['%value' => $item->value]);
}
}
}
/**
* Is unique?
*
* @param string $value
*/
private function isUnique($value) {
$uid = \Drupal::currentUser()->id();
$term_name_query = \Drupal::entityQuery('taxonomy_term')
->condition('name', $value, '=')
->condition('field_term_ref_author', $uid);
// If the query has any results, then it is not unique.
// So we should return the opposite of whether there are any results.
return !($term_name_query->execute());
}
}
The good thing about writing the entity query like this is that if the taxonomy vocabulary does not have a field_term_ref_author
field, there will never be any results, so the constraint will evaluate correctly with terms that are in bundles not subject to the constraint (although this will result in a wasted database query.)