diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ec2adb..a7906c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,13 +6,15 @@ jobs: strategy: fail-fast: false matrix: - php_version: ["7.4", "8.0", "8.1"] + php_version: ["7.4", "8.0", "8.1", "8.2"] drupal_version: ["9", "10"] exclude: - php_version: "7.4" drupal_version: "10" - php_version: "8.0" drupal_version: "10" + - php_version: "8.2" + drupal_version: "9" env: PHP_VERSION: ${{ matrix.php_version }} DRUPAL_VERSION: ${{ matrix.drupal_version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e57de1..3635a91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [2.2.2] +### Added + * [#258](https://github.com/jhedstrom/DrupalDriver/pull/258) Document testing of the Drupal Driver locally for contributors. +### Fixed + * [#260](https://github.com/jhedstrom/DrupalDriver/pull/260) Fix taxonomy term creation for hierarchies. ## [2.2.1] * [#250](https://github.com/jhedstrom/DrupalDriver/pull/250) Drupal 10 compatibility. ## [2.2.0] @@ -22,7 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * [#232](https://github.com/jhedstrom/DrupalDriver/pull/232) Fix type in ImageHandler. ## [2.1.0] ### Added - * [#186](https://github.com/jhedstrom/DrupalDriver/issues/168) Provide a method to directly authenticate on Drupal 8. + * [#186](https://github.com/jhedstrom/DrupalDriver/issues/186) Provide a method to directly authenticate on Drupal 8. ### Changed * Remove testing on PHP 5.6, added testing on PHP 7.3 and 7.4. * [#214](https://github.com/jhedstrom/DrupalDriver/pull/214) Fix D8 deprectations. @@ -112,7 +117,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). docblock for `CoreInterface::roleCreate`. -[Unreleased]: https://github.com/jhedstrom/DrupalDriver/compare/v2.2.0...HEAD +[Unreleased]: https://github.com/jhedstrom/DrupalDriver/compare/v2.2.2...HEAD +[2.2.2]: https://github.com/jhedstrom/DrupalDriver/compare/v2.2.1...v2.2.2 +[2.2.1]: https://github.com/jhedstrom/DrupalDriver/compare/v2.2.0...v2.2.1 [2.2.0]: https://github.com/jhedstrom/DrupalDriver/compare/v2.1.1...v2.2.0 [2.1.1]: https://github.com/jhedstrom/DrupalDriver/compare/v2.1.0...v2.1.1 [2.1.0]: https://github.com/jhedstrom/DrupalDriver/compare/v2.0.0...v2.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..16da228 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing + +Features and bug fixes are welcome! First-time contributors can jump in with the issues tagged [good first issue](https://github.com/jhedstrom/DrupalDriver/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). + +## Testing + +Testing is performed automatically in Github Actions when a PR is submitted. To execute tests locally before submitting a PR, you'll need [Docker and Docker Compose](https://docs.docker.com/engine/install/). + +Configure your test environment: +``` +export PHP_VERSION=8.1 +export DRUPAL_VERSION=10 +export DOCKER_USER_ID=${UID} +``` + +Prepare environment for testing: +``` +docker-compose up -d +docker-compose exec -T php composer self-update +docker-compose exec -u ${DOCKER_USER_ID} -T php composer require --no-interaction --dev --no-update drupal/core:^${DRUPAL_VERSION} +docker-compose exec -T php composer install +``` + +Execute all tests: +``` +docker-compose exec -T php composer test +``` + +Execute specific tests, eg just PHPUnit's Drupal7FieldHandlerTest: +``` +docker-compose exec -T php phpunit --filter Drupal7FieldHandlerTest +``` + +- Check the changes from `composer require` are not included in your submitted PR. +- Before testing another PHP or Drupal version, remove `composer.lock` and `vendor/` diff --git a/README.md b/README.md index 00deb10..e9f32f0 100644 --- a/README.md +++ b/README.md @@ -65,5 +65,13 @@ $node = (object) array( $driver->createNode($node); ``` +### Contributing + +Features and bug fixes are welcome! First-time contributors can jump in with the +issues tagged [good first issue](https://github.com/jhedstrom/DrupalDriver/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). + +See [CONTRIBUTING.md](https://github.com/jhedstrom/DrupalDriver/blob/master/CONTRIBUTING.md) for more information. + ### Release notes + See [CHANGELOG](CHANGELOG.MD). diff --git a/composer.json b/composer.json index beb4a8d..08458a1 100644 --- a/composer.json +++ b/composer.json @@ -37,16 +37,24 @@ "drupal/core": ">=8.0 <9.3" }, "scripts": { - "test": [ - "composer validate --no-interaction", - "parallel-lint src spec tests", - "phpunit", - "phpspec run -f pretty --no-interaction", - "phpcs --standard=./phpcs-ruleset.xml .", - "./vendor/bin/drupal-check --drupal-root=drupal ./src/Drupal/Driver/Cores/Drupal8.php ./src/Drupal/Driver/Fields/Drupal8", + "lint": "XDEBUG_MODE=off parallel-lint src spec tests", + "phpunit": "XDEBUG_MODE=coverage phpunit --coverage-text", + "phpspec": "XDEBUG_MODE=off phpspec run -f pretty --no-interaction", + "phpcs": "XDEBUG_MODE=off phpcs --standard=./phpcs-ruleset.xml .", + "drupal-check": "XDEBUG_MODE=off drupal-check --drupal-root=drupal ./src/Drupal/Driver/Cores/Drupal8.php ./src/Drupal/Driver/Fields/Drupal8", + "rector": [ "cp ./vendor/palantirnet/drupal-rector/rector.php drupal/.", - "cd drupal && ../vendor/bin/rector process ../src/Drupal/Driver/Cores/Drupal8.php --dry-run", - "cd drupal && ../vendor/bin/rector process ../src/Drupal/Driver/Fields/Drupal8 --dry-run" + "XDEBUG_MODE=off cd drupal && ../vendor/bin/rector process ../src/Drupal/Driver/Cores/Drupal8.php --dry-run", + "XDEBUG_MODE=off cd drupal && ../vendor/bin/rector process ../src/Drupal/Driver/Fields/Drupal8 --dry-run" + ], + "test": [ + "XDEBUG_MODE=off composer validate --no-interaction", + "@lint", + "@phpunit", + "@phpspec", + "@phpcs", + "@drupal-check", + "@rector" ] }, "autoload": { diff --git a/docker-compose.yml b/docker-compose.yml index 38dd332..ca8114b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,8 @@ services: PHP_FPM_GROUP: wodby PHP_FPM_CLEAR_ENV: "yes" PHP_OPCACHE_PRELOAD_USER: wodby - PHP_XDEBUG_MODE: "off" + PHP_XDEBUG_MODE: "on" + PHP_XDEBUG_DEFAULT_ENABLE: 1 PHP_XDEBUG_REMOTE_CONNECT_BACK: 1 PHP_XDEBUG_REMOTE_HOST: "10.254.254.254" PHP_XDEBUG_IDEKEY: "PHPSTORM" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index dcd41f4..b846318 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,15 +1,13 @@ - - - - - ./tests/Drupal/ - - - - - - ./src/Drupal/ - - + + + + ./src/Drupal/ + + + + + ./tests/Drupal/ + + diff --git a/src/Drupal/Driver/BaseDriver.php b/src/Drupal/Driver/BaseDriver.php index f5e1622..41ba9a6 100644 --- a/src/Drupal/Driver/BaseDriver.php +++ b/src/Drupal/Driver/BaseDriver.php @@ -133,6 +133,13 @@ public function isField($entity_type, $field_name) { return FALSE; } + /** + * {@inheritdoc} + */ + public function isBaseField($entity_type, $field_name) { + return FALSE; + } + /** * {@inheritdoc} */ diff --git a/src/Drupal/Driver/Cores/CoreInterface.php b/src/Drupal/Driver/Cores/CoreInterface.php index 89c2a08..03f728d 100644 --- a/src/Drupal/Driver/Cores/CoreInterface.php +++ b/src/Drupal/Driver/Cores/CoreInterface.php @@ -159,6 +159,19 @@ public function getFieldHandler($entity, $entity_type, $field_name); */ public function isField($entity_type, $field_name); + /** + * Checks if the specified field is a Drupal base field. + * + * @param string $entity_type + * The entity type to check. + * @param string $field_name + * The field name to check. + * + * @return bool + * TRUE if the given field is a base field, FALSE otherwise. + */ + public function isBaseField($entity_type, $field_name); + /** * Returns array of field types for the specified entity. * diff --git a/src/Drupal/Driver/Cores/Drupal6.php b/src/Drupal/Driver/Cores/Drupal6.php index 85bbe23..f857395 100644 --- a/src/Drupal/Driver/Cores/Drupal6.php +++ b/src/Drupal/Driver/Cores/Drupal6.php @@ -471,6 +471,14 @@ public function isField($entity_type, $field_name) { return isset($map[$field_name]); } + /** + * {@inheritdoc} + */ + public function isBaseField($entity_type, $field_name) { + // Base fields are only supported in Drupal 8 and higher. + return FALSE; + } + /** * {@inheritdoc} */ diff --git a/src/Drupal/Driver/Cores/Drupal7.php b/src/Drupal/Driver/Cores/Drupal7.php index 4736104..d3e9ff5 100644 --- a/src/Drupal/Driver/Cores/Drupal7.php +++ b/src/Drupal/Driver/Cores/Drupal7.php @@ -477,6 +477,14 @@ public function isField($entity_type, $field_name) { return !empty($map[$field_name]) && array_key_exists($entity_type, $map[$field_name]['bundles']); } + /** + * {@inheritdoc} + */ + public function isBaseField($entity_type, $field_name) { + // Base fields are only supported in Drupal 8 and higher. + return FALSE; + } + /** * {@inheritdoc} */ diff --git a/src/Drupal/Driver/Cores/Drupal8.php b/src/Drupal/Driver/Cores/Drupal8.php index a681767..ddbdf18 100644 --- a/src/Drupal/Driver/Cores/Drupal8.php +++ b/src/Drupal/Driver/Cores/Drupal8.php @@ -333,16 +333,14 @@ public function validateDrupalSite() { public function termCreate(\stdClass $term) { $term->vid = $term->vocabulary_machine_name; - if (isset($term->parent)) { + if (!empty($term->parent)) { $query = \Drupal::entityQuery('taxonomy_term') ->accessCheck(FALSE) - ->condition('id', $term->parent) + ->condition('name', $term->parent) ->condition('vid', $term->vocabulary_machine_name); - $parent = $query->execute(); - if (!empty($parent)) { - /** @var \Drupal\taxonomy\Entity\Term $parent */ - $parent = reset($parent); - $term->parent = $parent->id(); + $parent_terms = $query->execute(); + if (!empty($parent_terms)) { + $term->parent = reset($parent_terms); } } diff --git a/src/Drupal/Driver/DriverInterface.php b/src/Drupal/Driver/DriverInterface.php index ed39f32..5519533 100644 --- a/src/Drupal/Driver/DriverInterface.php +++ b/src/Drupal/Driver/DriverInterface.php @@ -153,6 +153,19 @@ public function roleDelete($rid); */ public function isField($entity_type, $field_name); + /** + * Checks if the specified field is a Drupal base field. + * + * @param string $entity_type + * The entity type to check. + * @param string $field_name + * The field name to check. + * + * @return bool + * TRUE if the given field is a base field, FALSE otherwise. + */ + public function isBaseField($entity_type, $field_name); + /** * Returns a configuration item. * diff --git a/src/Drupal/Driver/DrupalDriver.php b/src/Drupal/Driver/DrupalDriver.php index 39bff9c..5048971 100644 --- a/src/Drupal/Driver/DrupalDriver.php +++ b/src/Drupal/Driver/DrupalDriver.php @@ -285,6 +285,13 @@ public function isField($entity_type, $field_name) { return $this->getCore()->isField($entity_type, $field_name); } + /** + * {@inheritdoc} + */ + public function isBaseField($entity_type, $field_name) { + return $this->getCore()->isBaseField($entity_type, $field_name); + } + /** * {@inheritdoc} */ diff --git a/src/Drupal/Driver/DrushDriver.php b/src/Drupal/Driver/DrushDriver.php index bc50179..8bcd116 100644 --- a/src/Drupal/Driver/DrushDriver.php +++ b/src/Drupal/Driver/DrushDriver.php @@ -414,7 +414,8 @@ public function drush($command, array $arguments = [], array $options = []) { // Add any global arguments. $global = $this->getArguments(); - $process = Process::fromShellCommandline("{$this->binary} {$alias} {$string_options} {$global} {$command} {$arguments}"); + $cmd = "{$this->binary} {$alias} {$string_options} {$global} {$command} {$arguments}"; + $process = method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline($cmd) : new Process($cmd); $process->setTimeout(3600); $process->run(); diff --git a/src/Drupal/Driver/Fields/Drupal8/EntityReferenceHandler.php b/src/Drupal/Driver/Fields/Drupal8/EntityReferenceHandler.php index f86e468..b8a6291 100644 --- a/src/Drupal/Driver/Fields/Drupal8/EntityReferenceHandler.php +++ b/src/Drupal/Driver/Fields/Drupal8/EntityReferenceHandler.php @@ -41,6 +41,8 @@ class EntityReferenceHandler extends AbstractHandler { public function expand($values) { $this->targetEntityTypeId = $this->fieldInfo->getSetting('target_type'); + $id_key = $entity_definition->getKey('id'); + // Determine label field key. if ($this->targetEntityTypeId !== 'user') { $this->targetLabelKey = $this->getEntityTypeKey($this->targetEntityTypeId, 'label');