View source
<?php
namespace Drupal\state_machine\Plugin\Field\FieldType;
use Drupal\Core\Entity\ContentEntityStorageInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\OptionsProviderInterface;
use Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint;
use Drupal\state_machine\Event\WorkflowTransitionEvent;
use Drupal\state_machine\Plugin\Workflow\WorkflowState;
use Drupal\state_machine\Plugin\Workflow\WorkflowTransition;
class StateItem extends FieldItemBase implements StateItemInterface, OptionsProviderInterface {
protected $originalValue;
protected $transitionToApply;
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
'value' => [
'type' => 'varchar_ascii',
'length' => 255,
],
],
];
}
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('State'))
->setRequired(TRUE);
return $properties;
}
public function getConstraints() {
$constraints = parent::getConstraints();
foreach ($constraints as $key => $constraint) {
if ($constraint instanceof AllowedValuesConstraint) {
unset($constraints[$key]);
}
}
$manager = \Drupal::typedDataManager()
->getValidationConstraintManager();
$constraints[] = $manager
->create('State', []);
return $constraints;
}
public static function defaultFieldSettings() {
return [
'workflow' => '',
'workflow_callback' => '',
] + parent::defaultFieldSettings();
}
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = [];
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;
}
public function isEmpty() {
return $this->value === NULL || $this->value === '';
}
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;
}
public function setValue($values, $notify = TRUE) {
if (empty($this->originalValue)) {
if (isset($values) && !is_array($values)) {
$values = [
'value' => $values,
];
}
$this->originalValue = $values['value'];
}
parent::setValue($values, $notify);
}
public function isValid() {
$allowed_states = $this
->getAllowedStates($this->originalValue);
return isset($allowed_states[$this->value]);
}
public function getPossibleValues(AccountInterface $account = NULL) {
return array_keys($this
->getPossibleOptions($account));
}
public function getPossibleOptions(AccountInterface $account = NULL) {
$workflow = $this
->getWorkflow();
if (!$workflow) {
return [];
}
$state_labels = array_map(function (WorkflowState $state) {
return $state
->getLabel();
}, $workflow
->getStates());
return $state_labels;
}
public function getSettableValues(AccountInterface $account = NULL) {
return array_keys($this
->getSettableOptions($account));
}
public function getSettableOptions(AccountInterface $account = NULL) {
$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;
}
protected function getAllowedStates($value) {
$workflow = $this
->getWorkflow();
if (!$workflow) {
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;
}
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);
}
public function getOriginalId() {
return $this->originalValue;
}
public function getId() {
return $this->value;
}
public function getLabel() {
return $this
->getStateLabel($this->value);
}
public function getOriginalLabel() {
return $this
->getStateLabel($this->originalValue);
}
protected function getStateLabel($state_id) {
$label = $state_id;
if ($workflow = $this
->getWorkflow()) {
$state = $workflow
->getState($state_id);
if ($state) {
$label = $state
->getLabel();
}
}
return $label;
}
public function getTransitions() {
$transitions = [];
if ($workflow = $this
->getWorkflow()) {
$transitions = $workflow
->getAllowedTransitions($this->value, $this
->getEntity());
}
return $transitions;
}
public function isTransitionAllowed($transition_id) {
$allowed_transitions = $this
->getTransitions();
return isset($allowed_transitions[$transition_id]);
}
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()));
}
$this->transitionToApply = $transition;
$this
->setValue([
'value' => $transition
->getToState()
->getId(),
]);
}
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);
}
public function preSave() {
if ($this->value != $this->originalValue) {
$this
->dispatchTransitionEvent('pre_transition');
}
}
public function postSave($update) {
if ($this->value != $this->originalValue) {
$this
->dispatchTransitionEvent('post_transition');
}
$this->originalValue = $this->value;
$this->transitionToApply = NULL;
}
protected function dispatchTransitionEvent($phase) {
$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 = [
$group_id . '.' . $transition_id . '.' . $phase,
$group_id . '.' . $phase,
'state_machine.' . $phase,
];
foreach ($events as $event_id) {
$event_dispatcher
->dispatch($event_id, $event);
}
}
}
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
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 = [];
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');
}
if (empty($workflow_id)) {
return [];
}
$workflow_manager = \Drupal::service('plugin.manager.workflow');
$workflow = $workflow_manager
->createInstance($workflow_id);
$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;
}
}