diff --git a/composer.json b/composer.json index 566e08e1f55c1ad4c4d7264887214fbc2a386e65..15dd9a69b235a41f491d063c9cf6e07f344f8eb1 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,7 @@ "keywords": ["Drupal"], "minimum-stability": "dev", "require": { - "drupal/core": "^9 || ^10" + "drupal/core": "^9 || ^10", + "php": ">=8.1" } } diff --git a/src/Controller/TypedResourceController.php b/src/Controller/TypedResourceController.php index 3f764dd25a27943d048b1bbdfb75311757ac6474..45ab35b9dc1338710ee12f6da6a04c12a8c8ad2d 100644 --- a/src/Controller/TypedResourceController.php +++ b/src/Controller/TypedResourceController.php @@ -9,33 +9,27 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; /** - * Class TypedResourceController. + * Class to provide a controller for autocomplete look-ups. * * @package Drupal\jsonapi_reference\Controller */ class TypedResourceController extends ControllerBase { - /** - * Client to talk to our JSON:API entrypoint. - * - * @var \Drupal\jsonapi_reference\JsonApiClientInterface - */ - protected $client; - /** * TypedResourceController constructor. * * @param \Drupal\jsonapi_reference\JsonApiClientInterface $client * Client to talk to our JSON:API entrypoint. */ - public function __construct(JsonApiClientInterface $client) { - $this->client = $client; + public function __construct( + protected readonly JsonApiClientInterface $client, + ) { } /** * {@inheritdoc} */ - public static function create(ContainerInterface $container) { + public static function create(ContainerInterface $container): TypedResourceController { return new static($container->get('jsonapi_reference.jsonapi_client')); } @@ -51,11 +45,11 @@ class TypedResourceController extends ControllerBase { * * @return \Symfony\Component\HttpFoundation\JsonResponse * JsonResponse containing array of search results. - * Each element in the array conatins a value and label showing the matching + * Each element in the array contains a value and label showing the matching * values of the autocomplete attribute, and the corresponding objects * JSON:API ID. */ - public function autocomplete(Request $request, $resource_object_type, $autocomplete_attribute) { + public function autocomplete(Request $request, string $resource_object_type, string $autocomplete_attribute): JsonResponse { $response = []; $query = $request->query->get('q'); $search_results = $this->client->search($resource_object_type, $autocomplete_attribute, $query); diff --git a/src/Form/JsonApiReferenceConfigForm.php b/src/Form/JsonApiReferenceConfigForm.php index ae0f3b654efc2b8d1d4f20b32369e744f226f611..75bc402640e46e754def9dde4e40d53215d09eef 100644 --- a/src/Form/JsonApiReferenceConfigForm.php +++ b/src/Form/JsonApiReferenceConfigForm.php @@ -6,14 +6,14 @@ use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; /** - * Class JsonApiReferenceConfigForm. + * Class to provide a form for configuring the module. */ class JsonApiReferenceConfigForm extends ConfigFormBase { /** * {@inheritdoc} */ - protected function getEditableConfigNames() { + protected function getEditableConfigNames(): array { return [ 'jsonapi_reference.settings', ]; @@ -22,14 +22,14 @@ class JsonApiReferenceConfigForm extends ConfigFormBase { /** * {@inheritdoc} */ - public function getFormId() { + public function getFormId(): string { return 'json_api_reference_config_form'; } /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state) { + public function buildForm(array $form, FormStateInterface $form_state): array { $config = $this->config('jsonapi_reference.settings'); $form['entrypoint'] = [ '#type' => 'textfield', @@ -61,7 +61,7 @@ class JsonApiReferenceConfigForm extends ConfigFormBase { /** * {@inheritdoc} */ - public function submitForm(array &$form, FormStateInterface $form_state) { + public function submitForm(array &$form, FormStateInterface $form_state): void { parent::submitForm($form, $form_state); $config = $this->config('jsonapi_reference.settings'); diff --git a/src/JsonApiClient.php b/src/JsonApiClient.php index 7b8d6df30fbc0fafa1beae4fa1c93be1e7fc344e..a2a0aa15b8e417ba85cf1826ead14b2c0acdfdaa 100644 --- a/src/JsonApiClient.php +++ b/src/JsonApiClient.php @@ -9,46 +9,26 @@ use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\RequestOptions; /** - * Class JsonApiClient. + * Client for accessing data through JSON:API. * * @package Drupal\jsonapi_reference */ class JsonApiClient implements JsonApiClientInterface { - /** - * Client for querying the JSON:API entrypoint. - * - * @var \GuzzleHttp\ClientInterface - */ - protected $httpClient; - /** * Entrypoint to call to retrieve JSONAPI objects. - * - * @var string */ - protected $entrypoint; + protected string $entrypoint; /** * Username to use when authenticating to the entrypoint. - * - * @var string */ - protected $username; + protected string $username; /** * Password to use when authenticating to the entrypoint. - * - * @var string */ - protected $password; - - /** - * Logger channel. - * - * @var \Drupal\Core\Logger\LoggerChannelInterface - */ - protected $loggerChannel; + protected string $password; /** * JsonApiClient constructor. @@ -60,18 +40,19 @@ class JsonApiClient implements JsonApiClientInterface { * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory * Config factory. */ - public function __construct(ClientInterface $httpClient, LoggerChannelInterface $loggerChannel, ConfigFactoryInterface $configFactory) { - $this->httpClient = $httpClient; + public function __construct( + protected readonly ClientInterface $httpClient, + protected readonly LoggerChannelInterface $loggerChannel, + ConfigFactoryInterface $configFactory) { $this->entrypoint = $configFactory->get('jsonapi_reference.settings')->get('endpoint'); $this->username = $configFactory->get('jsonapi_reference.settings')->get('username'); $this->password = $configFactory->get('jsonapi_reference.settings')->get('password'); - $this->loggerChannel = $loggerChannel; } /** * {@inheritdoc} */ - public function listResourceObjectTypes() { + public function listResourceObjectTypes(): array { return array_keys($this->getResourceObjectTypes()); } @@ -83,7 +64,7 @@ class JsonApiClient implements JsonApiClientInterface { * Each object contains a href element pointing to the JSON:API entrypoint * for that specific type. */ - protected function getResourceObjectTypes() { + protected function getResourceObjectTypes(): array { try { $response = $this->httpClient->request('get', $this->entrypoint, $this->getRequestOptions()); } @@ -102,7 +83,7 @@ class JsonApiClient implements JsonApiClientInterface { /** * {@inheritdoc} */ - public function search($resource_object_type, $search_attribute, $query) { + public function search($resource_object_type, $search_attribute, $query): array { $path = $this->getPathForResourceObjectType($resource_object_type); if (!$path) { $this->loggerChannel->error('No path for resource object type @type found at JSON:API entrypoint @entrypoint.', [ @@ -149,7 +130,7 @@ class JsonApiClient implements JsonApiClientInterface { /** * {@inheritdoc} */ - public function searchById($resource_object_type, $id) { + public function searchById(string $resource_object_type, string $id): object|NULL { $path = $this->getPathForResourceObjectType($resource_object_type); if (!$path) { return NULL; @@ -163,10 +144,8 @@ class JsonApiClient implements JsonApiClientInterface { $this->loggerChannel->error($e->getMessage()); return NULL; } - $response = json_decode($response->getBody()->getContents()); - - return $response; + return json_decode($response->getBody()->getContents()); } /** @@ -175,7 +154,7 @@ class JsonApiClient implements JsonApiClientInterface { * @return array * Array of request options, including headers and auth. */ - protected function getRequestOptions() { + protected function getRequestOptions(): array { $options = [ RequestOptions::HEADERS => [ 'Content-Type' => 'application/json', @@ -202,7 +181,7 @@ class JsonApiClient implements JsonApiClientInterface { * URI for the given object type. * Empty string if none found. */ - protected function getPathForResourceObjectType($resource_object_type) { + protected function getPathForResourceObjectType(string $resource_object_type): string { $types = $this->getResourceObjectTypes(); if (!array_key_exists($resource_object_type, $types)) { diff --git a/src/JsonApiClientInterface.php b/src/JsonApiClientInterface.php index fb97fcd3a6b077947fdae5a160acb482d311d9f6..8e2e98e435ee67aa8704c955afff890cbe06fb22 100644 --- a/src/JsonApiClientInterface.php +++ b/src/JsonApiClientInterface.php @@ -3,7 +3,7 @@ namespace Drupal\jsonapi_reference; /** - * Interface JsonApiClientInterface. + * Interface for JSON:API clients. * * @package Drupal\jsonapi_reference */ @@ -22,7 +22,7 @@ interface JsonApiClientInterface { * . * ]; */ - public function listResourceObjectTypes(); + public function listResourceObjectTypes(): array; /** * Search the entrypoint for a resource object of a given type. @@ -39,13 +39,13 @@ interface JsonApiClientInterface { * $search_attribute containing $query, and the values are the IDs of the * corresponding resource objects. */ - public function search($resource_object_type, $search_attribute, $query); + public function search(string $resource_object_type, string $search_attribute, string $query): array; /** * Looks up a resource object of a given type by ID. * * @param string $resource_object_type - * Object type, eg node--article. + * Object type, e.g. node--article. * @param string $id * GUID of object we are searching for. * @@ -58,6 +58,6 @@ interface JsonApiClientInterface { * properties. * Returns NULL if no object found. */ - public function searchById($resource_object_type, $id); + public function searchById(string $resource_object_type, string $id): object|NULL; } diff --git a/src/Plugin/Field/FieldType/TypedResourceObjectItem.php b/src/Plugin/Field/FieldType/TypedResourceObjectItem.php index c9855a9f60047f2e72f899ff5ea0ef6f78497490..03a3c26c571f8c5fa15311e8ae11e5045f1b7f92 100644 --- a/src/Plugin/Field/FieldType/TypedResourceObjectItem.php +++ b/src/Plugin/Field/FieldType/TypedResourceObjectItem.php @@ -26,7 +26,7 @@ class TypedResourceObjectItem extends FieldItemBase { /** * {@inheritdoc} */ - public static function defaultStorageSettings() { + public static function defaultStorageSettings(): array { return [ 'resource_object_type' => '', ] + parent::defaultStorageSettings(); @@ -35,7 +35,7 @@ class TypedResourceObjectItem extends FieldItemBase { /** * {@inheritdoc} */ - public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { + public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition): array { // Prevent early t() calls by using the TranslatableMarkup. $properties['value'] = DataDefinition::create('string') ->setLabel(new TranslatableMarkup('ID')) @@ -48,8 +48,8 @@ class TypedResourceObjectItem extends FieldItemBase { /** * {@inheritdoc} */ - public static function schema(FieldStorageDefinitionInterface $field_definition) { - $schema = [ + public static function schema(FieldStorageDefinitionInterface $field_definition): array { + return [ 'columns' => [ 'value' => [ 'type' => 'varchar_ascii', @@ -58,14 +58,12 @@ class TypedResourceObjectItem extends FieldItemBase { ], ], ]; - - return $schema; } /** * {@inheritdoc} */ - public function getConstraints() { + public function getConstraints(): array { $constraints = parent::getConstraints(); $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager(); // @todo: do we know these will all be GUIDs? What about a non-Drupal @@ -78,7 +76,7 @@ class TypedResourceObjectItem extends FieldItemBase { /** * {@inheritdoc} */ - public static function generateSampleValue(FieldDefinitionInterface $field_definition) { + public static function generateSampleValue(FieldDefinitionInterface $field_definition): array { $values['value'] = \Drupal::service('uuid')->generate(); return $values; } @@ -86,10 +84,10 @@ class TypedResourceObjectItem extends FieldItemBase { /** * {@inheritdoc} */ - public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) { + public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data): array { $elements = []; - /* @var $client \Drupal\jsonapi_reference\JsonApiClientInterface */ + /** @var \Drupal\jsonapi_reference\JsonApiClientInterface $client */ $client = \Drupal::service('jsonapi_reference.jsonapi_client'); $types = $client->listResourceObjectTypes(); @@ -108,8 +106,10 @@ class TypedResourceObjectItem extends FieldItemBase { /** * {@inheritdoc} + * + * @throws \Drupal\Core\TypedData\Exception\MissingDataException */ - public function isEmpty() { + public function isEmpty(): bool { $value = $this->get('value')->getValue(); return $value === NULL || $value === ''; } diff --git a/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php b/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php index 3cb1d4773d20e706c9277c15e291c4347b1d4797..be9a592e34a7c0677292ecd60f8b8a1fc4163d7a 100644 --- a/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php +++ b/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php @@ -23,7 +23,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase { /** * {@inheritdoc} */ - public static function defaultSettings() { + public static function defaultSettings(): array { return [ 'autocomplete_attribute' => 'title', ] + parent::defaultSettings(); @@ -32,7 +32,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase { /** * {@inheritdoc} */ - public function settingsForm(array $form, FormStateInterface $form_state) { + public function settingsForm(array $form, FormStateInterface $form_state): array { $element['autocomplete_attribute'] = [ '#type' => 'textfield', '#title' => $this->t('Autocomplete attribute'), @@ -47,7 +47,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase { /** * {@inheritdoc} */ - public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array { $field = $items->getFieldDefinition(); $field_label = $field->getLabel(); $field_settings = $field->getSettings(); @@ -87,11 +87,11 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase { * @return mixed * The value to assign to the element. */ - public static function valueCallback(array $element, $input, FormStateInterface $form_state) { + public static function valueCallback(array $element, mixed $input, FormStateInterface $form_state): mixed { $value = ''; - // If we're rebuilding the form due to AJAX (eg adding another item to the - // field), just keep the input as is. + // If we're rebuilding the form due to AJAX (for example, adding another + // item to the field), just keep the input as is. if ($form_state->isRebuilding() && $input) { return $input; } @@ -116,7 +116,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase { // Otherwise we need to convert the other way - for the given id.. if ($element['#default_value']) { // ..look it up via jsonapi and format with the title. - /* @var $client \Drupal\jsonapi_reference\JsonApiClientInterface */ + /** @var \Drupal\jsonapi_reference\JsonApiClientInterface $client */ $client = \Drupal::service('jsonapi_reference.jsonapi_client'); $jsonapi_object = $client->searchById($element['#resource_object_type'], $element['#default_value']); if ($jsonapi_object) {