clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
<?php
/**
* @file
* Hooks provided by farm_entity.
*
* This file contains no working PHP code; it exists to provide additional
* documentation for doxygen as well as to document hooks in the standard
* Drupal manner.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Allows modules to add field definitions to asset, log, and plan bundles.
*
* @todo https://www.drupal.org/project/farm/issues/3194206
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type object.
* @param string $bundle
* The machine name of the bundle.
*
* @return \Drupal\entity\BundleFieldDefinition[]
* Returns an array of BundleFieldDefinition objects.
*/
function hook_farm_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, string $bundle) {
$fields = [];
// Add a new string field to Input Logs.
if ($entity_type->id() == 'log' && $bundle == 'input') {
$options = [
'type' => 'string',
'label' => t('My new field'),
'description' => t('My field description.'),
'weight' => [
'form' => 10,
'view' => 10,
],
];
$fields['myfield'] = \Drupal::service('farm_field.factory')->bundleFieldDefinition($options);
}
return $fields;
}
/**
* @} End of "addtogroup hooks".
*/

View File

@@ -0,0 +1,12 @@
name: farmOS Entity
description: Adds opinionated entity and field configuration to farmOS entity types.
type: module
package: farmOS
core_version_requirement: ^10
dependencies:
- entity:entity
- entity_reference_integrity:entity_reference_integrity_enforce
- exif_orientation:exif_orientation
- farm:farm_entity_fields
- farm:farm_entity_views
- farm:farm_log

View File

@@ -0,0 +1,25 @@
<?php
/**
* @file
* Install, update and uninstall functions for the farm_entity module.
*/
/**
* Implements hook_install().
*/
function farm_entity_install() {
// Enforce entity reference integrity on the entity types we care about.
$enforced_entity_types = [
'asset',
'data_stream',
'file',
'log',
'plan',
'quantity',
'taxonomy_term',
'user',
];
\Drupal::configFactory()->getEditable('entity_reference_integrity_enforce.settings')->set('enabled_entity_type_ids', array_combine($enforced_entity_types, $enforced_entity_types))->save();
}

View File

@@ -0,0 +1,7 @@
farm_entity:
default_permissions:
- view asset_type
- view data_stream_type
- view log_type
- view plan_type
- view quantity_type

View File

@@ -0,0 +1,192 @@
<?php
/**
* @file
* Contains farm_entity.module.
*/
use Drupal\Core\Entity\ContentEntityFormInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\entity\EntityAccessControlHandler;
use Drupal\entity\EntityPermissionProvider;
use Drupal\farm_entity\BundlePlugin\FarmEntityBundlePluginHandler;
use Drupal\farm_entity\Routing\DefaultHtmlRouteProvider;
/**
* Implements hook_modules_installed().
*/
function farm_entity_modules_installed($modules, $is_syncing) {
// Rebuild bundle field map when modules are installed.
\Drupal::service('entity_field.manager')->rebuildBundleFieldMap();
}
/**
* Implements hook_modules_uninstalled().
*/
function farm_entity_modules_uninstalled($modules, $is_syncing) {
// Rebuild bundle field map when modules are uninstalled.
\Drupal::service('entity_field.manager')->rebuildBundleFieldMap();
}
/**
* Implements hook_module_implements_alter().
*/
function farm_entity_module_implements_alter(&$implementations, $hook) {
// Make sure this module's hook_entity_type_build() runs before the
// entity module's implementation, so that we can override the bundle plugin
// handler, and so that we can set the Log entity type's bundle_plugin_type.
$module = 'farm_entity';
if ($hook == 'entity_type_build') {
$implementation = [$module => $implementations[$module]];
unset($implementations[$module]);
$implementations = array_merge($implementation, $implementations);
}
}
/**
* Implements hook_entity_type_build().
*/
function farm_entity_entity_type_build(array &$entity_types) {
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
// Allow the "view label" operation on the bundle entity type.
foreach (['asset', 'log', 'plan', 'quantity', 'data_stream'] as $entity_type) {
if (!empty($entity_types[$entity_type])) {
$bundle_entity_type = $entity_types[$entity_type]->getBundleEntityType();
$entity_types[$bundle_entity_type]->setHandlerClass('access', EntityAccessControlHandler::class);
$entity_types[$bundle_entity_type]->setHandlerClass('permission_provider', EntityPermissionProvider::class);
}
}
// Enable the use of bundle plugins on specific entity types.
foreach (['asset', 'log', 'plan', 'plan_record', 'quantity'] as $entity_type) {
if (!empty($entity_types[$entity_type])) {
$entity_types[$entity_type]->set('bundle_plugin_type', $entity_type . '_type');
$entity_types[$entity_type]->setHandlerClass('bundle_plugin', FarmEntityBundlePluginHandler::class);
// Deny access to the entity type add form. New entity types of entities
// with bundle plugins cannot be created in the UI.
// See https://www.drupal.org/project/farm/issues/3196423
$bundle_entity_type = $entity_types[$entity_type]->getBundleEntityType();
$route_providers = $entity_types[$bundle_entity_type]->getRouteProviderClasses();
$route_providers['default'] = DefaultHtmlRouteProvider::class;
$entity_types[$bundle_entity_type]->setHandlerClass('route_provider', $route_providers);
}
}
}
/**
* Implements hook_entity_field_storage_info_alter().
*
* @todo https://www.drupal.org/project/farm/issues/3194206
*/
function farm_entity_entity_field_storage_info_alter(&$fields, EntityTypeInterface $entity_type) {
// Bail if not a farm entity type that allows bundle plugins.
if (!in_array($entity_type->id(), ['log', 'asset', 'plan', 'quantity'])) {
return;
}
// Get all bundles of the entity type.
$bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type->id());
// Invoke hook_farm_entity_bundle_field_info() with each bundle.
$hook = 'farm_entity_bundle_field_info';
foreach (array_keys($bundles) as $bundle) {
\Drupal::moduleHandler()->invokeAllWith($hook, function (callable $hook, string $module) use ($fields, $entity_type, $bundle) {
// Get bundle field definitions provided by the module.
$definitions = $hook($entity_type, $bundle);
// Set the provider for each field the module provided.
// This is required so that field storage definitions are created in the
// database when the module is installed.
foreach (array_keys($definitions) as $field) {
if (isset($fields[$field])) {
$fields[$field]->setProvider($module);
}
}
});
}
}
/**
* Implements hook_entity_presave().
*
* Forces revisions on all farm entities if the entity type supports them and
* the bundle has them enabled. This removes the option for users to disable a
* revision per-entity but as JSON:API doesn't support revisions yet, this is a
* trade-off that allows us to create revisions consistently on both the UI and
* the API.
*/
function farm_entity_entity_presave(EntityInterface $entity) {
// Only apply to farm controlled entities.
$entity_types = [
'asset',
'log',
'plan',
'quantity',
];
if (!in_array($entity->getEntityTypeId(), $entity_types)) {
return;
}
// Force create new revision as json api doesn't do that by default.
// @see https://www.drupal.org/project/drupal/issues/2993557
// @see https://www.drupal.org/project/drupal/issues/2795279
// @see https://github.com/json-api/json-api/pull/824
if ($entity->type->entity->shouldCreateNewRevision() && $entity->getEntityType()->isRevisionable()) {
/** @var \Drupal\Core\Entity\RevisionLogInterface $entity */
// Always create a new revision.
$entity->setNewRevision(TRUE);
// If the new revision log message matches the original, then set a blank
// revision log message. We don't want the same message repeated across
// every revision created by the API.
if (!empty($entity->original)) {
if ($entity->original->get('revision_log_message')->value == $entity->get('revision_log_message')->value) {
$entity->setRevisionLogMessage('');
}
}
// Set the user ID and creation time.
$entity->setRevisionUserId(\Drupal::currentUser()->getAccount()->id());
$entity->setRevisionCreationTime(\Drupal::time()->getRequestTime());
}
}
/**
* Implements hook_form_alter().
*
* Hides the revision control from the user, @see farm_entity_entity_presave()
*/
function farm_entity_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Only alter content entity forms.
$form_object = $form_state->getFormObject();
if (!($form_object instanceof ContentEntityFormInterface)) {
return;
}
// Only apply to farm controlled entities.
$entity = $form_object->getEntity();
$entity_types = [
'asset',
'log',
'plan',
'quantity',
];
if (!in_array($entity->getEntityTypeId(), $entity_types)) {
return;
}
// Disable access to the revision checkbox.
$form['revision']['#access'] = FALSE;
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* @file
* Post update hooks for the farm_entity module.
*/
/**
* Enforce entity reference integrity on plan reference fields.
*/
function farm_entity_post_update_enforce_plan_eri(&$sandbox) {
$config = \Drupal::configFactory()->getEditable('entity_reference_integrity_enforce.settings');
$entity_types = $config->get('enabled_entity_type_ids');
$entity_types['plan'] = 'plan';
$config->set('enabled_entity_type_ids', $entity_types);
$config->save();
}
/**
* Rebuild bundle field maps.
*/
function farm_entity_post_update_rebuild_bundle_field_maps(&$sandbox = NULL) {
\Drupal::service('entity_field.manager')->rebuildBundleFieldMap();
}

