Commit f6d0fdf4 authored by Joel Muzzerall's avatar Joel Muzzerall
Browse files

Issue #2628388: Replaced 8.x-2.x branch with an informative README.

parent 53b971f6
The 8.x-2.x branch of field collection is no longer supported or maintained.
For details, see https://www.drupal.org/node/2628388
To use the final version of 8.x-2.x, use the command:
git checkout 53b971f6dccce0ee5911346ea6eadbb6ae789034
langcode: en
status: true
dependencies:
module:
- field_collection
id: field_collection_item.table
label: Table
targetEntityType: field_collection_item
cache: true
# Schema for the configurations files of the field_collection module
field_collection.field_collection.*:
type: config_entity
label: 'Field collection'
mapping:
id:
type: string
label: 'Machine-readable name'
label:
type: label
label: 'Human-readable name'
<?php
/**
* @file
* Field module functionality for the Field Collection module.
*/
/**
* Implements hook_field_formatter_info_alter().
*
* Allow any formatter that works with Entity References to be used with Field Collection.
*/
function field_collection_field_formatter_info_alter(&$info){
foreach ($info as &$formatter) {
if (isset($formatter['field_types'])) {
if (in_array('entity_reference',$formatter['field_types'])
&& !in_array('field_collection',$formatter['field_types'])
) {
$formatter['field_types'][] = 'field_collection';
}
}
}
}
name: Field Collection
description: 'Provides a field collection field, to which any number of fields can be attached.'
package: Field types
version: VERSION
core: 8.x
type: module
dependencies:
- field
- inline_entity_form
field_collection.overview_field_collections:
title: 'Field collections'
parent: system.admin_structure
description: 'Manage field collections.'
route_name: field_collection.overview_field_collections
<?php
/**
* @file
* Module implementing field collection field type.
*/
use Drupal\field\FieldStorageConfigInterface;
use Drupal\field_collection\Entity\FieldCollection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Url;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
// Load all Field module hooks for File.
require_once __DIR__ . '/field_collection.field.inc';
/**
* Implements hook_help().
*/
function field_collection_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.field_collection':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The field collection module provides a field, to which any number of fields can be attached. See the <a href="@field-help">Field module help page</a> for more information about fields.', [
'@field-help' => Url::fromRoute('help.page', ['name' => 'field']),
]) . '</p>';
return $output;
}
}
/**
* Implements hook_ENTITY_TYPE_insert() for field_storage_config.
*
* Create a field collection bundle when a new field collection field is made.
*/
function field_collection_field_storage_config_insert(FieldStorageConfigInterface $field) {
if ($field->getType() == 'field_collection') {
$field_collection_exists = \Drupal::entityQuery('field_collection')
->condition('id', $field->getName())
->count()
->execute();
if (!$field_collection_exists) {
$field_collection = new FieldCollection();
$field_collection->set('label', $field->getName());
$field_collection->set('id', $field->getName());
$field_collection->enforceIsNew();
$field_collection->save();
}
// TODO: entity_invoke_bundle_hook in post save like in nodeType ?
// Clear caches.
//entity_info_cache_clear();
// Do not directly issue menu rebuilds here to avoid potentially multiple
// rebuilds. Instead, let menu_get_item() issue the rebuild on the next
// request.
//
// TODO: Figure out whether this is still needed and replace it with the
// new API if it is.
// https://drupal.org/node/2183531
//
// variable_set('menu_rebuild_needed', TRUE);
}
}
/**
* Implements hook_ENTITY_TYPE_delete() for field_storage_config.
*
* Delete the field collection bundle when it's corrosponding field no longer
* exists in any bundle.
*/
function field_collection_field_storage_config_delete(EntityInterface $field) {
if ($field->getType() == 'field_collection') {
$field_collection_bundle = FieldCollection::load($field->getName());
$field_collection_bundle->delete();
}
}
/**
* Implements hook_ENTITY_TYPE_predelete() for field_config.
*
* Delete field collection item entities when their corrosponding field
* collection field is deleted.
*
* TODO: Perform the operation in batches because it will fail when there is
* too much data. See @link field_purge Field API bulk data deletion @endlink
* for inspiration.
*/
function field_collection_field_config_predelete(EntityInterface $field) {
if ($field->getType() == 'field_collection') {
// @todo This is not how you interpolate variables into a db_query().
$field_collection_item_ids = \Drupal::database()->query('SELECT `entity_id` FROM {' . $field->getTargetEntityTypeId() . '__' . $field->getName() . '} WHERE `bundle` = \'' . $field->getTargetBundle() . '\' ')->fetchCol();
$controller = \Drupal::entityTypeManager()->getStorage('field_collection_item');
$entities = $controller->loadMultiple($field_collection_item_ids);
$controller->delete($entities);
}
}
/**
* Implements hood_form_FORM_ID_alter() for field_collection_edit_form.
*
* Remove the save button since there are no options to save.
*/
function field_collection_form_field_collection_edit_form_alter(&$form, FormStateInterface $form_state) {
unset($form['actions']);
}
function field_collection_page_attachments(array &$attachments) {
$s = \Drupal::service('user.permissions');
}
/**
* Implements hood_form_FORM_ID_alter() for field_ui_field_edit_form.
*
* Remove default value from field collection field settings.
*/
function field_collection_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state) {
if ($form_state->getFormObject()->getEntity()->getType() == 'field_collection') {
unset($form['default_value']['widget']);
$form['default_value']['#description'] = t('To specify a default value, configure it via the regular default value setting of each field that is part of the field collection. To do so, go to the <a href=":url/fields">manage fields</a> screen of the field collection.', array(
':url' => Url::fromRoute('entity.field_collection.edit_form', array(
'field_collection' => $form_state->getFormObject()->getEntity()->getName()
))->setAbsolute()->toString(),
));
}
}
/**
* Sort function for items order.
*
* Copied from D7 '_field_sort_items_helper'.
*
* TODO: Replace this and references to it with whatever that function was
* replaced with in Drupal 8.
*/
function _field_collection_sort_items_helper($a, $b) {
$a_weight = (is_array($a) ? $a['_weight'] : 0);
$b_weight = (is_array($b) ? $b['_weight'] : 0);
return $a_weight - $b_weight;
}
/**
* Returns whether or not the FieldItemList is full.
*
* TODO: Find the standard way to do this and replace calls to it.
*/
function _field_collection_field_item_list_full(FieldItemListInterface $field_list) {
$cardinality = $field_list->getFieldDefinition()
->getFieldStorageDefinition()
->getCardinality();
$total = $field_list->count();
return ($cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && $cardinality <= $total);
}
administer field_collection_item fields:
title: 'Administer field collections'
description: 'Create and delete fields on field collections.'
restrict access: true
field_collection.overview_field_collections:
path: '/admin/structure/field_collections'
defaults:
_title: 'Manage field collections'
_controller: '\Drupal\Core\Entity\Controller\EntityListController::listing'
entity_type: 'field_collection'
requirements:
_permission: 'administer field_collection_item fields'
entity.field_collection.edit_form:
path: '/admin/structure/field_collections/manage/{field_collection}'
defaults:
_entity_form: 'field_collection.edit'
requirements:
_permission: 'administer field_collection_item fields'
entity.field_collection_item.canonical:
path: '/field_collection_item/{field_collection_item}'
defaults:
_controller: '\Drupal\field_collection\Controller\FieldCollectionItemController::page'
_title_callback: '\Drupal\field_collection\Controller\FieldCollectionItemController::pageTitle'
requirements:
_access_field_collection_item_host: 'view'
field_collection_item.revision_show:
path: '/field_collection_item/{field_collection_item}/revisions/{field_collection_item_revision}/view'
defaults:
_controller: '\Drupal\field_collection\Controller\FieldCollectionItemController::revisionShow'
_title_callback: '\Drupal\field_collection\Controller\FieldCollectionItemController::revisionPageTitle'
requirements:
_access_field_collection_item_host_revisions: 'view'
field_collection_item.add_page:
path: '/field_collection_item/add/{field_collection}/{host_type}/{host_id}'
defaults:
_controller: '\Drupal\field_collection\Controller\FieldCollectionItemController::add'
_title_callback: '\Drupal\field_collection\Controller\FieldCollectionItemController::addPageTitle'
requirements:
_access_add_field_collection_item_to_host: '{host_type}:{host_id}'
entity.field_collection_item.edit_form:
path: '/field_collection_item/{field_collection_item}/edit'
defaults:
_entity_form: 'field_collection_item.edit'
requirements:
_access_field_collection_item_host: 'update'
entity.field_collection_item.delete_form:
path: '/field_collection_item/{field_collection_item}/delete'
defaults:
_entity_form: 'field_collection_item.delete'
requirements:
_access_field_collection_item_host: 'update'
<?php
/**
* @file
* Provide views data for Field Collection.
*/
use Drupal\field\FieldStorageConfigInterface;
/**
* Implements hook_field_views_data().
*
* The function implements the hook in behalf of 'core' because it adds a
* relationship and a reverse relationship to entity_reference field type, which
* is provided by core.
*/
function field_collection_field_views_data(FieldStorageConfigInterface $field_storage) {
$data = views_field_default_views_data($field_storage);
// The code below only deals with the Entity reference field type.
if ($field_storage->getType() != 'field_collection') {
return $data;
}
$entity_manager = \Drupal::entityManager();
$entity_type_id = $field_storage->getTargetEntityTypeId();
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping();
foreach ($data as $table_name => $table_data) {
// Add a relationship to the target entity type.
$target_entity_type_id = $field_storage->getSetting('target_type');
$target_entity_type = $entity_manager->getDefinition($target_entity_type_id);
$entity_type_id = $field_storage->getTargetEntityTypeId();
$entity_type = $entity_manager->getDefinition($entity_type_id);
$target_base_table = $target_entity_type->getDataTable() ?: $target_entity_type->getBaseTable();
$field_name = $field_storage->getName();
// Provide a relationship for the entity type with the entity reference
// field.
$args = array(
'@label' => $target_entity_type->getLabel(),
'@field_name' => $field_name,
);
$data[$table_name][$field_name]['relationship'] = array(
'title' => t('@label referenced from @field_name', $args),
'label' => t('@field_name: @label', $args),
'group' => $entity_type->getLabel(),
'help' => t('Appears in: @bundles.', array('@bundles' => implode(', ', $field_storage->getBundles()))),
'id' => 'standard',
'base' => $target_base_table,
'entity type' => $target_entity_type_id,
'base field' => $target_entity_type->getKey('id'),
'relationship field' => $field_name . '_target_id',
);
// Provide a reverse relationship for the entity type that is referenced by
// the field.
$args['@entity'] = $entity_type->getLabel();
$args['@label'] = $target_entity_type->getLowercaseLabel();
$pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $field_name;
$data[$target_base_table][$pseudo_field_name]['relationship'] = array(
'title' => t('@entity using @field_name', $args),
'label' => t('@field_name', array('@field_name' => $field_name)),
'group' => $target_entity_type->getLabel(),
'help' => t('Relate each @entity with a @field_name set to the @label.', $args),
'id' => 'entity_reverse',
'base' => $entity_type->getDataTable() ?: $entity_type->getBaseTable(),
'entity_type' => $entity_type_id,
'base field' => $entity_type->getKey('id'),
'field_name' => $field_name,
'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
'field field' => $field_name . '_target_id',
'join_extra' => array(
array(
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
),
),
);
}
return $data;
}
<?php
/**
* @file
* Contains \Drupal\field_collection\Controller\FieldCollectionItemController.
*/
namespace Drupal\field_collection\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\field_collection\Entity\FieldCollection;
use Drupal\field_collection\Entity\FieldCollectionItem;
use Drupal\Core\Entity\Controller\EntityViewController;
/**
* Returns responses for Field collection item routes.
*/
class FieldCollectionItemController extends ControllerBase {
/**
* Provides the field collection item submission form.
*
* @param \Drupal\field_collection\Entity\FieldCollection $field_collection
* The field_collection entity for the field collection item.
*
* @param $host_type
* The type of the entity hosting the field collection item.
*
* @param $host_id
* The id of the entity hosting the field collection item.
*
* @return array
* A field collection item submission form.
*
* TODO: additional fields
*/
public function add(FieldCollection $field_collection, $host_type, $host_id) {
$host = $this->entityTypeManager()->getStorage($host_type)->load($host_id);
if (_field_collection_field_item_list_full($host->{$field_collection->id()})) {
drupal_set_message(t('This field is already full.'), 'error');
return array('#markup' => 'Can not add to an already full field.');
}
else {
$field_collection_item = $this->entityTypeManager()
->getStorage('field_collection_item')
->create(array(
'field_name' => $field_collection->id(),
'host_type' => $host_type,
'revision_id' => 0,
));
$form = $this->entityFormBuilder()->getForm($field_collection_item);
return $form;
}
}
/**
* Displays a field collection item.
*
* @param \Drupal\field_collection\Entity\FieldCollectionItem $field_collection_item
* The field collection item we are displaying.
*
* @return array
* An array suitable for drupal_render().
*/
public function page(FieldCollectionItem $field_collection_item) {
$build = $this->buildPage($field_collection_item);
return $build;
}
/**
* Builds a field collection item page render array.
*
* @param \Drupal\field_collection\Entity\FieldCollectionItem $field_collection_item
* The field collection item we are displaying.
*
* @return array
* An array suitable for drupal_render().
*/
protected function buildPage(FieldCollectionItem $field_collection_item) {
$ret = array('field_collection_items' => $this->entityTypeManager()
->getViewBuilder('field_collection_item')
->view($field_collection_item));
return $ret;
}
/**
* The _title_callback for the field_collection_item.view route.
*
* @param FieldCollectionItem $field_collection_item
* The current field_collection_item.
*
* @return string
* The page title.
*/
public function pageTitle(FieldCollectionItem $field_collection_item) {
return \Drupal::service('entity.repository')->getTranslationFromContext($field_collection_item)->label();
}
/**
* The _title_callback for the field_collection_item.add route.
*
* @param \Drupal\field_collection\Entity\FieldCollection $field_collection
* The current field collection.
*
* @return string
* The page title.
*/
public function addPageTitle(FieldCollection $field_collection) {
return $this->t('Create @label', array('@label' => $field_collection->label()));
}
/**
* Displays a field collection item revision.
*
* @param int $field_collection_item_revision
* The field collection item revision ID.
*
* @return array
* An array suitable for drupal_render().
*/
public function revisionShow($field_collection_item_revision) {
$field_collection_item = $this->entityTypeManager()
->getStorage('field_collection_item')
->loadRevision($field_collection_item_revision);
$field_collection_item_view_controller = new EntityViewController($this->entityManager(), \Drupal::service('renderer'));
$page = $field_collection_item_view_controller
->view($field_collection_item);
unset($page['field_collection_item'][$field_collection_item->id()]['#cache']);
return $page;
}
/**
* Page title callback for a field collection item revision.
*
* @param int $field_collection_item_revision
* The field collection item revision ID.
*
* @return string
* The page title.
*/
public function revisionPageTitle($field_collection_item_revision) {
$field_collection_item = $this->entityTypeManager()
->getStorage('field_collection_item')
->loadRevision($field_collection_item_revision);
return $this->t('Revision %revision of %title', array('%revision' => $field_collection_item_revision, '%title' => $field_collection_item->label()));
}
}
<?php
/**
* @file
* Contains \Drupal\field_collection\Entity\FieldCollection.
*/
namespace Drupal\field_collection\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
use Drupal\Core\Annotation\Translation;
use Drupal\field_collection\FieldCollectionInterface;
/**
* Defines the Field collection configuration entity.
*
* @ConfigEntityType(
* id = "field_collection",
* label = @Translation("Field collection"),
* handlers = {
* "storage" = "Drupal\Core\Config\Entity\ConfigEntityStorage",
* "access" = "Drupal\field_collection\FieldCollectionAccessControlHandler",
* "form" = {
* "add" = "Drupal\field_collection\FieldCollectionForm",
* "edit" = "Drupal\field_collection\FieldCollectionForm",
* "delete" = "Drupal\field_collection\Form\FieldCollectionDeleteConfirm"
* },
* "list_builder" = "Drupal\field_collection\FieldCollectionListBuilder",
* },
* admin_permission = "administer content types",
* config_prefix = "field_collection",
* bundle_of = "field_collection_item",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid",
* },
* links = {
* "edit-form" = "/admin/structure/field_collections/manage/{field_collection}"
* }
* )
*/
class FieldCollection extends ConfigEntityBundleBase implements FieldCollectionInterface {
/**
* The machine name of this field collection.
*
* @var string
*/
protected $id;
/**
* The UUID of the node type.
*
* @var string
*/
protected $uuid;
/**
* The human-readable name of the field collection.
*
* @var string
*/
protected $label;
/**
* TODO: Figure out if this is really needed (it may not be defined by entity classes).
*/
protected $entityType;
public function __construct(array $values = array(), $entity_type = 'field_collection') {
parent::__construct($values, $entity_type);
$this->entityType = 'field_collection';
}
}