diff --git a/config/schema/rethinkdb.schema.yml b/config/schema/rethinkdb.schema.yml index ceea6d0..2802ad3 100644 --- a/config/schema/rethinkdb.schema.yml +++ b/config/schema/rethinkdb.schema.yml @@ -1,3 +1,31 @@ +field.storage_settings.rethinkdb: + type: mapping + label: 'RethinkDB reference storage settings' + mapping: + target_type: + type: string + label: 'Type of entity to reference' + handler: + type: string + label: 'Type of entity to reference' + handler_settings: + type: string + label: 'Type of entity to reference' + +field.field_settings.rethinkdb: + type: mapping + label: 'RethinkDB refernec field settings' + mapping: + handler: + type: string + label: 'Type of entity to reference' + handler_settings: + type: mapping + mapping: + search_key: + type: string + label: 'foo' + rethinkdb.database: type: config_object label: 'RethinkDB Config config' diff --git a/modules/rethinkdb_example/src/Entity/RethinkMessages.php b/modules/rethinkdb_example/src/Entity/RethinkMessages.php index 0c0d648..cbdce8e 100644 --- a/modules/rethinkdb_example/src/Entity/RethinkMessages.php +++ b/modules/rethinkdb_example/src/Entity/RethinkMessages.php @@ -14,10 +14,13 @@ * label = @Translation("RethinkDB messages"), * base_table = "rethinkdb_messages", * translatable = FALSE, + * reference = "rethinkdb", + * entity_keys = { + * "id" = "id" + * }, * handlers = { * "storage" = "Drupal\rethinkdb\RethinkStorage" - * }, - * entity_keys = {} + * } * ) */ class RethinkMessages extends AbstractRethinkDbEntity { diff --git a/rethinkdb.module b/rethinkdb.module new file mode 100644 index 0000000..50e0aca --- /dev/null +++ b/rethinkdb.module @@ -0,0 +1,19 @@ +getKey('id')] = BaseFieldDefinition::create('string') + ->setLabel(new TranslatableMarkup('ID')) + ->setReadOnly(TRUE) + ->setSetting('unsigned', TRUE); + + return $fields; } } diff --git a/src/Entity/Query.php b/src/Entity/Query.php index d26fa6c..0e2e6a4 100644 --- a/src/Entity/Query.php +++ b/src/Entity/Query.php @@ -28,6 +28,7 @@ class Query extends QueryBase implements QueryInterface { '<' => 'lt', '<=' => 'le', 'CONTAINS' => 'match', + 'IN' => 'args', ]; /** @@ -65,7 +66,13 @@ protected function addConditions() { throw new RqlException("The operator {$operator} does not allowed. Only " . implode(', ', array_keys($this->operators))); } - $row = \r\row($condition['field'])->{$this->operators[$operator]}($condition['value']); + if ($operator == 'IN') { + $row = \r\args($condition['value']); + } + else { + $row = \r\row($condition['field'])->{$this->operators[$operator]}($condition['value']); + } + $this->table = $this->table->filter($row); } diff --git a/src/Plugin/EntityReferenceSelection/RethinkDBSelection.php b/src/Plugin/EntityReferenceSelection/RethinkDBSelection.php new file mode 100644 index 0000000..83c20f9 --- /dev/null +++ b/src/Plugin/EntityReferenceSelection/RethinkDBSelection.php @@ -0,0 +1,148 @@ +configuration['handler_settings']['search_key']) ? '' : $this->configuration['handler_settings']['search_key']; + $form['search_key'] = [ + '#type' => 'textfield', + '#title' => $this->t('Search field'), + '#description' => $this->t('The key on which the query will match the text to input of the user.'), + '#default_value' => $key, + '#required' => TRUE, + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { + $query = $this->buildEntityQuery($match, $match_operator); + if ($limit > 0) { + $query->range(0, $limit); + } + + $entity_ids = $query->execute(); + + if (empty($entity_ids)) { + return array(); + } + + $entities = \Drupal::entityTypeManager() + ->getStorage($this->configuration['target_type']) + ->loadMultiple($entity_ids); + + $handler_settings = $this->configuration['handler_settings']; + $options = array(); + + foreach ($entities as $entity) { + $value = $entity->getValues(); + $options[$this->configuration['target_type']][$value['id']] = Html::escape($value[$handler_settings['search_key']]); + } + + return $options; + } + + /** + * {@inheritdoc} + */ + public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') { + return $this->buildEntityQuery($match, $match_operator) + ->count() + ->execute(); + } + + /** + * {@inheritdoc} + */ + public function validateReferenceableEntities(array $ids) { + $result = array(); + + if ($ids) { + return $this->buildEntityQuery() + ->condition('id', reset($ids)) + ->execute(); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function validateReferenceableNewEntities(array $entities) { + return array_filter($entities, function ($entity) { + if (isset($this->configuration['handler_settings']['target_bundles'])) { + return in_array($entity->bundle(), $this->configuration['handler_settings']['target_bundles']); + } + return TRUE; + }); + } + + /** + * Builds an EntityQuery to get referenceable entities. + * + * @param string|null $match + * (Optional) Text to match the label against. Defaults to NULL. + * @param string $match_operator + * (Optional) The operation the matching should be done with. Defaults + * to "CONTAINS". + * + * @return \Drupal\Core\Entity\Query\QueryInterface + * The EntityQuery object with the basic conditions and sorting applied to + * it. + */ + protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') { + $handler_settings = $this->configuration['handler_settings']; + + $query = $this->entityManager->getStorage($this->configuration['target_type'])->getQuery(); + + if (isset($match)) { + if ($match_operator == '=' && preg_match("/.+\s\((\S+)\)/", $match, $matches)) { + $query->condition('id', $matches[1], $match_operator); + } + else { + $query->condition($handler_settings['search_key'], $match, $match_operator); + } + } + + // Add the sort option. + if (!empty($handler_settings['sort'])) { + $sort_settings = $handler_settings['sort']; + if ($sort_settings['field'] != '_none') { + $query->sort($sort_settings['field'], $sort_settings['direction']); + } + } + + return $query; + } + + /** + * {@inheritdoc} + */ + public function entityQueryAlter(SelectInterface $query) { } + +} diff --git a/src/Plugin/Field/FieldFormatter/RethinkDbEntityReferenceFieldFormatter.php b/src/Plugin/Field/FieldFormatter/RethinkDbEntityReferenceFieldFormatter.php new file mode 100644 index 0000000..775f3d2 --- /dev/null +++ b/src/Plugin/Field/FieldFormatter/RethinkDbEntityReferenceFieldFormatter.php @@ -0,0 +1,85 @@ + '', + ) + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $elements['key'] = array( + '#type' => 'textfield', + '#title' => t('Property name'), + '#description' => $this->t('Which key of the document would you like to display.'), + '#default_value' => $this->getSetting('key'), + '#required' => TRUE, + ); + + return $elements; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = array(); + + if ($key = $this->getSetting('key')) { + $params = [ + '%key' => $key, + ]; + $text = $this->t('Display %key from the JSON document', $params); + } + else { + $text = t('No property to display'); + } + + $summary[] = $text; + + return $summary; + } + + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items, $langcode) { + $elements = []; + + foreach ($items as $item) { + $entity = $item->getValue(); + $values = $entity->getValues(); + $elements[] = [ + '#plain_text' => $values[$this->getSetting('key')], + ]; + } + + return $elements; + } + +} diff --git a/src/Plugin/Field/FieldType/RethinkDBFieldItemList.php b/src/Plugin/Field/FieldType/RethinkDBFieldItemList.php new file mode 100644 index 0000000..298a64e --- /dev/null +++ b/src/Plugin/Field/FieldType/RethinkDBFieldItemList.php @@ -0,0 +1,24 @@ + array( + 'target_id' => array( + 'description' => 'The ID of the file entity.', + 'type' => 'varchar_ascii', + 'length' => 255, + ), + ), + ); + } + + /** + * {@inheritdoc} + */ + public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) { + $element = []; + + $entity_types = \Drupal::entityTypeManager()->getDefinitions(); + $select = ['---' => $this->t('Select entity type')]; + foreach ($entity_types as $entity_type) { + if ($entity_type->get('reference') == 'rethinkdb') { + $select[$entity_type->id()] = $entity_type->getLabel(); + } + } + + $element['target_type'] = [ + '#type' => 'select', + '#title' => t('Select RethinkDB based entity'), + '#options' => $select, + '#default_value' => $this->getSetting('target_type') + ]; + + return $element; + } + + /** + * {@inheritdoc} + */ + public static function getPreconfiguredOptions() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getValue() { + return $this->entity->getValu; + } + +} diff --git a/src/RethinkStorage.php b/src/RethinkStorage.php index bcc8734..56b77c7 100644 --- a/src/RethinkStorage.php +++ b/src/RethinkStorage.php @@ -15,7 +15,6 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\Sql\SqlContentEntityStorage; use Drupal\Core\Language\LanguageManagerInterface; -use Drupal\rethinkdb\Entity\AbstractRethinkDbEntity; use r\Queries\Tables\Table; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -61,6 +60,12 @@ public static function createInstance(ContainerInterface $container, EntityTypeI ); } + /** + * {@inheritdoc} + */ + public function initFieldValues(ContentEntityInterface $entity, array $values = [], array $field_names = []) { + $entity->values = $values; + } /** * Get the base table easilly. @@ -205,8 +210,8 @@ public function deleteRevision($revision_id) { /** * {@inheritdoc} */ - protected function initFieldValues(ContentEntityInterface $entity, array $values = [], array $field_names = []) { - $entity->values = $values; + protected function invokeFieldMethod($method, ContentEntityInterface $entity) { + return []; } } diff --git a/tests/src/Kernel/Entity/EntityCRUDTest.php b/tests/src/Kernel/Entity/EntityCRUDTest.php index d98ad13..3db8f1e 100644 --- a/tests/src/Kernel/Entity/EntityCRUDTest.php +++ b/tests/src/Kernel/Entity/EntityCRUDTest.php @@ -16,6 +16,9 @@ */ class EntityCRUDTest extends RethinkTestsBase { + /** + * {@inheritdoc} + */ protected $tables = ['rethinkdb_messages']; /** @@ -23,7 +26,10 @@ class EntityCRUDTest extends RethinkTestsBase { */ public static $modules = ['rethinkdb', 'rethinkdb_example']; - function testTesting() { + /** + * Testing the basic CRUD operations. + */ + function testCrud() { $message = RethinkMessages::create(['title' => $this->randomString(), 'body' => $this->randomString()]); // Save it to the DB. $results = $message->save(); diff --git a/tests/src/Kernel/Entity/RethinkEntityReferenceTest.php b/tests/src/Kernel/Entity/RethinkEntityReferenceTest.php new file mode 100644 index 0000000..0bc36c9 --- /dev/null +++ b/tests/src/Kernel/Entity/RethinkEntityReferenceTest.php @@ -0,0 +1,126 @@ +installConfig(['rethinkdb']); + + /** @var RethinkDB $rethinkdb */ + $rethinkdb = \Drupal::getContainer()->get('rethinkdb'); + $rethinkdb->createDb(); + + if ($this->tables) { + foreach ($this->tables as $table) { + $rethinkdb->tableCreate($table); + } + } + + // Setting up the node type. + NodeType::create([ + 'type' => 'page', + 'name' => $this->randomString() + ])->save(); + + // Setting up an entity reference field. + $this->createRethinkDBReferenceField( + 'node', + 'page', + 'field_message_reference', + 'Message reference', + 'rethinkdb_message', + 'rethinkdb', + ['search_key' => 'title'] + ); + + $this->selectionHandler = \Drupal::service('plugin.manager.entity_reference_selection')->createInstance('rethinkdb', [ + 'handler' => 'rethink', + 'target_type' => 'rethinkdb_message', + 'handler_settings' => [ + 'search_key' => 'title', + ] + ]); + + // Create two two entities. + $this->titles = [ + 'title 1', + 'title 2', + 'title 3', + ]; + + foreach ($this->titles as $title) { + \Drupal::entityTypeManager() + ->getStorage('rethinkdb_message') + ->create(['title' => $title]) + ->save(); + } + } + + /** + * Verify we get form the selection handler the expected entities. + */ + function testEntityReferenceField() { + // Verify we get nothing with a title that don't exist. + $results = $this->selectionHandler->getReferenceableEntities('foobar'); + $this->assertEquals($results, []); + + foreach ($this->titles as $delta => $title) { + $results = $this->selectionHandler->getReferenceableEntities($title); + $this->assertEquals(reset($results['rethinkdb_message']), $title); + } + } + +} diff --git a/tests/src/Kernel/Entity/RethinkTestsBase.php b/tests/src/Kernel/Entity/RethinkTestsBase.php index a627dac..81a7e4b 100644 --- a/tests/src/Kernel/Entity/RethinkTestsBase.php +++ b/tests/src/Kernel/Entity/RethinkTestsBase.php @@ -17,7 +17,12 @@ */ abstract class RethinkTestsBase extends KernelTestBase { - protected $table = []; + /** + * List of tables to install. + * + * @var array + */ + protected $tables = []; /** * {@inheritdoc} diff --git a/tests/src/RethinkDBEntityReferenceSelectionTrait.php b/tests/src/RethinkDBEntityReferenceSelectionTrait.php new file mode 100644 index 0000000..162dc74 --- /dev/null +++ b/tests/src/RethinkDBEntityReferenceSelectionTrait.php @@ -0,0 +1,65 @@ + $field_name, + 'type' => 'rethinkdb', + 'entity_type' => $entity_type, + 'cardinality' => $cardinality, + 'settings' => array( + 'target_type' => $target_entity_type, + 'form_display' => [ + 'type' => 'options_buttons', + ], + ), + ))->save(); + } + if (!FieldConfig::loadByName($entity_type, $bundle, $field_name)) { + FieldConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'label' => $field_label, + 'settings' => array( + 'handler' => $selection_handler, + 'handler_settings' => $selection_handler_settings, + ), + ))->save(); + } + } + +}