username-1.0.x-dev/src/Controller/UsernameAutocompleteController.php
src/Controller/UsernameAutocompleteController.php
<?php
namespace Drupal\username\Controller;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Site\Settings;
use Drupal\system\Controller\EntityAutocompleteController;
use Drupal\user\Entity\User;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Defines a route controller for entity autocomplete form elements.
*/
class UsernameAutocompleteController extends EntityAutocompleteController {
/**
* {@inheritdoc}
*/
public function handleAutocomplete(Request $request, $target_type, $selection_handler, $selection_settings_key) {
// Check if the autocomplete request is for user entities.
$defaults = [
'default:user',
'default',
];
if ($target_type !== 'user' || !in_array($selection_handler, $defaults, TRUE)) {
return parent::handleAutocomplete($request, $target_type, $selection_handler, $selection_settings_key);
}
$matches = [];
if ($input = $request->query->get('q')) {
// Extract and lowercase the typed string.
$typed_string = Tags::explode($input);
$typed_string = mb_strtolower(array_pop($typed_string));
$selection_settings = $this->keyValue->get($selection_settings_key, FALSE);
if ($selection_settings !== FALSE) {
// Verify the selection settings key.
$selection_settings_hash = Crypt::hmacBase64(serialize($selection_settings) . $target_type . $selection_handler, Settings::getHashSalt());
if ($selection_settings_hash !== $selection_settings_key) {
throw new AccessDeniedHttpException('Invalid selection settings key.');
}
}
else {
throw new AccessDeniedHttpException();
}
// Get matching user entities based on the typed string.
$matches = $this->getMatches($selection_settings, $typed_string);
}
return new JsonResponse($matches);
}
/**
* Gets matched labels based on a given search string.
*
* @param array $selection_settings
* An array of settings that will be passed to the selection handler.
* @param string $string
* (optional) The label of the entity to query by.
*
* @return array
* An array of matched entity labels, in the format required by the AJAX
* autocomplete API (e.g., array('value' => $value, 'label' => $label)).
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* Thrown when the current user doesn't have access to the specified entity.
*/
protected function getMatches(array $selection_settings, $string = '') {
$matches = [];
if ($string === '') {
return $matches;
}
// Prepare the query to find matching user entities.
$match_operator = !empty($selection_settings['match_operator']) ? $selection_settings['match_operator'] : 'CONTAINS';
$include_anonymous = $selection_settings['include_anonymous'] ?? TRUE;
$connection = \Drupal::database();
$query = $connection->select('users_field_data', 'u')
->fields('u', ['uid'])
->leftJoin('user__names', 'un', 'u.uid = un.uid');
// Add conditions to the query based on the match operator.
if ($match_operator == 'CONTAINS') {
$query->condition((new Condition('OR'))
->condition('un.name', '%' . $connection->escapeLike($string) . '%', 'LIKE')
->condition('u.name', '%' . $connection->escapeLike($string) . '%', 'LIKE')
);
}
else {
$query->condition((new Condition('OR'))
->condition('un.name', $connection->escapeLike($string) . '%', 'LIKE')
->condition('u.name', $connection->escapeLike($string) . '%', 'LIKE')
);
}
// Exclude anonymous users if specified.
if ($include_anonymous == FALSE) {
$query->condition('u.uid', 0, '>');
}
// Limit the number of results returned.
$query->range(0, 10);
$uids = $query->execute()->fetchCol();
$accounts = User::loadMultiple($uids);
// Format the matches for the autocomplete response.
/** @var \Drupal\user\Entity\User $account */
foreach ($accounts as $account) {
$matches[] = [
'value' => $this->t('@name (@id)', [
'@name' => $account->getDisplayName(),
'@id' => $account->id(),
]),
'label' => $this->t('@name (@username)', [
'@name' => $account->getDisplayName(),
'@username' => $account->getAccountName(),
]),
];
}
return $matches;
}
}