View File

@@ -0,0 +1,20 @@
services:
farm_entity.bundle_plugin_installer:
class: Drupal\farm_entity\BundlePlugin\BundlePluginInstaller
decorates: entity.bundle_plugin_installer
arguments: [ '@entity_type.manager', '@entity_bundle.listener', '@field_storage_definition.listener', '@field_definition.listener']
plugin.manager.asset_type:
class: Drupal\farm_entity\AssetTypeManager
parent: default_plugin_manager
plugin.manager.log_type:
class: Drupal\farm_entity\LogTypeManager
parent: default_plugin_manager
plugin.manager.plan_type:
class: Drupal\farm_entity\PlanTypeManager
parent: default_plugin_manager
plugin.manager.plan_record_type:
class: Drupal\farm_entity\PlanRecordTypeManager
parent: default_plugin_manager
plugin.manager.quantity_type:
class: Drupal\farm_entity\QuantityTypeManager
parent: default_plugin_manager

View File

@@ -0,0 +1,190 @@
<?php
/**
* @file
* Code for creating common farmOS entity base field definitions.
*/
/**
* Define common asset base fields.
*/
function farm_entity_fields_asset_base_fields() {
$field_info = [
'data' => [
'type' => 'string_long',
'label' => t('Data'),
'hidden' => TRUE,
],
'file' => [
'type' => 'file',
'label' => t('Files'),
'file_directory' => 'farm/asset/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 90,
'view' => 90,
],
],
'image' => [
'type' => 'image',
'label' => t('Images'),
'file_directory' => 'farm/asset/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 89,
'view' => 89,
],
],
'notes' => [
'type' => 'text_long',
'label' => t('Notes'),
'weight' => [
'form' => 95,
'view' => 95,
],
],
];
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = [];
foreach ($field_info as $name => $info) {
$fields[$name] = \Drupal::service('farm_field.factory')->baseFieldDefinition($info);
}
return $fields;
}
/**
* Define common log base fields.
*/
function farm_entity_fields_log_base_fields() {
$field_info = [
'data' => [
'type' => 'string_long',
'label' => t('Data'),
'hidden' => TRUE,
],
'file' => [
'type' => 'file',
'label' => t('Files'),
'file_directory' => 'farm/log/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 90,
'view' => 90,
],
],
'image' => [
'type' => 'image',
'label' => t('Images'),
'file_directory' => 'farm/log/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 89,
'view' => 89,
],
],
'notes' => [
'type' => 'text_long',
'label' => t('Notes'),
'weight' => [
'form' => 95,
'view' => 95,
],
],
];
$fields = [];
foreach ($field_info as $name => $info) {
$fields[$name] = \Drupal::service('farm_field.factory')->baseFieldDefinition($info);
}
return $fields;
}
/**
* Define common plan base fields.
*/
function farm_entity_fields_plan_base_fields() {
$field_info = [
'data' => [
'type' => 'string_long',
'label' => t('Data'),
'hidden' => TRUE,
],
'file' => [
'type' => 'file',
'label' => t('Files'),
'file_directory' => 'farm/plan/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 90,
'view' => 90,
],
],
'image' => [
'type' => 'image',
'label' => t('Images'),
'file_directory' => 'farm/plan/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 89,
'view' => 89,
],
],
'notes' => [
'type' => 'text_long',
'label' => t('Notes'),
'weight' => [
'form' => 95,
'view' => 95,
],
],
];
$fields = [];
foreach ($field_info as $name => $info) {
$fields[$name] = \Drupal::service('farm_field.factory')->baseFieldDefinition($info);
}
return $fields;
}
/**
* Define common taxonomy term base fields.
*/
function farm_entity_fields_taxonomy_term_base_fields() {
$field_info = [
'file' => [
'type' => 'file',
'label' => t('Files'),
'file_directory' => 'farm/term/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 90,
'view' => 90,
],
],
'image' => [
'type' => 'image',
'label' => t('Images'),
'file_directory' => 'farm/term/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 89,
'view' => 89,
],
],
'external_uri' => [
'type' => 'uri',
'label' => t('External URI'),
'description' => t('Link this term to one or more external URLs or ontology item URIs.'),
'multiple' => TRUE,
'weight' => [
'form' => 80,
'view' => 80,
],
],
];
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = [];
foreach ($field_info as $name => $info) {
$fields[$name] = \Drupal::service('farm_field.factory')->baseFieldDefinition($info);
}
return $fields;
}

View File

@@ -0,0 +1,22 @@
name: farmOS Entity Fields
description: Adds common base fields to farmOS entity types.
type: module
package: farmOS
core_version_requirement: ^10
dependencies:
- drupal:file
- drupal:image
- drupal:taxonomy
- drupal:text
- drupal:user
- farm:asset
- farm:farm_field
- farm:farm_flag
- farm:farm_format
- farm:farm_id_tag
- farm:farm_location
- farm:farm_log_category
- farm:farm_log_quantity
- farm:farm_owner
- farm:farm_parent
- token:token

View File

@@ -0,0 +1,70 @@
<?php
/**
* @file
* Install, update and uninstall functions for the farm_entity_fields module.
*/
use Drupal\Core\Database\DatabaseExceptionWrapper;
/**
* Install taxonomy term file and image fields.
*/
function farm_entity_fields_update_100300(&$sandbox) {
$entity_type = 'taxonomy_term';
$module_name = 'farm_entity_fields';
// Ensure that this has not run already.
// farmOS 3.2.0 was released with this code in a hook_post_update_NAME()
// hook. It was discovered that this does not work when run in the browser
// via update.php because Drupal core calls drupal_flush_all_caches() in
// between hook_update_N() and hook_post_update_NAME() hooks, which does not
// happen when updates are run via drush. This causes an error because the
// image field changes from a config field to a base field. So this code was
// moved to a hook_update_N() hook. This means there's a risk of it running
// twice, if a site was updated to 3.2.0 and then to 3.2.1. So we prevent that
// here by checking for the existence of the file base field database table.
try {
\Drupal::database()->query('SELECT COUNT(*) FROM {taxonomy_term__file}');
return;
}
catch (DatabaseExceptionWrapper $e) {
}
// Install file field.
$field_info = [
'type' => 'file',
'label' => t('Files'),
'file_directory' => 'farm/term/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 90,
'view' => 90,
],
];
$field_definition = \Drupal::service('farm_field.factory')->baseFieldDefinition($field_info);
\Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('file', $entity_type, $module_name, $field_definition);
// Install image field.
$field_info = [
'type' => 'image',
'label' => t('Images'),
'file_directory' => 'farm/term/[date:custom:Y]-[date:custom:m]',
'multiple' => TRUE,
'weight' => [
'form' => 89,
'view' => 89,
],
];
$field_definition = \Drupal::service('farm_field.factory')->baseFieldDefinition($field_info);
\Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('image', $entity_type, $module_name, $field_definition);
// If the farm_plant_type module is installed, remove old image field config.
// This module previously provided an image field for plant_type taxonomy
// terms, so we need to clean up the configuration entities it created.
if (\Drupal::moduleHandler()->moduleExists('farm_plant_type')) {
foreach (['field.field.taxonomy_term.plant_type.image', 'field.storage.taxonomy_term.image'] as $config) {
\Drupal::configFactory()->getEditable($config)->delete();
}
}
}

