class StateItem in State Machine 8
Plugin implementation of the 'state' field type.
Plugin annotation
@FieldType(
id = "state",
label = @Translation("State"),
description = @Translation("Stores the current workflow state."),
default_widget = "options_select",
default_formatter = "list_default"
)
Hierarchy
- class \Drupal\Core\TypedData\TypedData implements PluginInspectionInterface, TypedDataInterface uses DependencySerializationTrait, StringTranslationTrait, TypedDataTrait
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\state_machine\Plugin\Field\FieldType\StateItem implements OptionsProviderInterface, StateItemInterface
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
Expanded class hierarchy of StateItem
File
- src/
Plugin/ Field/ FieldType/ StateItem.php, line 29
Namespace
Drupal\state_machine\Plugin\Field\FieldTypeView source
class StateItem extends FieldItemBase implements StateItemInterface, OptionsProviderInterface {
/**
* The original value, used to validate state changes.
*
* @var string
*/
protected $originalValue;
/**
* The transition to apply.
*
* @var \Drupal\state_machine\Plugin\Workflow\WorkflowTransition
*/
protected $transitionToApply;
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
'value' => [
'type' => 'varchar_ascii',
'length' => 255,
],
],
];
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('State'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = parent::getConstraints();
// Replace the 'AllowedValuesConstraint' constraint with the 'State' one.
foreach ($constraints as $key => $constraint) {
if ($constraint instanceof AllowedValuesConstraint) {
unset($constraints[$key]);
}
}
$manager = \Drupal::typedDataManager()
->getValidationConstraintManager();
$constraints[] = $manager
->create('State', []);
return $constraints;
}
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return [
'workflow' => '',
'workflow_callback' => '',
] + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = [];
// Allow the workflow to be changed if it's not determined by a callback.
if (!$this
->getSetting('workflow_callback')) {
$workflow_manager = \Drupal::service('plugin.manager.workflow');
$workflows = $workflow_manager
->getGroupedLabels($this
->getEntity()
->getEntityTypeId());
$element['workflow'] = [
'#type' => 'select',
'#title' => $this
->t('Workflow'),
'#options' => $workflows,
'#default_value' => $this
->getSetting('workflow'),
'#required' => TRUE,
];
}
return $element;
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
// Note that in this field's case the value will never be empty
// because of the default returned in applyDefaultValue().
return $this->value === NULL || $this->value === '';
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
if ($workflow = $this
->getWorkflow()) {
$states = $workflow
->getStates();
$initial_state = reset($states);
$this
->setValue([
'value' => $initial_state
->getId(),
], $notify);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
if (empty($this->originalValue)) {
// If no array is given, then the method received just the state value.
if (isset($values) && !is_array($values)) {
$values = [
'value' => $values,
];
}
// Track the original field value to allow isValid() to validate changes
// and to react to transitions.
$this->originalValue = $values['value'];
}
parent::setValue($values, $notify);
}
/**
* {@inheritdoc}
*/
public function isValid() {
$allowed_states = $this
->getAllowedStates($this->originalValue);
return isset($allowed_states[$this->value]);
}
/**
* {@inheritdoc}
*/
public function getPossibleValues(AccountInterface $account = NULL) {
return array_keys($this
->getPossibleOptions($account));
}
/**
* {@inheritdoc}
*/
public function getPossibleOptions(AccountInterface $account = NULL) {
$workflow = $this
->getWorkflow();
if (!$workflow) {
// The workflow is not known yet, the field is probably being created.
return [];
}
$state_labels = array_map(function (WorkflowState $state) {
return $state
->getLabel();
}, $workflow
->getStates());
return $state_labels;
}
/**
* {@inheritdoc}
*/
public function getSettableValues(AccountInterface $account = NULL) {
return array_keys($this
->getSettableOptions($account));
}
/**
* {@inheritdoc}
*/
public function getSettableOptions(AccountInterface $account = NULL) {
// $this->value is unpopulated due to https://www.drupal.org/node/2629932
$field_name = $this
->getFieldDefinition()
->getName();
$value = $this
->getEntity()
->get($field_name)->value;
$allowed_states = $this
->getAllowedStates($value);
$state_labels = array_map(function (WorkflowState $state) {
return $state
->getLabel();
}, $allowed_states);
return $state_labels;
}
/**
* Gets the next allowed states for the given field value.
*
* @param string $value
* The field value, representing the state ID.
*
* @return \Drupal\state_machine\Plugin\Workflow\WorkflowState[]
* The allowed states.
*/
protected function getAllowedStates($value) {
$workflow = $this
->getWorkflow();
if (!$workflow) {
// The workflow is not known yet, the field is probably being created.
return [];
}
$allowed_states = [];
if (!empty($value) && ($current_state = $workflow
->getState($value))) {
$allowed_states[$value] = $current_state;
}
$transitions = $workflow
->getAllowedTransitions($value, $this
->getEntity());
foreach ($transitions as $transition) {
$state = $transition
->getToState();
$allowed_states[$state
->getId()] = $state;
}
return $allowed_states;
}
/**
* {@inheritdoc}
*/
public function getWorkflow() {
if ($callback = $this
->getSetting('workflow_callback')) {
$workflow_id = call_user_func($callback, $this
->getEntity());
}
else {
$workflow_id = $this
->getSetting('workflow');
}
if (empty($workflow_id)) {
return FALSE;
}
$workflow_manager = \Drupal::service('plugin.manager.workflow');
return $workflow_manager
->createInstance($workflow_id);
}
/**
* {@inheritdoc}
*/
public function getOriginalId() {
return $this->originalValue;
}
/**
* {@inheritdoc}
*/
public function getId() {
return $this->value;
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this
->getStateLabel($this->value);
}
/**
* {@inheritdoc}
*/
public function getOriginalLabel() {
return $this
->getStateLabel($this->originalValue);
}
/**
* Gets the state label for the given state ID.
*
* @param string $state_id
* The state ID.
*
* @return string
* The state label.
*/
protected function getStateLabel($state_id) {
$label = $state_id;
if ($workflow = $this
->getWorkflow()) {
$state = $workflow
->getState($state_id);
if ($state) {
$label = $state
->getLabel();
}
}
return $label;
}
/**
* {@inheritdoc}
*/
public function getTransitions() {
$transitions = [];
if ($workflow = $this
->getWorkflow()) {
$transitions = $workflow
->getAllowedTransitions($this->value, $this
->getEntity());
}
return $transitions;
}
/**
* {@inheritdoc}
*/
public function isTransitionAllowed($transition_id) {
$allowed_transitions = $this
->getTransitions();
return isset($allowed_transitions[$transition_id]);
}
/**
* {@inheritdoc}
*/
public function applyTransition(WorkflowTransition $transition) {
if (!$this
->isTransitionAllowed($transition
->getId())) {
throw new \InvalidArgumentException(sprintf('The transition "%s" is currently not allowed. (Current state: "%s".)', $transition
->getId(), $this
->getId()));
}
// Store the transition to apply, to ensure we're applying the requested
// transition instead of guessing based on the original state.
$this->transitionToApply = $transition;
$this
->setValue([
'value' => $transition
->getToState()
->getId(),
]);
}
/**
* {@inheritdoc}
*/
public function applyTransitionById($transition_id) {
$transition = NULL;
if ($workflow = $this
->getWorkflow()) {
$transition = $workflow
->getTransition($transition_id);
}
if (!$transition) {
throw new \InvalidArgumentException(sprintf('Unknown transition ID "%s".', $transition_id));
}
$this
->applyTransition($transition);
}
/**
* {@inheritdoc}
*/
public function preSave() {
if ($this->value != $this->originalValue) {
$this
->dispatchTransitionEvent('pre_transition');
}
}
/**
* {@inheritdoc}
*/
public function postSave($update) {
if ($this->value != $this->originalValue) {
$this
->dispatchTransitionEvent('post_transition');
}
$this->originalValue = $this->value;
// Nullify the transition to apply, to ensure the next entity save
// doesn't trigger the same transition by mistake.
$this->transitionToApply = NULL;
}
/**
* Dispatches a transition event for the given phase.
*
* @param string $phase
* The phase: pre_transition OR post_transition.
*/
protected function dispatchTransitionEvent($phase) {
/** @var \Drupal\state_machine\Plugin\Workflow\WorkflowInterface $workflow */
$workflow = $this
->getWorkflow();
$transition = $this->transitionToApply ?? $workflow
->findTransition($this->originalValue, $this->value);
if ($transition) {
$field_name = $this
->getFieldDefinition()
->getName();
$group_id = $workflow
->getGroup();
$transition_id = $transition
->getId();
$event_dispatcher = \Drupal::getContainer()
->get('event_dispatcher');
$event = new WorkflowTransitionEvent($transition, $workflow, $this
->getEntity(), $field_name);
$events = [
// For example: 'commerce_order.place.pre_transition'.
$group_id . '.' . $transition_id . '.' . $phase,
// For example: 'commerce_order.pre_transition'.
$group_id . '.' . $phase,
// For example: 'state_machine.pre_transition'.
'state_machine.' . $phase,
];
foreach ($events as $event_id) {
$event_dispatcher
->dispatch($event_id, $event);
}
}
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
// Attempt to determine the right workflow to use.
if ($callback = $field_definition
->getSetting('workflow_callback')) {
$entity_type_id = $field_definition
->getTargetEntityTypeId();
$entity_storage = \Drupal::entityTypeManager()
->getStorage($entity_type_id);
if (!$entity_storage instanceof ContentEntityStorageInterface) {
return [];
}
$values = [];
// Attempt to create a sample entity with at least the bundle set.
if ($bundle_key = $entity_storage
->getEntityType()
->getKey('bundle')) {
if ($field_definition
->getTargetBundle()) {
$bundle = $field_definition
->getTargetBundle();
}
else {
$bundle_ids = \Drupal::service('entity_type.bundle.info')
->getBundleInfo($entity_type_id);
$bundle = array_rand($bundle_ids);
}
$values[$bundle_key] = $bundle;
}
$entity = $entity_storage
->create($values);
$workflow_id = call_user_func($callback, $entity);
}
else {
$workflow_id = $field_definition
->getSetting('workflow');
}
// The workflow could not be determined, cannot generate a sample value.
if (empty($workflow_id)) {
return [];
}
/** @var \Drupal\state_machine\WorkflowManagerInterface $workflow_manager */
$workflow_manager = \Drupal::service('plugin.manager.workflow');
/** @var \Drupal\state_machine\Plugin\Workflow\WorkflowInterface $workflow */
$workflow = $workflow_manager
->createInstance($workflow_id);
// Select states that allow at least one transition.
$candidate_states = $states = $workflow
->getStates();
foreach ($candidate_states as $key => $candidate) {
if (empty($workflow
->getPossibleTransitions($candidate
->getId()))) {
unset($states[$key]);
}
}
$random_state = array_rand($states);
$values = [
'value' => $states[$random_state]
->getId(),
];
return $values;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
FieldItemBase:: |
public static | function |
Calculates dependencies for field items. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public static | function |
Calculates dependencies for field items on the storage level. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public static | function |
Defines the storage-level settings for this plugin. Overrides FieldItemInterface:: |
10 |
FieldItemBase:: |
public | function |
Defines custom delete behavior for field values. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Defines custom revision delete behavior for field values. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Gets the entity that field belongs to. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the field definition. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the langcode of the field values held in the object. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function | Returns the value of a field setting. | |
FieldItemBase:: |
protected | function | Returns the array of field settings. | |
FieldItemBase:: |
public static | function |
Returns the name of the main property, if any. Overrides FieldItemInterface:: |
8 |
FieldItemBase:: |
public static | function |
Informs the plugin that a dependency of the field will be deleted. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Returns a form for the storage-level settings. Overrides FieldItemInterface:: |
8 |
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Returns a renderable array for a single field item. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function |
Different to the parent Map class, we avoid creating property objects as
far as possible in order to optimize performance. Thus we just update
$this->values if no property object has been created yet. Overrides Map:: |
|
FieldItemBase:: |
public | function |
Constructs a TypedData object given its definition and context. Overrides TypedData:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Gets a property value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Magic method: Determines whether a property is set. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Magic method: Sets a property value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Unsets a property. Overrides FieldItemInterface:: |
|
Map:: |
protected | property |
The data definition. Overrides TypedData:: |
|
Map:: |
protected | property | The array of properties. | |
Map:: |
protected | property | An array of values for the contained properties. | |
Map:: |
public | function |
Gets a property object. Overrides ComplexDataInterface:: |
|
Map:: |
public | function | ||
Map:: |
public | function |
Gets an array of property objects. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns a string representation of the data. Overrides TypedData:: |
|
Map:: |
public | function |
Gets the data value. Overrides TypedData:: |
1 |
Map:: |
public | function |
Overrides TraversableTypedDataInterface:: |
4 |
Map:: |
public | function |
Sets a property value. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns an array of all property values. Overrides ComplexDataInterface:: |
1 |
Map:: |
public | function | Magic method: Implements a deep clone. | |
StateItem:: |
protected | property | The original value, used to validate state changes. | |
StateItem:: |
protected | property | The transition to apply. | |
StateItem:: |
public | function |
Applies the default value. Overrides Map:: |
|
StateItem:: |
public | function |
Applies the given transition, changing the current state. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Applies a transition with the given ID, changing the current state. Overrides StateItemInterface:: |
|
StateItem:: |
public static | function |
Defines the field-level settings for this plugin. Overrides FieldItemBase:: |
|
StateItem:: |
protected | function | Dispatches a transition event for the given phase. | |
StateItem:: |
public | function |
Returns a form for the field-level settings. Overrides FieldItemBase:: |
|
StateItem:: |
public static | function |
Generates placeholder field values. Overrides FieldItemBase:: |
|
StateItem:: |
protected | function | Gets the next allowed states for the given field value. | |
StateItem:: |
public | function |
Gets a list of validation constraints. Overrides TypedData:: |
|
StateItem:: |
public | function |
Gets the current state ID. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Gets the label of the current state. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Gets the original state ID. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Gets the label of the original state. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Returns an array of possible values with labels for display. Overrides OptionsProviderInterface:: |
|
StateItem:: |
public | function |
Returns an array of possible values. Overrides OptionsProviderInterface:: |
|
StateItem:: |
public | function |
Returns an array of settable values with labels for display. Overrides OptionsProviderInterface:: |
|
StateItem:: |
public | function |
Returns an array of settable values. Overrides OptionsProviderInterface:: |
|
StateItem:: |
protected | function | Gets the state label for the given state ID. | |
StateItem:: |
public | function |
Gets the allowed transitions for the current state. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Gets the workflow used by the field. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Determines whether the data structure is empty. Overrides Map:: |
|
StateItem:: |
public | function |
Gets whether the given transition is allowed. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Gets whether the current state is valid. Overrides StateItemInterface:: |
|
StateItem:: |
public | function |
Defines custom post-save behavior for field values. Overrides FieldItemBase:: |
|
StateItem:: |
public | function |
Defines custom presave behavior for field values. Overrides FieldItemBase:: |
|
StateItem:: |
public static | function |
Defines field item properties. Overrides FieldItemInterface:: |
|
StateItem:: |
public static | function |
Returns the schema for the field. Overrides FieldItemInterface:: |
|
StateItem:: |
public | function |
Sets the data value. Overrides FieldItemBase:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
TypedData:: |
protected | property | The property name. | |
TypedData:: |
protected | property | The parent typed data object. | |
TypedData:: |
public static | function |
Constructs a TypedData object given its definition and context. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets the data definition. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the name of a property or item. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the parent data structure; i.e. either complex data or a list. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Returns the property path of the data. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the root of the typed data tree. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Sets the context of a property or item via a context aware parent. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Validates the currently set data value. Overrides TypedDataInterface:: |
|
TypedDataTrait:: |
protected | property | The typed data manager used for creating the data types. | |
TypedDataTrait:: |
public | function | Gets the typed data manager. | 2 |
TypedDataTrait:: |
public | function | Sets the typed data manager. | 2 |