Skip to content

Commit

Permalink
Typed Relation (#3)
Browse files Browse the repository at this point in the history
* add token requirement

Person's auto_entitylabel config isn't working without the token module.

* add TypedRelation field

includes field, widget, and formatter

* added field_linked_agent

* coding standards

* more schema relationship predicates

* remove unused variable
  • Loading branch information
seth-shaw-unlv authored and whikloj committed Aug 29, 2018
1 parent 5263576 commit 23d4da6
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 1 deletion.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"require": {
"drupal/auto_entitylabel": "^2.1@beta",
"drupal/name": "^1.0@RC",
"drupal/geolocation": "^2.0"
"drupal/geolocation": "^2.0",
"drupal/token": "^1.3"
},
"require-dev": {
"phpunit/phpunit": "^6",
Expand Down
10 changes: 10 additions & 0 deletions config/install/core.entity_form_display.node.person.default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies:
- field.field.node.person.field_cat_date_begin
- field.field.node.person.field_cat_date_end
- field.field.node.person.field_cat_member_of
- field.field.node.person.field_linked_agent
- field.field.node.person.field_person_alternate_names
- field.field.node.person.field_person_name_authorities
- field.field.node.person.field_person_preferred_name
Expand Down Expand Up @@ -62,6 +63,15 @@ content:
third_party_settings: { }
type: entity_reference_autocomplete
region: content
field_linked_agent:
weight: 29
settings:
match_operator: CONTAINS
size: '60'
placeholder: ''
third_party_settings: { }
type: typed_relation_default
region: content
field_person_alternate_names:
weight: 3
settings: { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies:
- field.field.node.person.field_cat_date_begin
- field.field.node.person.field_cat_date_end
- field.field.node.person.field_cat_member_of
- field.field.node.person.field_linked_agent
- field.field.node.person.field_person_alternate_names
- field.field.node.person.field_person_name_authorities
- field.field.node.person.field_person_preferred_name
Expand Down Expand Up @@ -61,6 +62,14 @@ content:
third_party_settings: { }
type: entity_reference_label
region: content
field_linked_agent:
weight: 9
label: above
settings:
link: true
third_party_settings: { }
type: typed_relation_default
region: content
field_person_alternate_names:
weight: 3
label: above
Expand Down
11 changes: 11 additions & 0 deletions config/install/core.entity_view_display.node.person.teaser.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
uuid: 4ccf6aa8-482a-4bfe-bb49-849fdf573c84
langcode: en
status: true
dependencies:
config:
- core.entity_view_mode.node.teaser
- field.field.node.person.body
- field.field.node.person.field_cat_date_begin
- field.field.node.person.field_cat_date_end
- field.field.node.person.field_cat_member_of
- field.field.node.person.field_person_alternate_names
- field.field.node.person.field_person_name_authorities
- field.field.node.person.field_person_preferred_name
- field.field.node.person.field_relation
- node.type.person
module:
Expand All @@ -27,5 +33,10 @@ content:
weight: 100
region: content
hidden:
field_cat_date_begin: true
field_cat_date_end: true
field_cat_member_of: true
field_person_alternate_names: true
field_person_name_authorities: true
field_person_preferred_name: true
field_relation: true
46 changes: 46 additions & 0 deletions config/install/field.field.node.person.field_linked_agent.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
uuid: f7e611af-b0d6-4325-81dc-52839a6c56be
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_linked_agent
- node.type.corporate_body
- node.type.family
- node.type.person
module:
- controlled_access_terms
id: node.person.field_linked_agent
field_name: field_linked_agent
entity_type: node
bundle: person
label: 'Linked Agent'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:node'
handler_settings:
target_bundles:
corporate_body: corporate_body
family: family
person: person
sort:
field: _none
auto_create: 0
auto_create_bundle: family
rel_types:
'schema:knows': Knows
'schema:alumniOf': Alumni Of
'schema:children': Children
'schema:colleague': Colleague
'schema:follows': Follows
'schema:knowsAbout': Knows About
'schema:parent': Parent
'schema:relatedTo': Related To
'schema:sibling': Sibling
'schema:sponsor': Sponsor
'schema:spouse': Spouse
'schema:worksFor': Works For (use only with Corporate Bodies)
field_type: typed_relation
20 changes: 20 additions & 0 deletions config/install/field.storage.node.field_linked_agent.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
uuid: 5eadb639-a448-437d-b048-e2b92b040cf6
langcode: en
status: true
dependencies:
module:
- controlled_access_terms
- node
id: node.field_linked_agent
field_name: field_linked_agent
entity_type: node
type: typed_relation
settings:
target_type: node
module: controlled_access_terms
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
1 change: 1 addition & 0 deletions controlled_access_terms.info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ dependencies:
- name
- auto_entitylabel
- geolocation
- token
38 changes: 38 additions & 0 deletions src/Plugin/Field/FieldFormatter/TypedRelationFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Drupal\controlled_access_terms\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceLabelFormatter;
use Drupal\Core\Field\FieldItemListInterface;

/**
* Plugin implementation of the 'TypedRelationFormatter'.
*
* @FieldFormatter(
* id = "typed_relation_default",
* label = @Translation("Typed Relation Formatter"),
* field_types = {
* "typed_relation"
* }
* )
*/
class TypedRelationFormatter extends EntityReferenceLabelFormatter {

/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = parent::viewElements($items, $langcode);

foreach ($items as $delta => $item) {

$rel_types = $item->getRelTypes();
$rel_type = isset($rel_types[$item->rel_type]) ? $rel_types[$item->rel_type] : $item->rel_type;

$elements[$delta]['#prefix'] = $rel_type . ': ';
}

return $elements;
}

}
187 changes: 187 additions & 0 deletions src/Plugin/Field/FieldType/TypedRelation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php

namespace Drupal\controlled_access_terms\Plugin\Field\FieldType;

use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\TypedData\DataDefinition;

/**
* Implements a Typed Relation field.
*
* @FieldType(
* id = "typed_relation",
* label = @Translation("Typed Relation"),
* module = "controlled_access_terms",
* description = @Translation("Implements a typed relation field"),
* default_formatter = "typed_relation_default",
* default_widget = "typed_relation_default",
* list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
* )
*/
class TypedRelation extends EntityReferenceItem {

/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$schema['columns']['rel_type'] = [
'type' => 'text',
'size' => 'tiny',
'not null' => TRUE,
];

return $schema;
}

/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
$properties['rel_type'] = DataDefinition::create('string')
->setLabel(t('Type'))
->setRequired(TRUE);

return $properties;
}

/**
* {@inheritdoc}
*/
public function isEmpty() {
$parentEmpty = parent::isEmpty();

// All must have a value.
if ($this->rel_type !== NULL &&
!empty($this->rel_type) &&
!($parentEmpty)
) {
return FALSE;
}

return TRUE;
}

/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return ['rel_types' => []] + parent::defaultFieldSettings();
}

