Provide a plan_record entity type for plan record relationships with metadata #781

This commit is contained in:
Michael Stenta 2024-02-02 19:55:41 -05:00
commit ee71b28ae6
18 changed files with 439 additions and 2 deletions

View File

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [Inventory quick form #766](https://github.com/farmOS/farmOS/pull/766)
- [Add UI for creating instances of quick forms #785](https://github.com/farmOS/farmOS/pull/785)
- [Show map on /locations #779](https://github.com/farmOS/farmOS/pull/779)
- [Provide a plan_record entity type for plan record relationships with metadata #781](https://github.com/farmOS/farmOS/pull/781)
### Changed

View File

@ -25,7 +25,7 @@
"drupal/date_popup": "^1.3",
"drupal/entity": "1.4",
"drupal/entity_browser": "^2.10",
"drupal/entity_reference_integrity": "^1.1",
"drupal/entity_reference_integrity": "1.2",
"drupal/entity_reference_revisions": "1.11",
"drupal/entity_reference_validators": "^1.0@beta",
"drupal/exif_orientation": "^1.2",
@ -67,6 +67,9 @@
"drupal/entity": {
"Issue #3206703: Provide reverse relationships for bundle plugin entity_reference fields.": "https://www.drupal.org/files/issues/2022-05-11/3206703-10.patch"
},
"drupal/entity_reference_integrity": {
"Issue #3418000: Delete action only overridden on first entity type": "https://www.drupal.org/files/issues/2024-01-30/3418000-3.patch"
},
"drupal/entity_reference_revisions": {
"Issue #3267304: Infer target_revision_id to be Latest Revision when Only a target_id is Provided": "https://www.drupal.org/files/issues/2022-05-13/3267304-9.patch"
},

View File

@ -16,6 +16,7 @@ function farm_entity_install() {
'data_stream',
'file',
'log',
'plan',
'quantity',
'taxonomy_term',
'user',

View File

@ -46,7 +46,7 @@ function farm_entity_entity_type_build(array &$entity_types) {
}
// Enable the use of bundle plugins on specific entity types.
foreach (['asset', 'log', 'plan', 'quantity'] as $entity_type) {
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);

View File

@ -0,0 +1,17 @@
<?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();
}

View File

@ -12,6 +12,9 @@ services:
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,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,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,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

@ -19,6 +19,20 @@ plan.type.*:
type: boolean
label: 'Create new revision'
plan.record.type.*:
type: config_entity
label: 'Plan record relationship type'
mapping:
id:
type: string
label: 'Machine-readable name'
label:
type: label
label: 'Type'
description:
type: text
label: 'Description'
condition.plugin.plan_type:
type: condition.plugin
mapping:

View File

@ -0,0 +1,18 @@
<?php
/**
* @file
* Post update hooks for the plan module.
*/
/**
* Install plan_record and plan_record_type entity types.
*/
function plan_post_update_install_plan_record(&$sandbox) {
\Drupal::entityDefinitionUpdateManager()->installEntityType(
\Drupal::entityTypeManager()->getDefinition('plan_record_type')
);
\Drupal::entityDefinitionUpdateManager()->installEntityType(
\Drupal::entityTypeManager()->getDefinition('plan_record')
);
}

View File

@ -0,0 +1,30 @@
<?php
namespace Drupal\plan\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Defines plan_record access logic.
*/
class PlanRecordAccess extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
// If a plan is referenced, access is based on access to the plan.
/** @var \Drupal\plan\Entity\PlanRecordInterface $plan */
if ($plan = $entity->getPlan()) {
return AccessResult::allowedIf($plan->access($operation, $account));
}
// Otherwise, delegate to the parent method.
return parent::checkAccess($entity, $operation, $account);
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace Drupal\plan\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Defines the Plan record relationship entity.
*
* This entity type can be used to create relationships between a plan and other
* record(s) along with additional metadata fields to describe the relationship.
*
* @ContentEntityType(
* id = "plan_record",
* label = @Translation("Plan record relationship"),
* bundle_label = @Translation("Plan record relationship type"),
* label_collection = @Translation("Plan record relationships"),
* label_singular = @Translation("plan record relationship"),
* label_plural = @Translation("plan record relationships"),
* label_count = @PluralTranslation(
* singular = "@count plan record relationship",
* plural = "@count plan record relationships",
* ),
* handlers = {
* "access" = "Drupal\plan\Access\PlanRecordAccess",
* "form" = {
* "edit" = "Drupal\Core\Entity\ContentEntityForm",
* },
* "route_provider" = {
* "default" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
* },
* },
* base_table = "plan_record",
* data_table = "plan_record_data",
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "label" = "uuid",
* "bundle" = "type",
* },
* bundle_entity_type = "plan_record_type",
* common_reference_target = TRUE,
* links = {
* "edit-form" = "/plan/record/{plan_record}/edit",
* },
* )
*/
class PlanRecord extends ContentEntityBase implements PlanRecordInterface {
/**
* {@inheritdoc}
*/
public function getBundleLabel() {
/** @var \Drupal\plan\Entity\PlanRecordTypeInterface $type */
$type = \Drupal::entityTypeManager()
->getStorage('plan_record_type')
->load($this->bundle());
return $type->label();
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['plan'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Plan'))
->setDescription(t('Associate this plan record relationship with a plan entity.'))
->setTranslatable(FALSE)
->setCardinality(1)
->setSetting('target_type', 'plan')
->setDisplayOptions('form', [
'type' => 'entity_reference',
'weight' => 12,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
return $fields;
}
/**
* {@inheritdoc}
*/
public function getPlan(): ?PlanInterface {
return $this->get('plan')->first()?->entity;
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Drupal\plan\Entity;
use Drupal\Core\Entity\ContentEntityInterface;
/**
* Provides an interface for defining plan record relationship entities.
*/
interface PlanRecordInterface extends ContentEntityInterface {
/**
* Gets the label of the plan record relationship type.
*
* @return string
* The label of the plan record relationship type.
*/
public function getBundleLabel();
/**
* Returns the Plan entity the plan record is assigned to.
*
* @return \Drupal\plan\Entity\PlanInterface|null
* The plant entity or NULL if not assigned.
*/
public function getPlan(): ?PlanInterface;
}

View File

@ -0,0 +1,74 @@
<?php
namespace Drupal\plan\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
/**
* Defines the Plan record relationship type entity.
*
* @ConfigEntityType(
* id = "plan_record_type",
* label = @Translation("Plan record relationship type"),
* label_collection = @Translation("Plan record relationship types"),
* label_singular = @Translation("Plan record relationship type"),
* label_plural = @Translation("plan record relationship types"),
* label_count = @PluralTranslation(
* singular = "@count plan record relationship type",
* plural = "@count plan record relationship types",
* ),
* handlers = {
* "access" = "\Drupal\entity\BundleEntityAccessControlHandler",
* },
* config_prefix = "record.type",
* bundle_of = "plan_record",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid",
* },
* config_export = {
* "id",
* "label",
* "description",
* }
* )
*/
class PlanRecordType extends ConfigEntityBundleBase implements PlanRecordTypeInterface {
/**
* The Plan record relationship type ID.
*
* @var string
*/
protected $id;
/**
* The Plan record relationship type label.
*
* @var string
*/
protected $label;
/**
* A brief description of this plan record relationship type.
*
* @var string
*/
protected $description;
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->description;
}
/**
* {@inheritdoc}
*/
public function setDescription($description) {
return $this->set('description', $description);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Drupal\plan\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\EntityDescriptionInterface;
/**
* Provides an interface for defining Plan record relationship type entities.
*/
interface PlanRecordTypeInterface extends ConfigEntityInterface, EntityDescriptionInterface {
}