View File

@@ -0,0 +1,126 @@
<?php
/**
* @file
* Contains farm_entity_fields.module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_entity_base_field_info().
*/
function farm_entity_fields_entity_base_field_info(EntityTypeInterface $entity_type) {
// Include helper functions.
\Drupal::moduleHandler()->loadInclude('farm_entity_fields', 'inc', 'farm_entity_fields.base_fields');
// Add common base fields to all asset types.
if ($entity_type->id() == 'asset') {
return farm_entity_fields_asset_base_fields();
}
// Add common base fields to all log types.
elseif ($entity_type->id() == 'log') {
return farm_entity_fields_log_base_fields();
}
// Add common base fields to all plan types.
elseif ($entity_type->id() == 'plan') {
return farm_entity_fields_plan_base_fields();
}
// Add common base fields to all taxonomy terms.
elseif ($entity_type->id() == 'taxonomy_term') {
return farm_entity_fields_taxonomy_term_base_fields();
}
return [];
}
/**
* Implements hook_entity_base_field_info_alter().
*/
function farm_entity_fields_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
// Only alter asset, log, and plan fields.
if (!in_array($entity_type->id(), ['asset', 'log', 'plan'])) {
return;
}
$alter_fields = [
'name' => [
'label' => 'hidden',
'weight' => -100,
],
'status' => [
'weight' => -95,
],
'timestamp' => [
'weight' => -90,
],
'type' => [
'weight' => -85,
'hidden' => 'form',
],
'created' => [
'hidden' => TRUE,
],
'uid' => [
'hidden' => TRUE,
],
];
foreach ($alter_fields as $name => $options) {
// If the field does not exist on this entity type, skip it.
if (empty($fields[$name])) {
continue;
}
// Load the form and view display options.
$form_display_options = $fields[$name]->getDisplayOptions('form');
$view_display_options = $fields[$name]->getDisplayOptions('view');
// Set the field weight.
if (!empty($options['weight'])) {
$form_display_options['weight'] = $view_display_options['weight'] = $options['weight'];
}
// Hide the field, if desired.
if (!empty($options['hidden'])) {
if ($options['hidden'] === TRUE || $options['hidden'] === 'form') {
$form_display_options['region'] = 'hidden';
}
if ($options['hidden'] === TRUE || $options['hidden'] === 'view') {
$view_display_options['region'] = 'hidden';
}
}
// Hide the label, if desired.
if (!empty($options['label']) && $options['label'] == 'hidden') {
$view_display_options['label'] = 'hidden';
}
// Otherwise, set the label to inline.
else {
$view_display_options['label'] = 'inline';
}
switch ($name) {
// Change state field from transition form to default.
case 'status':
$view_display_options['type'] = 'list_default';
break;
// Don't display a link to the entity type reference.
case 'type':
$view_display_options['settings']['link'] = FALSE;
break;
}
// Save the options.
$fields[$name]->setDisplayOptions('form', $form_display_options);
$fields[$name]->setDisplayOptions('view', $view_display_options);
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* @file
* Updates farm_entity_fields module.
*/
/**
* Install farm_parent module.
*/
function farm_entity_fields_post_update_enable_farm_parent(&$sandbox = NULL) {
if (!\Drupal::service('module_handler')->moduleExists('farm_parent')) {
\Drupal::service('module_installer')->install(['farm_parent']);
}
}
/**
* Install taxonomy term external URI field.
*/
function farm_entity_fields_post_update_add_term_external_uri(&$sandbox) {
$field_info = [
'type' => 'uri',
'label' => t('External URI'),
'description' => t('Link this term to one or more external URLs or ontology item URIs.'),
'multiple' => TRUE,
'weight' => [
'form' => 80,
'view' => 80,
],
];
$field_definition = \Drupal::service('farm_field.factory')->baseFieldDefinition($field_info);
\Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('external_uri', 'taxonomy_term', 'farm_entity_fields', $field_definition);
}

View File

@@ -0,0 +1,199 @@
langcode: en
status: true
dependencies:
module:
- asset
enforced:
module:
- farm_entity_views
id: farm_asset_reference
label: 'Farm Asset Reference'
module: views
description: ''
tag: ''
base_table: asset_field_data
base_field: id
display:
default:
id: default
display_title: Master
display_plugin: default
position: 0
display_options:
fields:
name:
id: name
table: asset_field_data
field: name
relationship: none
group_type: group
admin_label: ''
entity_type: null
entity_field: name
plugin_id: field
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
pager:
type: mini
options:
offset: 0
items_per_page: 10
total_pages: null
id: 0
tags:
next:
previous:
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
pagination_heading_level: h4
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
access:
type: none
options: { }
cache:
type: tag
options: { }
empty: { }
sorts:
name:
id: name
table: asset_field_data
field: name
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: name
plugin_id: standard
order: ASC
expose:
label: ''
field_identifier: name
exposed: false
arguments: { }
filters: { }
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
default_field_elements: true
inline: { }
separator: ''
hide_empty: false
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
relationships: { }
header: { }
footer: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
tags: { }
entity_reference:
id: entity_reference
display_title: 'Entity Reference'
display_plugin: entity_reference
position: 1
display_options:
style:
type: entity_reference
options:
search_fields:
name: name
row:
type: entity_reference
options:
default_field_elements: false
inline: { }
separator: '-'
hide_empty: false
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
tags: { }

View File

@@ -0,0 +1,5 @@
name: farmOS Entity Views
description: Views integration support for farmOS entities and fields.
type: module
package: farmOS
core_version_requirement: ^10

View File

@@ -0,0 +1,65 @@
<?php
/**
* @file
* Contains farm_entity_views.module.
*/
use Drupal\farm_entity_views\FarmEntityViewsData;
use Drupal\farm_entity_views\FarmLogViewsData;
use Drupal\farm_entity_views\FarmQuantityViewsData;
/**
* Implements hook_module_implements_alter().
*/
function farm_entity_views_module_implements_alter(&$implementations, $hook) {
// Make sure this module's hook_modules_installed runs after the entity
// module's implementation, so that we rebuild views data after bundle fields
// are installed.
$module = 'farm_entity_views';
if ($hook == 'modules_installed') {
$implementation = [$module => $implementations[$module]];
unset($implementations[$module]);
$implementations = array_merge($implementations, $implementation);
}
}
/**
* Implements hook_modules_installed().
*/
function farm_entity_views_modules_installed($modules, $is_syncing) {
// Reset the views data after installing modules.
// See https://www.drupal.org/project/entity/issues/3206703#comment-14073184
if (\Drupal::hasService('views.views_data')) {
\Drupal::service('views.views_data')->clear();
}
}
/**
* Implements hook_entity_type_build().
*/
function farm_entity_views_entity_type_build(array &$entity_types) {
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
// Set the views data handler class to FarmEntityViewsData.
foreach (['asset', 'log', 'plan', 'plan_record', 'quantity'] as $entity_type) {
if (!empty($entity_types[$entity_type])) {
// Use the correct class for each entity type.
// Logs and quantities provide their own that we must extend from.
$views_data_class = FarmEntityViewsData::class;
switch ($entity_type) {
case 'log':
$views_data_class = FarmLogViewsData::class;
break;
case 'quantity':
$views_data_class = FarmQuantityViewsData::class;
break;
}
$entity_types[$entity_type]->setHandlerClass('views_data', $views_data_class);
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* @file
* Provides Views data for farm_entity_views.module.
*/
/**
* Implements hook_views_data_alter().
*/
function farm_entity_views_views_data_alter(array &$data) {
// Because Drupal core does not provide full Views integration for base fields
// we must manually add support for certain fields.
// Workaround for core issue #2489476.
// Add support for state_machine filters.
$status_filter = [
'id' => 'state_machine_state',
'field_name' => 'status',
];
$tables = [
'asset_field_data',
'asset_field_revision',
'log_field_data',
'log_field_revision',
'plan_field_data',
'plan_field_revision',
];
foreach ($tables as $table) {
if (!empty($data[$table]['status'])) {
$data[$table]['status']['filter'] = $status_filter;
}
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Drupal\farm_entity_views;
/**
* Configures the correct view filter for taxonomy_term reference fields.
*
* @see EntityViewsData
* @see \taxonomy_field_views_data_alter()
*/
trait EntityViewsDataTaxonomyFilterTrait {
/**
* {@inheritdoc}
*/
protected function addReverseRelationships(array &$data, array $fields) {
parent::addReverseRelationships($data, $fields);
// Configure the taxonomy_term reference field filter.
// Logic derived form taxonomy_field_views_data_alter().
foreach ($fields as $field) {
// If this is not a taxonomy term reference field, skip it.
if ($field->getSettings()['target_type'] !== 'taxonomy_term') {
continue;
}
// Get the field name.
$field_name = $field->getName();
// Iterate through the Views data tables and columns.
foreach ($data as $table_name => $table_data) {
foreach ($table_data as $table_field_name => $field_data) {
// If this field doesn't have a filter handler, skip it.
if (!isset($field_data['filter'])) {
continue;
}
// Ensure that we are only altering the Views field we want.
// This will either be the field name itself, or the field name plus
// a `_target_id` suffix (depending on whether the field is a base or
// bundle field, single or multiple values, etc).
$table_field_names = [
$field_name,
$field_name . '_target_id',
];
if (in_array($table_field_name, $table_field_names)) {
// Set the filter handler ID.
$data[$table_name][$table_field_name]['filter']['id'] = 'taxonomy_index_tid';
}
}
}
}
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Drupal\farm_entity_views;
use Drupal\entity\EntityViewsData;
/**
* Configures the correct view filter for taxonomy_term reference fields.
*/
class FarmEntityViewsData extends EntityViewsData {
use EntityViewsDataTaxonomyFilterTrait;
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Drupal\farm_entity_views;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\log\LogViewsData;
/**
* Provides the views data for the log entity type.
*/
class FarmLogViewsData extends LogViewsData {
use EntityViewsDataTaxonomyFilterTrait;
/**
* {@inheritdoc}
*/
public function getViewsData() {
$data = parent::getViewsData();
// Provide a reverse entity reference relationship from quantities to logs
// that reference them.
// Workaround for core issue #2706431.
// Copied from Entity API module's EntityViewsData, modified to support
// Entity Reference Revisions field.
// @todo Patch Entity to support Entity Reference Revisions instead?
$entity_type_id = $this->entityType->id();
$base_fields = $this->getEntityFieldManager()->getBaseFieldDefinitions($entity_type_id);
$entity_reference_fields = array_filter($base_fields, function (BaseFieldDefinition $field) {
return !$field->isComputed() && $field->getType() == 'entity_reference_revisions';
});
$this->addReverseRelationships($data, $entity_reference_fields);
return $data;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Drupal\farm_entity_views;
use Drupal\quantity\QuantityViewsData;
/**
* Provides the views data for the quantity entity type.
*/
class FarmQuantityViewsData extends QuantityViewsData {
use EntityViewsDataTaxonomyFilterTrait;
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Drupal\farm_entity\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines the asset type plugin annotation object.
*
* Plugin namespace: Plugin\Asset\AssetType.
*
* @see plugin_api
*
* @Annotation
*/
class AssetType extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The asset type label.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Drupal\farm_entity\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines the log type plugin annotation object.
*
* Plugin namespace: Plugin\Log\LogType.
*
* @see plugin_api
*
* @Annotation
*/
class LogType extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The log type label.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Drupal\farm_entity\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines the plan record relationship type plugin annotation object.
*
* Plugin namespace: Plugin\PlanRecord\PlanRecordType.
*
* @see plugin_api
*
* @Annotation
*/
class PlanRecordType extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The plan record relationship type label.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Drupal\farm_entity\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines the plan type plugin annotation object.
*
* Plugin namespace: Plugin\Plan\PlanType.
*
* @see plugin_api
*
* @Annotation
*/
class PlanType extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The plan type label.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Drupal\farm_entity\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines the quantity type plugin annotation object.
*
* Plugin namespace: Plugin\Quantity\QuantityType.
*
* @see plugin_api
*
* @Annotation
*/
class QuantityType extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The quantity type label.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Drupal\farm_entity;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Manages discovery and instantiation of asset type plugins.
*
* @see \Drupal\farm_entity\Annotation\AssetType
* @see plugin_api
*/
class AssetTypeManager extends DefaultPluginManager {
/**
* Constructs a new AssetTypeManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* The cache backend.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Asset/AssetType', $namespaces, $module_handler, 'Drupal\farm_entity\Plugin\Asset\AssetType\AssetTypeInterface', 'Drupal\farm_entity\Annotation\AssetType');
$this->alterInfo('asset_type_info');
$this->setCacheBackend($cache_backend, 'asset_type_plugins');
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
foreach (['id', 'label'] as $required_property) {
if (empty($definition[$required_property])) {
throw new PluginException(sprintf('The asset type %s must define the %s property.', $plugin_id, $required_property));
}
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Drupal\farm_entity\BundlePlugin;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\entity\BundlePlugin\BundlePluginInstaller as EntityBundlePluginInstaller;
/**
* Extends the entity BundlePluginInstaller service.
*
* Only removes field storage definitions when not in use by another module.
* This allows field names to be reused across bundles.
*
* @see https://www.drupal.org/project/farm/issues/3200219
*/
class BundlePluginInstaller extends EntityBundlePluginInstaller {
/**
* {@inheritdoc}
*/
public function uninstallBundles(EntityTypeInterface $entity_type, array $modules) {
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
$bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) {
return in_array($bundle_info['provider'], $modules, TRUE);
});
/**
* We need to uninstall the field storage definitions in a separate loop.
*
* This way we can allow a module to re-use the same field within multiple
* bundles, allowing e.g to subclass a bundle plugin.
*
* @var \Drupal\entity\BundleFieldDefinition[] $field_storage_definitions
*/
$field_storage_definitions = [];
// Field definitions that should persist after uninstalling these bundles.
$field_definitions_to_persist = $this->getFieldDefinitionsToPersist($entity_type, array_keys($bundles));
foreach (array_keys($bundles) as $bundle) {
$this->entityBundleListener->onBundleDelete($bundle, $entity_type->id());
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
$field_name = $definition->getName();
$this->fieldDefinitionListener->onFieldDefinitionDelete($definition);
// Delete the field storage definition if it should not persist.
if (!in_array($field_name, array_keys($field_definitions_to_persist))) {
$field_storage_definitions[$field_name] = $definition;
}
}
}
foreach ($field_storage_definitions as $definition) {
$this->fieldStorageDefinitionListener->onFieldStorageDefinitionDelete($definition);
}
}
/**
* Get field definitions from all remaining bundles.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type to check.
* @param array $uninstalled_bundles
* The bundles that will be uninstalled.
*
* @return array
* Remaining field definitions.
*/
protected function getFieldDefinitionsToPersist(EntityTypeInterface $entity_type, array $uninstalled_bundles) {
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
$remaining_bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_name) use ($uninstalled_bundles) {
return !in_array($bundle_name, $uninstalled_bundles, TRUE);
}, ARRAY_FILTER_USE_KEY);
$fields_to_persist = [];
foreach (array_keys($remaining_bundles) as $bundle) {
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
$field_name = $definition->getName();
if (!isset($fields_to_persist[$field_name])) {
$fields_to_persist[$field_name] = $definition;
}
}
}
return $fields_to_persist;
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Drupal\farm_entity\BundlePlugin;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\entity\BundlePlugin\BundlePluginHandler;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Extends BundlePluginHandler to invoke hook_farm_entity_bundle_field_info().
*
* @todo https://www.drupal.org/project/farm/issues/3194206
*/
class FarmEntityBundlePluginHandler extends BundlePluginHandler {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a new FarmEntityBundlePluginHandler object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
* @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
* The bundle plugin manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(EntityTypeInterface $entity_type, PluginManagerInterface $plugin_manager, ModuleHandlerInterface $module_handler) {
parent::__construct($entity_type, $plugin_manager);
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('plugin.manager.' . $entity_type->get('bundle_plugin_type')),
$container->get('module_handler'),
);
}
/**
* {@inheritdoc}
*/
public function getFieldStorageDefinitions() {
$definitions = [];
// Allow modules to add definitions.
foreach (array_keys($this->pluginManager->getDefinitions()) as $plugin_id) {
$definitions += $this->moduleHandler->invokeAll('farm_entity_bundle_field_info', [$this->entityType, $plugin_id]);
}
// Ensure the presence of required keys which aren't set by the plugin.
// This is copied directly from the parent method for consistency.
foreach ($definitions as $field_name => $definition) {
$definition->setName($field_name);
$definition->setTargetEntityTypeId($this->entityType->id());
$definitions[$field_name] = $definition;
}
// Get definitions from the parent method.
$definitions += parent::getFieldStorageDefinitions();
return $definitions;
}
/**
* {@inheritdoc}
*/
public function getFieldDefinitions($bundle) {
// Allow modules to add definitions.
$definitions = $this->moduleHandler->invokeAll('farm_entity_bundle_field_info', [$this->entityType, $bundle]);
// Ensure the presence of required keys which aren't set by the plugin.
// This is copied directly from the parent method for consistency.
foreach ($definitions as $field_name => $definition) {
$definition->setName($field_name);
$definition->setTargetEntityTypeId($this->entityType->id());
$definition->setTargetBundle($bundle);
$definitions[$field_name] = $definition;
}
// Get definitions from the parent method.
$definitions += parent::getFieldDefinitions($bundle);
return $definitions;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Drupal\farm_entity;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\farm_field\FarmFieldFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a FarmEntityTypeBase for plugins to extends.
*/
abstract class FarmEntityTypeBase extends PluginBase implements ContainerFactoryPluginInterface {
/**
* The farm_field.factory service.
*
* @var \Drupal\farm_field\FarmFieldFactoryInterface
*/
protected $farmFieldFactory;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, FarmFieldFactoryInterface $farm_field_factory) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->farmFieldFactory = $farm_field_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('farm_field.factory')
);
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Drupal\farm_entity;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Manages discovery and instantiation of log type plugins.
*
* @see \Drupal\farm_entity\Annotation\LogType
* @see plugin_api
*/
class LogTypeManager extends DefaultPluginManager {
/**
* Constructs a new LogTypeManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* The cache backend.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Log/LogType', $namespaces, $module_handler, 'Drupal\farm_entity\Plugin\Log\LogType\LogTypeInterface', 'Drupal\farm_entity\Annotation\LogType');
$this->alterInfo('log_type_info');
$this->setCacheBackend($cache_backend, 'log_type_plugins');
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
foreach (['id', 'label'] as $required_property) {
if (empty($definition[$required_property])) {
throw new PluginException(sprintf('The log type %s must define the %s property.', $plugin_id, $required_property));
}
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Drupal\farm_entity;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Manages discovery and instantiation of plan record relationship type plugins.
*
* @see \Drupal\farm_entity\Annotation\PlanType
* @see plugin_api
*/
class PlanRecordTypeManager extends DefaultPluginManager {
/**
* Constructs a new PlanRecordTypeManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* The cache backend.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/PlanRecord/PlanRecordType', $namespaces, $module_handler, 'Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType\PlanRecordTypeInterface', 'Drupal\farm_entity\Annotation\PlanRecordType');
$this->alterInfo('plan_record_type_info');
$this->setCacheBackend($cache_backend, 'plan_record_type_plugins');
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
foreach (['id', 'label'] as $required_property) {
if (empty($definition[$required_property])) {
throw new PluginException(sprintf('The plan record relationship type %s must define the %s property.', $plugin_id, $required_property));
}
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Drupal\farm_entity;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Manages discovery and instantiation of plan type plugins.
*
* @see \Drupal\farm_entity\Annotation\PlanType
* @see plugin_api
*/
class PlanTypeManager extends DefaultPluginManager {
/**
* Constructs a new PlanTypeManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* The cache backend.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Plan/PlanType', $namespaces, $module_handler, 'Drupal\farm_entity\Plugin\Plan\PlanType\PlanTypeInterface', 'Drupal\farm_entity\Annotation\PlanType');
$this->alterInfo('plan_type_info');
$this->setCacheBackend($cache_backend, 'plan_type_plugins');
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
foreach (['id', 'label'] as $required_property) {
if (empty($definition[$required_property])) {
throw new PluginException(sprintf('The plan type %s must define the %s property.', $plugin_id, $required_property));
}
}
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Drupal\farm_entity\Plugin\Asset\AssetType;
use Drupal\farm_entity\FarmEntityTypeBase;
/**
* Provides the base asset type class.
*/
abstract class AssetTypeBase extends FarmEntityTypeBase implements AssetTypeInterface {
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->pluginDefinition['label'];
}
/**
* {@inheritdoc}
*/
public function getWorkflowId() {
return $this->pluginDefinition['workflow'];
}
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
return [];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Drupal\farm_entity\Plugin\Asset\AssetType;
use Drupal\entity\BundlePlugin\BundlePluginInterface;
/**
* Defines the interface for asset types.
*/
interface AssetTypeInterface extends BundlePluginInterface {
/**
* Gets the asset type label.
*
* @return string
* The asset type label.
*/
public function getLabel();
/**
* Gets the asset workflow ID.
*
* @return string
* The asset workflow ID.
*/
public function getWorkflowId();
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Drupal\farm_entity\Plugin\Asset\AssetType;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Provides a farmOS asset type base class.
*/
class FarmAssetType extends AssetTypeBase {
use StringTranslationTrait;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Drupal\farm_entity\Plugin\Log\LogType;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Provides a farmOS log type base class.
*/
class FarmLogType extends LogTypeBase {
use StringTranslationTrait;
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Drupal\farm_entity\Plugin\Log\LogType;
use Drupal\farm_entity\FarmEntityTypeBase;
/**
* Provides the base log type class.
*/
abstract class LogTypeBase extends FarmEntityTypeBase implements LogTypeInterface {
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->pluginDefinition['label'];
}
/**
* {@inheritdoc}
*/
public function getWorkflowId() {
return $this->pluginDefinition['workflow'];
}
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
return [];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Drupal\farm_entity\Plugin\Log\LogType;
use Drupal\entity\BundlePlugin\BundlePluginInterface;
/**
* Defines the interface for log types.
*/
interface LogTypeInterface extends BundlePluginInterface {
/**
* Gets the log type label.
*
* @return string
* The log type label.
*/
public function getLabel();
/**
* Gets the log workflow ID.
*
* @return string
* The log workflow ID.
*/
public function getWorkflowId();
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Drupal\farm_entity\Plugin\Plan\PlanType;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Provides a farmOS plan type base class.
*/
class FarmPlanType extends PlanTypeBase {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
$fields = [];
// Assets in the plan.
$options = [
'type' => 'entity_reference',
'label' => $this->t('Assets'),
'target_type' => 'asset',
'multiple' => TRUE,
'hidden' => TRUE,
];
$fields['asset'] = $this->farmFieldFactory->bundleFieldDefinition($options);
// Logs in the plan.
$options = [
'type' => 'entity_reference',
'label' => $this->t('Logs'),
'target_type' => 'log',
'multiple' => TRUE,
'hidden' => TRUE,
];
$fields['log'] = $this->farmFieldFactory->bundleFieldDefinition($options);
return $fields;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Drupal\farm_entity\Plugin\Plan\PlanType;
use Drupal\farm_entity\FarmEntityTypeBase;
/**
* Provides the base plan type class.
*/
abstract class PlanTypeBase extends FarmEntityTypeBase implements PlanTypeInterface {
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->pluginDefinition['label'];
}
/**
* {@inheritdoc}
*/
public function getWorkflowId() {
return $this->pluginDefinition['workflow'];
}
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
return [];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Drupal\farm_entity\Plugin\Plan\PlanType;
use Drupal\entity\BundlePlugin\BundlePluginInterface;
/**
* Defines the interface for plan types.
*/
interface PlanTypeInterface extends BundlePluginInterface {
/**
* Gets the plan type label.
*
* @return string
* The plan type label.
*/
public function getLabel();
/**
* Gets the plan workflow ID.
*
* @return string
* The plan workflow ID.
*/
public function getWorkflowId();
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Provides a farmOS plan record relationship type base class.
*/
class FarmPlanRecordType extends PlanRecordTypeBase {
use StringTranslationTrait;
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType;
use Drupal\farm_entity\FarmEntityTypeBase;
/**
* Provides the base plan record relationship type class.
*/
abstract class PlanRecordTypeBase extends FarmEntityTypeBase implements PlanRecordTypeInterface {
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->pluginDefinition['label'];
}
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
return [];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType;
use Drupal\entity\BundlePlugin\BundlePluginInterface;
/**
* Defines the interface for plan record relationship types.
*/
interface PlanRecordTypeInterface extends BundlePluginInterface {
/**
* Gets the plan record relationship type label.
*
* @return string
* The plan record relationship type label.
*/
public function getLabel();
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Drupal\farm_entity\Plugin\Quantity\QuantityType;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Provides a farmOS quantity type base class.
*/
class FarmQuantityType extends QuantityTypeBase {
use StringTranslationTrait;
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Drupal\farm_entity\Plugin\Quantity\QuantityType;
use Drupal\farm_entity\FarmEntityTypeBase;
/**
* Provides the base quantity type class.
*/
abstract class QuantityTypeBase extends FarmEntityTypeBase implements QuantityTypeInterface {
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->pluginDefinition['label'];
}
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
return [];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Drupal\farm_entity\Plugin\Quantity\QuantityType;
use Drupal\entity\BundlePlugin\BundlePluginInterface;
/**
* Defines the interface for quantity types.
*/
interface QuantityTypeInterface extends BundlePluginInterface {
/**
* Gets the quantity type label.
*
* @return string
* The quantity type label.
*/
public function getLabel();
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Drupal\farm_entity;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Manages discovery and instantiation of quantity type plugins.
*
* @see \Drupal\farm_entity\Annotation\QuantityType
* @see plugin_api
*/
class QuantityTypeManager extends DefaultPluginManager {
/**
* Constructs a new QuantityTypeManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* The cache backend.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Quantity/QuantityType', $namespaces, $module_handler, 'Drupal\farm_entity\Plugin\Quantity\QuantityType\QuantityTypeInterface', 'Drupal\farm_entity\Annotation\QuantityType');
$this->alterInfo('quantity_type_info');
$this->setCacheBackend($cache_backend, 'quantity_type_plugins');
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
foreach (['id', 'label'] as $required_property) {
if (empty($definition[$required_property])) {
throw new PluginException(sprintf('The quantity type %s must define the %s property.', $plugin_id, $required_property));
}
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Drupal\farm_entity\Routing;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\entity\Routing\DefaultHtmlRouteProvider as EntityDefaultHtmlRouteProvider;
/**
* Deny access to the entity type add form.
*
* New entity types of entities with bundle plugins cannot be created in the UI.
*
* @See https://www.drupal.org/project/farm/issues/3196423
*/
class DefaultHtmlRouteProvider extends EntityDefaultHtmlRouteProvider {
/**
* {@inheritdoc}
*/
protected function getAddFormRoute(EntityTypeInterface $entity_type) {
$route = parent::getAddFormRoute($entity_type);
if (!empty($route)) {
$route->setRequirement('_access', 'FALSE');
}
return $route;
}
}

View File

@@ -0,0 +1,12 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_entity_bundle_fields_test
id: second
label: Second
description: ''
name_pattern: 'Second plan [plan:id]'
workflow: plan_default
new_revision: true

View File

@@ -0,0 +1,8 @@
name: farmOS Bundle Fields Test
description: Module for testing farmOS bundle fields behavior.
type: module
package: Testing
core_version_requirement: ^10
dependencies:
- farm:farm_entity
- farm:farm_entity_test

View File

@@ -0,0 +1,33 @@
<?php
namespace Drupal\farm_entity_bundle_fields_test\Plugin\Plan\PlanType;
use Drupal\entity\BundleFieldDefinition;
use Drupal\farm_entity\Plugin\Plan\PlanType\FarmPlanType;
/**
* Provides the second test plan type.
*
* @PlanType(
* id = "second",
* label = @Translation("Second"),
* )
*/
class Second extends FarmPlanType {
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
// Inherit all plan fields.
$fields = parent::buildFieldDefinitions();
// Create a field for just this bundle.
$fields['second_plan_field'] = BundleFieldDefinition::create('boolean')
->setLabel($this->t('Test field for second plan type'));
return $fields;
}
}

View File

@@ -0,0 +1,8 @@
name: farmOS Entity Contrib Test
description: Module for testing farmOS contrib module behavior.
type: module
package: Testing
core_version_requirement: ^10
dependencies:
- farm:farm_entity
- farm:farm_entity_test

View File

@@ -0,0 +1,26 @@
<?php
/**
* @file
* Contains farm_entity_contrib_test.module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_farm_entity_bundle_field_info().
*/
function farm_entity_contrib_test_farm_entity_bundle_field_info(EntityTypeInterface $entity_type, string $bundle) {
$fields = [];
// Add a new bundle field to test logs.
if ($entity_type->id() == 'log' && in_array($bundle, ['test'])) {
$options = [
'type' => 'string',
'label' => t('Test hook bundle field'),
];
$fields['test_contrib_hook_bundle_field'] = \Drupal::service('farm_field.factory')->bundleFieldDefinition($options);
}
return $fields;
}

View File

@@ -0,0 +1,8 @@
langcode: en
status: true
dependencies: { }
id: test
label: Test
description: ''
workflow: asset_default
new_revision: true

View File

@@ -0,0 +1,9 @@
langcode: en
status: true
dependencies: { }
id: test
label: Test
description: ''
name_pattern: 'Test log [log:id]'
workflow: log_default
new_revision: true

View File

@@ -0,0 +1,9 @@
langcode: en
status: true
dependencies: { }
id: test_override
label: Test Override
description: ''
name_pattern: 'Test override log [log:id]'
workflow: log_default
new_revision: true

View File

@@ -0,0 +1,9 @@
langcode: en
status: true
dependencies: { }
id: test
label: Test
description: ''
name_pattern: 'Test plan [plan:id]'
workflow: plan_default
new_revision: true

View File

@@ -0,0 +1,8 @@
name: farmOS Entity Test
description: Module for testing farmOS entities.
type: module
package: Testing
core_version_requirement: ^10
dependencies:
- farm:farm_entity
- farm:plan

View File

@@ -0,0 +1,54 @@
<?php
/**
* @file
* Contains farm_entity_test.module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_entity_base_field_info().
*/
function farm_entity_test_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = [];
// Add a new base field to all logs.
if ($entity_type->id() == 'log') {
$options = [
'type' => 'string',
'label' => t('Test hook base field'),
];
$fields['test_hook_base_field'] = \Drupal::service('farm_field.factory')->baseFieldDefinition($options);
}
return $fields;
}
/**
* Implements hook_farm_entity_bundle_field_info().
*/
function farm_entity_test_farm_entity_bundle_field_info(EntityTypeInterface $entity_type, string $bundle) {
$fields = [];
// Add a new bundle field to test logs.
if ($entity_type->id() == 'log' && in_array($bundle, ['test', 'test_override'])) {
$options = [
'type' => 'string',
'label' => t('Test hook bundle field'),
];
$fields['test_hook_bundle_field'] = \Drupal::service('farm_field.factory')->bundleFieldDefinition($options);
}
// Add bundle specific fields to all log types.
if ($entity_type->id() == 'log') {
$options = [
'type' => 'string',
'label' => t('Test bundle specific field for: @bundle', ['@bundle' => $bundle]),
];
$field_name = 'test_hook_bundle_' . $bundle . '_specific_field';
$fields[$field_name] = \Drupal::service('farm_field.factory')->bundleFieldDefinition($options);
}
return $fields;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Drupal\farm_entity_test\Plugin\Asset\AssetType;
use Drupal\farm_entity\Plugin\Asset\AssetType\FarmAssetType;
/**
* Provides the test asset type.
*
* @AssetType(
* id = "test",
* label = @Translation("Test"),
* )
*/
class Test extends FarmAssetType {
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Drupal\farm_entity_test\Plugin\Log\LogType;
use Drupal\farm_entity\Plugin\Log\LogType\FarmLogType;
/**
* Provides the test log type.
*
* @LogType(
* id = "test",
* label = @Translation("Test"),
* )
*/
class Test extends FarmLogType {
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
$fields = parent::buildFieldDefinitions();
// Add a test field to all Log bundles.
$options = [
'type' => 'string',
'label' => $this->t('Test default bundle field'),
];
$fields['test_default_bundle_field'] = $this->farmFieldFactory->bundleFieldDefinition($options);
return $fields;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Drupal\farm_entity_test\Plugin\Log\LogType;
/**
* Provides the test_override log type.
*
* @LogType(
* id = "test_override",
* label = @Translation("Test Override"),
* )
*/
class TestOverride extends Test {
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
// We are inheriting from the Test log type, which adds a bundle field. We
// are going to return an empty array to show that we can disable those
// default fields on specific log types.
return [];
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Drupal\farm_entity_test\Plugin\Plan\PlanType;
use Drupal\farm_entity\Plugin\Plan\PlanType\FarmPlanType;
/**
* Provides the test plan type.
*
* @PlanType(
* id = "test",
* label = @Translation("Test"),
* )
*/
class Test extends FarmPlanType {
}

View File

@@ -0,0 +1,209 @@
<?php
namespace Drupal\Tests\farm_entity\Functional;
use Drupal\Tests\farm_test\Functional\FarmBrowserTestBase;
/**
* Tests that bundle fields are created during a postponed install.
*
* @group farm
*/
class FarmEntityBundleFieldTest extends FarmBrowserTestBase {
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The module installer service.
*
* @var \Drupal\Core\Extension\ModuleInstallerInterface
*/
protected $moduleInstaller;
/**
* {@inheritdoc}
*/
protected static $modules = [
'farm_entity',
'farm_entity_test',
'farm_entity_bundle_fields_test',
];
/**
* {@inheritdoc}
*/
protected function setUp():void {
parent::setUp();
$this->entityFieldManager = $this->container->get('entity_field.manager');
$this->entityTypeManager = $this->container->get('entity_type.manager');
$this->database = $this->container->get('database');
$this->moduleInstaller = $this->container->get('module_installer');
}
/**
* Run all tests.
*/
public function testAll() {
$this->doTestBundleFieldMapUpdates();
$this->doTestBundleFieldPostponedInstall();
$this->doTestBundlePluginModuleUninstallation();
}
/**
* Test that bundle field maps are updated on install/uninstall.
*/
public function doTestBundleFieldMapUpdates() {
// Get the entity field map.
$field_map = $this->entityFieldManager->getFieldMap();
// Confirm that the 'test_default_bundle_field' exists in the log field map.
$this->assertArrayHasKey('test_default_bundle_field', $field_map['log']);
// Confirm that the 'test_contrib_hook_bundle_field' does NOT exist (yet).
$this->assertArrayNotHasKey('test_contrib_hook_bundle_field', $field_map['log']);
// Install the farm_entity_contrib_test module.
$result = $this->moduleInstaller->install(['farm_entity_contrib_test']);
$this->assertTrue($result);
// Reload the entity field map. We need to get a new instance of the
// entity_field.manager service from the container without old state.
$this->container->set('entity_field.manager', NULL);
$this->entityFieldManager = $this->container->get('entity_field.manager');
$field_map = $this->entityFieldManager->getFieldMap();
// Confirm that the 'test_contrib_hook_bundle_field' exists in the log field
// map, and exists in the 'test' bundle, but not in 'test_override'.
$this->assertArrayHasKey('test_contrib_hook_bundle_field', $field_map['log']);
$this->assertContains('test', $field_map['log']['test_contrib_hook_bundle_field']['bundles']);
$this->assertNotContains('test_override', $field_map['log']['test_contrib_hook_bundle_field']['bundles']);
// Uninstall the farm_entity_contrib_test module.
$result = $this->moduleInstaller->uninstall(['farm_entity_contrib_test']);
$this->assertTrue($result);
// Reload the entity field map. We need to get a new instance of the
// entity_field.manager service from the container without old state.
$this->container->set('entity_field.manager', NULL);
$this->entityFieldManager = $this->container->get('entity_field.manager');
$field_map = $this->entityFieldManager->getFieldMap();
// Confirm that the 'test_contrib_hook_bundle_field' no longer exists in the
// log field map.
$this->assertArrayNotHasKey('test_contrib_hook_bundle_field', $field_map['log']);
}
/**
* Test installing the farm_entity_contrib_test module after farm_entity_test.
*/
public function doTestBundleFieldPostponedInstall() {
// Install the farm_entity_contrib_test module.
$result = $this->moduleInstaller->install(['farm_entity_contrib_test'], TRUE);
$this->assertTrue($result);
// Must clear the cache for the test environment.
$this->entityFieldManager->clearCachedFieldDefinitions();
// Test bundle field definition exists.
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test');
$this->assertArrayHasKey('test_contrib_hook_bundle_field', $fields);
// Test log field storage definition exists.
$this->assertFieldStorageDefinitionExists('log', 'test_contrib_hook_bundle_field');
// Save the contrib field storage definition for later.
$installed_contrib_field_storage_definition = $this->entityFieldManager->getFieldStorageDefinitions('log')['test_contrib_hook_bundle_field'];
// Uninstall the farm_entity_contrib_test module.
$result = $this->moduleInstaller->uninstall(['farm_entity_contrib_test']);
$this->assertTrue($result);
// Must clear the cache for the test environment.
$this->entityFieldManager->clearCachedFieldDefinitions();
// Test bundle field definition is deleted.
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test');
$this->assertArrayNotHasKey('test_contrib_hook_bundle_field', $fields);
// Test log field storage definition is deleted.
$this->assertFieldStorageDefinitionExists('log', 'test_contrib_hook_bundle_field', FALSE);
// Ensure the database table was deleted.
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $this->entityTypeManager->getStorage('log')->getTableMapping();
$table = $table_mapping->getDedicatedDataTableName($installed_contrib_field_storage_definition);
$this->assertFalse($this->database->schema()->tableExists($table));
}
/**
* Test that bundle fields can be reused across bundles.
*/
public function doTestBundlePluginModuleUninstallation() {
// Test that database tables exist after uninstalling a bundle with
// a field storage definition used by other bundles.
$this->moduleInstaller->uninstall(['farm_entity_bundle_fields_test']);
// Must clear the cache for the test environment.
$this->entityFieldManager->clearCachedFieldDefinitions();
// Test that correct field storage definitions and database tables exist.
$test_fields = [
'second_plan_field' => FALSE,
'asset' => TRUE,
'log' => TRUE,
];
foreach ($test_fields as $field_name => $exists) {
$this->assertFieldStorageDefinitionExists('plan', $field_name, $exists);
}
}
/**
* Helper function to check the existence of field storage definitions.
*
* @param string $entity_type
* The entity type to check.
* @param string $field_name
* The field name to check.
* @param bool $exists
* If the field should exists, defaults to TRUE.
*/
protected function assertFieldStorageDefinitionExists(string $entity_type, string $field_name, bool $exists = TRUE) {
$field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type);
// Test the field storage definition existence.
$this->assertEquals($exists, array_key_exists($field_name, $field_storage_definitions));
// Test that the database table exists if the field storage definition
// exists.
if ($exists) {
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $this->entityTypeManager->getStorage($entity_type)->getTableMapping();
$table = $table_mapping->getDedicatedDataTableName($field_storage_definitions[$field_name]);
$this->assertTrue($this->database->schema()->tableExists($table));
}
}
}

View File

@@ -0,0 +1,197 @@
<?php
namespace Drupal\Tests\farm_entity\Kernel;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests farmOS entity fields.
*
* @group farm
*/
class FarmEntityFieldTest extends KernelTestBase {
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* {@inheritdoc}
*/
protected $profile = 'farm';
/**
* {@inheritdoc}
*/
protected static $modules = [
'entity',
'asset',
'log',
'plan',
'farm_field',
'farm_entity',
'farm_entity_fields',
'farm_entity_test',
'farm_flag',
'farm_id_tag',
'farm_location',
'farm_log',
'farm_log_asset',
'farm_owner',
'farm_parent',
'taxonomy',
];
/**
* {@inheritdoc}
*/
protected function setUp():void {
parent::setUp();
$this->entityFieldManager = $this->container->get('entity_field.manager');
}
/**
* Test farmOS fields defined in hook_entity_base_field_info().
*/
public function testHookEntityBaseFieldInfo() {
// Test asset field storage definitions.
$fields = $this->entityFieldManager->getFieldStorageDefinitions('asset');
$field_names = [
'data',
'flag',
'file',
'id_tag',
'image',
'intrinsic_geometry',
'is_fixed',
'is_location',
'notes',
'owner',
'parent',
];
foreach ($field_names as $field_name) {
$this->assertArrayHasKey($field_name, $fields, "The asset $field_name field exists.");
}
// Test parent field constraints.
$parent_field_constraints = $fields['parent']->getConstraints();
$this->assertArrayHasKey('CircularReference', $parent_field_constraints);
$this->assertArrayHasKey('DuplicateReference', $parent_field_constraints);
// Test log field storage definitions.
$fields = $this->entityFieldManager->getFieldStorageDefinitions('log');
$field_names = [
'asset',
'data',
'flag',
'file',
'geometry',
'image',
'is_movement',
'location',
'notes',
'owner',
'test_hook_base_field',
];
foreach ($field_names as $field_name) {
$this->assertArrayHasKey($field_name, $fields, "The log $field_name field exists.");
}
// Test plan field storage definitions.
$fields = $this->entityFieldManager->getFieldStorageDefinitions('plan');
$field_names = [
'data',
'flag',
'file',
'image',
'notes',
];
foreach ($field_names as $field_name) {
$this->assertArrayHasKey($field_name, $fields, "The plan $field_name field exists.");
}
// Test taxonomy term field storage definitions.
$fields = $this->entityFieldManager->getFieldStorageDefinitions('taxonomy_term');
$field_names = [
'file',
'image',
'external_uri',
];
foreach ($field_names as $field_name) {
$this->assertArrayHasKey($field_name, $fields, "The taxonomy term $field_name field exists.");
}
}
/**
* Test farmOS fields defined in hook_farm_entity_bundle_field_info().
*/
public function testHookFarmEntityBundleFieldInfo() {
// Get the log field storage definitions.
$log_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions('log');
// Test that 'test_hook_bundle_field' has a storage definition with the
// correct provider.
$this->assertArrayHasKey('test_hook_bundle_field', $log_storage_definitions);
$this->assertEquals('farm_entity_test', $log_storage_definitions['test_hook_bundle_field']->getProvider());
// Test fields definitions for the 'test' log type.
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test');
$this->assertArrayHasKey('test_hook_bundle_field', $fields);
// Test fields definitions for the 'test_override' log type.
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test_override');
$this->assertArrayHasKey('test_hook_bundle_field', $fields);
// Get all log bundles.
/** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info */
$entity_type_bundle_info = $this->container->get('entity_type.bundle.info');
$bundles = $entity_type_bundle_info->getBundleInfo('log');
// Test that all log types have a bundle specific field.
foreach (array_keys($bundles) as $bundle) {
$fields = $this->entityFieldManager->getFieldDefinitions('log', $bundle);
$field_name = 'test_hook_bundle_' . $bundle . '_specific_field';
// Assert field storage definition exists and has the correct provider.
$this->assertArrayHasKey($field_name, $log_storage_definitions);
$this->assertEquals('farm_entity_test', $log_storage_definitions[$field_name]->getProvider());
// Assert field definition for the bundle.
$this->assertArrayHasKey($field_name, $fields);
}
}
/**
* Test farmOS fields defined in buildFieldDefinitions().
*/
public function testBuildFieldDefinitions() {
// Test plan field definitions.
$fields = $this->entityFieldManager->getFieldDefinitions('plan', 'test');
$this->assertArrayHasKey('asset', $fields);
$this->assertArrayHasKey('log', $fields);
}
/**
* Test that farmOS base fields can be overridden.
*/
public function testFarmFieldsOverride() {
// Load field definitions for test_override logs.
$fields = $this->entityFieldManager->getFieldDefinitions('log', 'test_override');
// Test that a module extending FarmLogType can remove default bundle fields
// that were provided in parent plugin classes.
$this->assertArrayNotHasKey('test_default_bundle_field', $fields);
// But also confirm that a module extending a base log type can NOT remove
// bundle fields that were provided by hook_farm_entity_bundle_field_info().
$this->assertArrayHasKey('test_hook_bundle_field', $fields);
}
}