/** * {@inheritdoc} */ public function getFieldTableName($field_name) { $result = NULL; if (isset($this->fieldStorageDefinitions[$field_name])) { // Since a field may be stored in more than one table, we inspect tables // in order of relevance: the data table if present is the main place // where field data is stored, otherwise the base table is responsible for // storing field data. Revision metadata is an exception as it's stored // only in the revision table. // @todo The table mapping itself should know about entity tables. See // https://www.drupal.org/node/2274017. /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */ $storage = \Drupal::entityManager()->getStorage($this->entityType->id()); $table_names = array($storage->getDataTable(), $storage->getBaseTable(), $storage->getRevisionTable()); // Collect field columns. $field_columns = array(); $storage_definition = $this->fieldStorageDefinitions[$field_name]; foreach (array_keys($storage_definition->getColumns()) as $property_name) { $field_columns[] = $this->getFieldColumnName($storage_definition, $property_name); } foreach (array_filter($table_names) as $table_name) { $columns = $this->getAllColumns($table_name); // We assume finding one field column belonging to the mapping is enough // to identify the field table. if (array_intersect($columns, $field_columns)) { $result = $table_name; break; } } } if (!isset($result)) { throw new SqlContentEntityStorageException(SafeMarkup::format('Table information not available for the "@field_name" field.', array('@field_name' => $field_name))); } return $result; }
/** * @param ContentEntityTypeInterface $entity * @return array */ public function getViewsData(ContentEntityTypeInterface $entity = NULL) { $data = parent::getViewsData(); if ($entity) { // Get some info to work off. /** @var string $entity_table */ $entity_table = $entity->get('base_table'); /** @var string $entity_data_table */ $entity_data_table = $entity->get('data_table'); /** @var string $entity_revision_table */ $entity_revision_table = $entity->get('revision_table'); // Replacements for all strings. $replacements = ['@entity_label' => $entity->getLabel(), '!entity_id' => $entity->id()]; if (!empty($entity_data_table)) { // Add the entity ID field. $data[$entity_data_table]['id']['field']['id'] = 'field'; // Add entity info. $data[$entity_data_table]['info']['field']['id'] = 'field'; $data[$entity_data_table]['info']['field']['link_to_entity default'] = TRUE; // Add the bundle (type). $data[$entity_data_table]['type']['field']['id'] = 'field'; } if (!empty($entity_table) && !empty($entity_revision_table)) { // Advertise this table as a possible base table. $data[$entity_revision_table]['table']['base']['help'] = $this->t('@entity_label revision is a history of changes to a "!entity_id" entity.', $replacements); $data[$entity_revision_table]['table']['base']['defaults']['title'] = 'info'; // @todo EntityViewsData should add these relationships by default. // https://www.drupal.org/node/2410275 $data[$entity_revision_table]['id']['relationship']['id'] = 'standard'; $data[$entity_revision_table]['id']['relationship']['base'] = $entity_table; $data[$entity_revision_table]['id']['relationship']['base field'] = 'id'; $data[$entity_revision_table]['id']['relationship']['title'] = $this->t('@entity_label', $replacements); $data[$entity_revision_table]['id']['relationship']['label'] = $this->t('Get the actual @entity_label from a @entity_label revision.', $replacements); $data[$entity_revision_table]['revision_id']['relationship']['id'] = 'standard'; $data[$entity_revision_table]['revision_id']['relationship']['base'] = $entity_table; $data[$entity_revision_table]['revision_id']['relationship']['base field'] = 'revision_id'; $data[$entity_revision_table]['revision_id']['relationship']['title'] = $this->t('@entity_label', $replacements); $data[$entity_revision_table]['revision_id']['relationship']['label'] = $this->t('Get the actual @entity_label from a @entity_label revision.', $replacements); } } return $data; }
/** * Gets an array of entity type permissions. * * @param ContentEntityTypeInterface $entity_type * The custom entity definition. * * @return array * The entity type permissions. * * @see \Drupal\user\PermissionHandlerInterface::getPermissions() */ public function entityPermissions(ContentEntityTypeInterface $entity_type = NULL) { $perms = []; if (!empty($entity_type)) { // Get the entity ID. $entity_type_id = $entity_type->id(); // Build replacement data for lables and descriptions. $replacements = ['@entity_type_id' => $entity_type_id, '@entity_label' => $entity_type->getLabel()]; // Add the default entity permissions. $perms = ["bypass {$entity_type_id} access" => ['title' => $this->t('Bypass @entity_label access control', $replacements), 'description' => $this->t('View, edit and delete all @entity_label regardless of permission restrictions.', $replacements), 'restrict access' => TRUE], "administer {$entity_type_id} types" => ['title' => $this->t('Administer @entity_label types', $replacements), 'description' => $this->t('Promote, change ownership, edit revisions, and perform other tasks across all @entity_label types.', $replacements), 'restrict access' => TRUE], "administer {$entity_type_id}" => ['title' => $this->t('Administer @entity_label', $replacements), 'restrict access' => TRUE], "access {$entity_type_id} overview" => ['title' => $this->t('Access the @entity_label overview page', $replacements), 'description' => $this->t('Get an overview of all @entity_label.', $replacements)], "access {$entity_type_id}" => ['title' => $this->t('View published @entity_label', $replacements)], "view own unpublished {$entity_type_id}" => ['title' => $this->t('View own unpublished @entity_label', $replacements)], "view all {$entity_type_id} revisions" => ['title' => $this->t('View all @entity_label revisions', $replacements)], "revert all {$entity_type_id} revisions" => ['title' => $this->t('Revert all @entity_label revisions', $replacements), 'description' => $this->t('Role requires permission <em>View all @entity_label revisions</em> and <em>edit rights</em> for @entity_label in question or <em>Administer @entity_label</em>.', $replacements)], "delete all {$entity_type_id} revisions" => ['title' => $this->t('Delete all @entity_label revisions', $replacements), 'description' => $this->t('Role requires permission to <em>View all @entity_label revisions</em> and <em>delete rights</em> for @entity_label in question or <em>Administer @entity_label</em>.', $replacements)]]; // Load bundles if any are defined. if (($entity_type_storage = $this->entityManager->getStorage($entity_type->getBundleEntityType())) && ($entity_types = $entity_type_storage->loadMultiple())) { // Generate entity permissions for all types for this entity. foreach ($entity_types as $type) { $perms += $this->buildPermissions($type); } } } return $perms; }
/** * @covers ::create() */ public function testCreate() { $language_manager = $this->getMock('Drupal\\Core\\Language\\LanguageManagerInterface'); $this->container->set('language_manager', $language_manager); $this->container->set('entity.manager', $this->entityManager); $this->container->set('module_handler', $this->moduleHandler); $entity = $this->getMockBuilder('Drupal\\Core\\Entity\\ContentEntityBase')->disableOriginalConstructor()->setMethods(array('id'))->getMockForAbstractClass(); $this->entityType->expects($this->atLeastOnce())->method('id')->will($this->returnValue($this->entityTypeId)); $this->entityType->expects($this->atLeastOnce())->method('getClass')->will($this->returnValue(get_class($entity))); $this->entityType->expects($this->atLeastOnce())->method('getKeys')->will($this->returnValue(array('id' => 'id'))); // ContentEntityStorageBase iterates over the entity which calls this method // internally in ContentEntityBase::getProperties(). $this->entityManager->expects($this->once())->method('getFieldDefinitions')->will($this->returnValue(array())); $this->entityType->expects($this->atLeastOnce())->method('isRevisionable')->will($this->returnValue(FALSE)); $this->entityManager->expects($this->atLeastOnce())->method('getDefinition')->with($this->entityType->id())->will($this->returnValue($this->entityType)); $this->setUpEntityStorage(); $entity = $this->entityStorage->create(); $entity->expects($this->atLeastOnce())->method('id')->will($this->returnValue('foo')); $this->assertInstanceOf('Drupal\\Core\\Entity\\EntityInterface', $entity); $this->assertSame('foo', $entity->id()); $this->assertTrue($entity->isNew()); }
/** * Sets up the storage schema object to test. * * This uses the field definitions set in $this->storageDefinitions. * * @param array $expected * (optional) An associative array describing the expected entity schema to * be created. Defaults to expecting nothing. */ protected function setUpStorageSchema(array $expected = array()) { $this->entityManager->expects($this->any())->method('getDefinition')->with($this->entityType->id())->will($this->returnValue($this->entityType)); $this->entityManager->expects($this->any())->method('getFieldStorageDefinitions')->with($this->entityType->id())->will($this->returnValue($this->storageDefinitions)); $db_schema_handler = $this->getMockBuilder('Drupal\\Core\\Database\\Schema')->disableOriginalConstructor()->getMock(); if ($expected) { $invocation_count = 0; $expected_table_names = array_keys($expected); $expected_table_schemas = array_values($expected); $db_schema_handler->expects($this->any())->method('createTable')->with($this->callback(function ($table_name) use(&$invocation_count, $expected_table_names) { return $expected_table_names[$invocation_count] == $table_name; }), $this->callback(function ($table_schema) use(&$invocation_count, $expected_table_schemas) { return $expected_table_schemas[$invocation_count] == $table_schema; }))->will($this->returnCallback(function () use(&$invocation_count) { $invocation_count++; })); } $connection = $this->getMockBuilder('Drupal\\Core\\Database\\Connection')->disableOriginalConstructor()->getMock(); $connection->expects($this->any())->method('schema')->will($this->returnValue($db_schema_handler)); $key_value = $this->getMock('Drupal\\Core\\KeyValueStore\\KeyValueStoreInterface'); $this->storageSchema = $this->getMockBuilder('Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorageSchema')->setConstructorArgs(array($this->entityManager, $this->entityType, $this->storage, $connection))->setMethods(array('installedStorageSchema'))->getMock(); $this->storageSchema->expects($this->any())->method('installedStorageSchema')->will($this->returnValue($key_value)); }
/** * Gets the name to be used for the given entity index. * * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type * The entity type. * @param string $index * The index column name. * * @return string * The index name. */ protected function getEntityIndexName(ContentEntityTypeInterface $entity_type, $index) { return $entity_type->id() . '__' . $index; }
/** * Presents the custom entity creation form. * * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_definition * The custom entity definition. * @param \Drupal\content_entity_base\Entity\EntityTypeBaseInterface $entity_bundle * The custom entity type bundle to use. * * @return array * A form array as expected by drupal_render(). */ public function addForm(ContentEntityTypeInterface $entity_definition = NULL, EntityTypeBaseInterface $entity_bundle = NULL) { // Validate the bundle. if (!$entity_bundle || !$entity_definition) { // @todo Replace it with https://www.drupal.org/node/2571521. \Drupal::logger('content_entity_base')->error($this->t('Cannot create a @entity_type entity with an invalid bundle type of "%entity_bundle_id".'), ['@entity_type' => (string) $entity_definition, '%entity_bundle_id' => (string) $entity_bundle]); return new RedirectResponse(Url::fromRoute($entity_definition . '.add_page')->toString()); } // Get the entity storage for this entity type. $entity_storage = $this->entityManager()->getStorage($entity_definition->id()); $entity = $entity_storage->create(['type' => $entity_bundle->id()]); return $this->entityFormBuilder()->getForm($entity); }
/** * Modifies an entity definition to include moderation support. * * This primarily just means an extra handler. A Generic one is provided, * but individual entity types can provide their own as appropriate. * * @param \Drupal\Core\Entity\ContentEntityTypeInterface $type * The content entity definition to modify. * * @return \Drupal\Core\Entity\ContentEntityTypeInterface * The modified content entity definition. */ protected function addModerationToEntity(ContentEntityTypeInterface $type) { if (!$type->hasHandlerClass('moderation')) { $handler_class = !empty($this->moderationHandlers[$type->id()]) ? $this->moderationHandlers[$type->id()] : ModerationHandler::class; $type->setHandlerClass('moderation', $handler_class); } if (!$type->hasLinkTemplate('latest-version')) { $type->setLinkTemplate('latest-version', $type->getLinkTemplate('canonical') . '/latest'); } // @todo Core forgot to add a direct way to manipulate route_provider, so // we have to do it the sloppy way for now. $providers = $type->getRouteProviderClasses() ?: []; if (empty($providers['moderation'])) { $providers['moderation'] = EntityModerationRouteProvider::class; $type->setHandlerClass('route_provider', $providers); } return $type; }
/** * Returns the name to be used for the given entity index. * * @param string $index * The index column name. * * @return string * The index name. */ protected function getEntityIndexName($index) { return $this->entityType->id() . '__' . $index; }
/** * Sets up the schema handler. * * This uses the field definitions set in $this->fieldDefinitions. */ protected function setUpSchemaHandler() { $this->entityManager->expects($this->once())->method('getFieldStorageDefinitions')->with($this->entityType->id())->will($this->returnValue($this->storageDefinitions)); $this->schemaHandler = new ContentEntitySchemaHandler($this->entityManager, $this->entityType, $this->storage); }