You are here

class FivestarItem in Fivestar 8

Plugin implementation of the 'fivestar' field type.

Plugin annotation


@FieldType(
  id = "fivestar",
  label = @Translation("Fivestar rating"),
  description = @Translation("Store a rating for this piece of content."),
  default_widget = "fivestar_stars",
  default_formatter = "fivestar_stars",
)

Hierarchy

Expanded class hierarchy of FivestarItem

File

src/Plugin/Field/FieldType/FivestarItem.php, line 24

Namespace

Drupal\fivestar\Plugin\Field\FieldType
View source
class FivestarItem extends FieldItemBase {

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    return [
      'columns' => [
        'rating' => [
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => FALSE,
          'sortable' => TRUE,
        ],
        'target' => [
          'type' => 'int',
          'unsigned' => TRUE,
          'not null' => FALSE,
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $property_definitions['rating'] = DataDefinition::create('integer')
      ->setLabel(t('Rating'));
    $property_definitions['target'] = DataDefinition::create('integer')
      ->setLabel(t('Target'));
    return $property_definitions;
  }

  /**
   * {@inheritdoc}
   */
  public static function mainPropertyName() {
    return 'rating';
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultFieldSettings() {
    return [
      'stars' => 5,
      'allow_clear' => FALSE,
      'allow_revote' => TRUE,
      'allow_ownvote' => TRUE,
      'rated_while' => 'viewing',
      'enable_voting_target' => FALSE,
      'target_bridge_field' => '',
      'target_fivestar_field' => '',
    ] + parent::defaultFieldSettings();
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultStorageSettings() {
    return [
      'vote_type' => 'vote',
    ] + parent::defaultStorageSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
    $element = [];
    $vote_manager = \Drupal::service('fivestar.vote_manager');
    $vote_types_link = Link::createFromRoute($this
      ->t('here'), 'entity.vote_type.collection')
      ->toString();
    $element['vote_type'] = [
      '#type' => 'select',
      '#required' => TRUE,
      '#title' => $this
        ->t('Vote type'),
      '#options' => $vote_manager
        ->getVoteTypes(),
      '#description' => $this
        ->t('The vote type this rating will affect. Enter a property on which that this rating will affect, such as <em>quality</em>, <em>satisfaction</em>, <em>overall</em>, etc. You can add new vote type %vote_types_link.', [
        '%vote_types_link' => $vote_types_link,
      ]),
      '#default_value' => $this
        ->getSetting('vote_type'),
      '#disabled' => $has_data,
    ];
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
    $element = [];
    $element['stars'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Number of stars'),
      '#options' => array_combine(range(1, 10), range(1, 10)),
      '#default_value' => $this
        ->getSetting('stars'),
    ];
    $element['allow_clear'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Allow users to cancel their ratings.'),
      '#default_value' => $this
        ->getSetting('allow_clear'),
      '#return_value' => 1,
    ];
    $element['allow_revote'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Allow users to re-vote on already voted content.'),
      '#default_value' => $this
        ->getSetting('allow_revote'),
      '#return_value' => 1,
    ];
    $element['allow_ownvote'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Allow users to vote on their own content.'),
      '#default_value' => $this
        ->getSetting('allow_ownvote'),
      '#return_value' => 1,
    ];
    $element['rated_while'] = [
      '#type' => 'radios',
      '#default_value' => $this
        ->getSetting('rated_while'),
      '#title' => $this
        ->t('Select when user can rate the field'),
      '#options' => [
        'viewing' => 'Rated while viewing',
        'editing' => 'Rated while editing',
      ],
    ];
    $element['enable_voting_target'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Set voting target'),
      '#default_value' => $this
        ->getSetting('enable_voting_target'),
    ];
    $states = [
      'visible' => [
        ':input[name="settings[enable_voting_target]"]' => [
          'checked' => TRUE,
        ],
      ],
      'required' => [
        ':input[name="settings[enable_voting_target]"]' => [
          'checked' => TRUE,
        ],
      ],
    ];
    $element['target_bridge_field'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Target bridge field'),
      '#description' => $this
        ->t('Machine name of field that binds current entity with entity that contain target fivestar field.
        The field should have "entity_reference" type.'),
      '#states' => $states,
      '#default_value' => $this
        ->getSetting('target_bridge_field'),
    ];
    $element['target_fivestar_field'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Target fivestar field'),
      '#description' => $this
        ->t('Machine name of fivestar field which should affect after vote.'),
      '#states' => $states,
      '#default_value' => $this
        ->getSetting('target_fivestar_field'),
    ];
    $element['#element_validate'] = [
      [
        get_class($this),
        'fieldSettingsFormValidate',
      ],
    ];

    // @todo try to find the way to omit it.
    $form_state
      ->set('host_entity', $this
      ->getEntity());
    return $element;
  }

  /**
   * Validate callback: check field settings.
   */
  public static function fieldSettingsFormValidate(array $form, FormStateInterface $form_state) {
    $host_entity = $form_state
      ->get('host_entity');
    $field_settings = $form_state
      ->getValue('settings');

    // Validate voting target settings.
    if ($field_settings['enable_voting_target'] == 1) {

      // Check if bridge field exist.
      if (!$host_entity
        ->hasField($field_settings['target_bridge_field'])) {
        $form_state
          ->setErrorByName('target_bridge_field', t('The host entity doesn\'t contain field: "@field_name"', [
          '@field_name' => $field_settings['target_bridge_field'],
        ]));
        return;
      }

      // Check if bridge field has correct type.
      $field_type = $host_entity
        ->get($field_settings['target_bridge_field'])
        ->getFieldDefinition()
        ->getType();
      if ($field_type != 'entity_reference') {
        $form_state
          ->setErrorByName('target_bridge_field', t('The bridge field must have "entity_reference" type. The entered field has type: "@field_type"', [
          '@field_type' => $field_type,
        ]));
        return;
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function isEmpty() {
    $rating = $this
      ->get('rating')
      ->getValue();
    return empty($rating) || $rating == '-';
  }

  /**
   * {@inheritdoc}
   */
  public function postSave($update) {
    $entity = $this
      ->getEntity();
    $field_definition = $this
      ->getFieldDefinition();
    $field_settings = $field_definition
      ->getSettings();
    $vote_manager = \Drupal::service('fivestar.vote_manager');
    $target_entity = $this
      ->getTargetEntity($entity, $field_settings);
    $vote_rating = $entity
      ->get($field_definition
      ->getName())->rating ?: 0;
    $owner = $this
      ->getVoteOwner($entity, $field_settings['rated_while']);
    if ($update) {

      // Delete previous votes.
      $criteria = [
        'entity_id' => $entity
          ->id(),
        'entity_type' => $entity
          ->getEntityTypeId(),
        'type' => $field_settings['vote_type'],
        'user_id' => $owner
          ->id(),
      ];
      if ($owner
        ->isAnonymous()) {
        $ip_address = \Drupal::request()
          ->getClientIp();
        $criteria['vote_source'] = hash('sha256', serialize($ip_address));
      }
      foreach ($vote_manager
        ->getVotesByCriteria($criteria) as $vote) {
        $vote
          ->delete();
      }

      // Delete votes from target entity.
      if (!empty($target_entity)) {
        $criteria['entity_id'] = $target_entity
          ->id();
        $criteria['entity_type'] = $target_entity
          ->getEntityTypeId();
        foreach ($vote_manager
          ->getVotesByCriteria($criteria) as $target_vote) {
          $target_vote
            ->delete();
        }
      }
    }

    // Add new vote.
    $vote_manager
      ->addVote($entity, $vote_rating, $field_settings['vote_type'], $owner
      ->id());
    if (!empty($target_entity) && $entity
      ->isPublished()) {
      $vote_manager
        ->addVote($target_entity, $vote_rating, $field_settings['vote_type'], $owner
        ->id());
    }

    // No changes made to the Fivestar field item in this method.
    return FALSE;
  }

  /**
   * Get target entity.
   *
   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
   * @param array $field_settings
   *
   * @return \Drupal\Core\Entity\FieldableEntityInterface|null
   */
  public function getTargetEntity(FieldableEntityInterface $entity, array $field_settings) {
    if ($field_settings['enable_voting_target'] !== TRUE) {
      return NULL;
    }
    if (!$entity
      ->hasField($field_settings['target_bridge_field'])) {
      return NULL;
    }
    $bridge_entity = $entity->{$field_settings['target_bridge_field']}->entity;
    if ($bridge_entity && $bridge_entity
      ->hasField($field_settings['target_fivestar_field'])) {
      return $bridge_entity;
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function delete() {
    $del_entity = $this
      ->getEntity();
    $field_settings = $this
      ->getFieldDefinition()
      ->getSettings();
    $target_entity = $this
      ->getTargetEntity($del_entity, $field_settings);
    if (!$target_entity) {
      return;
    }
    $vote_storage = \Drupal::entityTypeManager()
      ->getStorage('vote');
    $votes = $vote_storage
      ->loadByProperties([
      'entity_type' => $del_entity
        ->getEntityTypeId(),
      'entity_id' => $del_entity
        ->id(),
    ]);
    foreach ($votes as $vote) {

      // Get target vote.
      $target_votes = $vote_storage
        ->loadByProperties([
        'entity_type' => $target_entity
          ->getEntityTypeId(),
        'entity_id' => $target_entity
          ->id(),
        'type' => $vote
          ->bundle(),
        'user_id' => $vote
          ->getOwnerId(),
        'value' => $vote
          ->getValue(),
        'vote_source' => $vote
          ->getSource(),
      ]);
      foreach ($target_votes as $target_vote) {
        $target_vote
          ->delete();
      }
    }
  }

  /**
   * Get owner for vote.
   *
   * In order to get correct vote owner need to do it based on fivestar field
   * settings, when selected "Rating mode viewing" mode, then have to use
   * current user. For "Rating mode editing" mode - if entity have method
   * "getOwner" use entity owner, otherwise the current user has to be used.
   *
   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
   *   The entity from which try to get owner.
   * @param string $rating_mode
   *   Determines under what conditions a user can leave a review.
   *
   * @return \Drupal\Core\Session\AccountInterface
   *   The account of the vote owner.
   */
  protected function getVoteOwner(FieldableEntityInterface $entity, $rating_mode) {
    switch ($rating_mode) {
      case 'editing':
        if ($entity instanceof EntityOwnerInterface) {
          return $entity
            ->getOwner();
        }

      // Fall through.
      case 'viewing':
      default:
        return \Drupal::currentUser();
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FieldItemBase::calculateDependencies public static function Calculates dependencies for field items. Overrides FieldItemInterface::calculateDependencies 2
FieldItemBase::calculateStorageDependencies public static function Calculates dependencies for field items on the storage level. Overrides FieldItemInterface::calculateStorageDependencies 1
FieldItemBase::deleteRevision public function Defines custom revision delete behavior for field values. Overrides FieldItemInterface::deleteRevision
FieldItemBase::fieldSettingsFromConfigData public static function Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface::fieldSettingsFromConfigData 1
FieldItemBase::fieldSettingsToConfigData public static function Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface::fieldSettingsToConfigData 1
FieldItemBase::generateSampleValue public static function Generates placeholder field values. Overrides FieldItemInterface::generateSampleValue 18
FieldItemBase::getEntity public function Gets the entity that field belongs to. Overrides FieldItemInterface::getEntity
FieldItemBase::getFieldDefinition public function Gets the field definition. Overrides FieldItemInterface::getFieldDefinition
FieldItemBase::getLangcode public function Gets the langcode of the field values held in the object. Overrides FieldItemInterface::getLangcode
FieldItemBase::getSetting protected function Returns the value of a field setting.
FieldItemBase::getSettings protected function Returns the array of field settings.
FieldItemBase::onDependencyRemoval public static function Informs the plugin that a dependency of the field will be deleted. Overrides FieldItemInterface::onDependencyRemoval 1
FieldItemBase::preSave public function Defines custom presave behavior for field values. Overrides FieldItemInterface::preSave 7
FieldItemBase::setValue public function Sets the data value. Overrides Map::setValue 4
FieldItemBase::storageSettingsFromConfigData public static function Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface::storageSettingsFromConfigData 2
FieldItemBase::storageSettingsToConfigData public static function Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface::storageSettingsToConfigData 2
FieldItemBase::view public function Returns a renderable array for a single field item. Overrides FieldItemInterface::view
FieldItemBase::writePropertyValue 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::writePropertyValue
FieldItemBase::__construct public function Constructs a TypedData object given its definition and context. Overrides TypedData::__construct 1
FieldItemBase::__get public function Magic method: Gets a property value. Overrides FieldItemInterface::__get 2
FieldItemBase::__isset public function Magic method: Determines whether a property is set. Overrides FieldItemInterface::__isset
FieldItemBase::__set public function Magic method: Sets a property value. Overrides FieldItemInterface::__set 1
FieldItemBase::__unset public function Magic method: Unsets a property. Overrides FieldItemInterface::__unset
FivestarItem::defaultFieldSettings public static function Defines the field-level settings for this plugin. Overrides FieldItemBase::defaultFieldSettings
FivestarItem::defaultStorageSettings public static function Defines the storage-level settings for this plugin. Overrides FieldItemBase::defaultStorageSettings
FivestarItem::delete public function Defines custom delete behavior for field values. Overrides FieldItemBase::delete
FivestarItem::fieldSettingsForm public function Returns a form for the field-level settings. Overrides FieldItemBase::fieldSettingsForm
FivestarItem::fieldSettingsFormValidate public static function Validate callback: check field settings.
FivestarItem::getTargetEntity public function Get target entity.
FivestarItem::getVoteOwner protected function Get owner for vote.
FivestarItem::isEmpty public function Determines whether the data structure is empty. Overrides Map::isEmpty
FivestarItem::mainPropertyName public static function Returns the name of the main property, if any. Overrides FieldItemBase::mainPropertyName
FivestarItem::postSave public function Defines custom post-save behavior for field values. Overrides FieldItemBase::postSave
FivestarItem::propertyDefinitions public static function Defines field item properties. Overrides FieldItemInterface::propertyDefinitions
FivestarItem::schema public static function Returns the schema for the field. Overrides FieldItemInterface::schema
FivestarItem::storageSettingsForm public function Returns a form for the storage-level settings. Overrides FieldItemBase::storageSettingsForm
Map::$definition protected property The data definition. Overrides TypedData::$definition
Map::$properties protected property The array of properties.
Map::$values protected property An array of values for the contained properties.
Map::applyDefaultValue public function Applies the default value. Overrides TypedData::applyDefaultValue 4
Map::get public function Gets a property object. Overrides ComplexDataInterface::get
Map::getIterator public function
Map::getProperties public function Gets an array of property objects. Overrides ComplexDataInterface::getProperties
Map::getString public function Returns a string representation of the data. Overrides TypedData::getString
Map::getValue public function Gets the data value. Overrides TypedData::getValue 1
Map::onChange public function Overrides TraversableTypedDataInterface::onChange 4
Map::set public function Sets a property value. Overrides ComplexDataInterface::set
Map::toArray public function Returns an array of all property values. Overrides ComplexDataInterface::toArray 1
Map::__clone public function Magic method: Implements a deep clone.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
TypedData::$name protected property The property name.
TypedData::$parent protected property The parent typed data object.
TypedData::createInstance public static function Constructs a TypedData object given its definition and context. Overrides TypedDataInterface::createInstance
TypedData::getConstraints public function Gets a list of validation constraints. Overrides TypedDataInterface::getConstraints 9
TypedData::getDataDefinition public function Gets the data definition. Overrides TypedDataInterface::getDataDefinition
TypedData::getName public function Returns the name of a property or item. Overrides TypedDataInterface::getName
TypedData::getParent public function Returns the parent data structure; i.e. either complex data or a list. Overrides TypedDataInterface::getParent
TypedData::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition
TypedData::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
TypedData::getPropertyPath public function Returns the property path of the data. Overrides TypedDataInterface::getPropertyPath
TypedData::getRoot public function Returns the root of the typed data tree. Overrides TypedDataInterface::getRoot
TypedData::setContext public function Sets the context of a property or item via a context aware parent. Overrides TypedDataInterface::setContext
TypedData::validate public function Validates the currently set data value. Overrides TypedDataInterface::validate
TypedDataTrait::$typedDataManager protected property The typed data manager used for creating the data types.
TypedDataTrait::getTypedDataManager public function Gets the typed data manager. 2
TypedDataTrait::setTypedDataManager public function Sets the typed data manager. 2