/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = parent::fieldSettingsForm($form, $form_state);

$element['rel_types'] = [
'#type' => 'textarea',
'#title' => t('Available Relations'),
'#default_value' => $this->encodeTextSettingsField($this->getSetting('rel_types')),
'#element_validate' => [[get_class($this), 'validateValues']],
'#required' => TRUE,
'#min' => 1,
'#description' => '<p>' . t('Enter one value per line, in the format key|label.') .
'<br/>' . t('The key is the stored value. The label will be used in displayed values and edit forms.') .
'<br/>' . t("Keys may not contain dots ('.'). They will be removed if used.") .
'<br/>' . t('The label is optional: if a line contains a single string, it will be used as key and label.') .
'</p>',
];

return $element;
}

/**
* Convience method allowing the Formatter to get the rel_types.
*
* @return array
* The array of relation types
*/
public function getRelTypes() {
return $this->getSetting('rel_types');
}

/**
* Encodes pipe-delimited key/value pairs.
*
* @param array $settings
* The array of key/value pairs to encode.
*
* @return string
* The string of encoded key/value pairs.
*/
protected function encodeTextSettingsField(array $settings) {
$output = '';
foreach ($settings as $key => $value) {
$output .= "$key|$value\n";
}
return $output;
}

/**
* Extracts pipe-delimited key/value pairs.
*
* @param string $string
* The raw string to extract values from.
*
* @return array|null
* The array of extracted key/value pairs, or NULL if the string is invalid.
*
* @see \Drupal\options\Plugin\Field\FieldType\ListItemBase::extractAllowedValues()
*/
protected static function extractPipedValues($string) {
$values = [];

$list = explode("\n", $string);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');

foreach ($list as $position => $text) {
// Check for an explicit key.
$matches = [];
if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
// Trim key and value to avoid unwanted spaces issues.
// Also remove dots in keys (which aren't permitted.)
$key = str_replace('.', '', trim($matches[1]));
$value = trim($matches[2]);
}
// Otherwise use the value as key and value.
else {
$key = $value = $text;
}

$values[$key] = $value;
}

return $values;
}

/**
* Callback for settings form.
*
* @param \Drupal\Core\Render\Element\FormElement $element
* An associative array containing the properties and children of the
* generic form element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form for the form this element belongs to.
*
* @see \Drupal\Core\Render\Element\FormElement::processPattern()
*/
public static function validateValues(FormElement $element, FormStateInterface $form_state) {
$values = static::extractPipedValues($element['#value']);

if (!is_array($values)) {
$form_state->setError($element, t('Allowed values list: invalid input.'));
}
else {
// We may want to validate key values in the future...
$form_state->setValueForElement($element, $values);
}
}

}
Loading

0 comments on commit 23d4da6

Please sign in to comment.