competition-8.x-1.x-dev/src/Entity/CompetitionEntry.php

src/Entity/CompetitionEntry.php
<?php

namespace Drupal\competition\Entity;

use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\file\Entity\File;
use Drupal\user\UserInterface;
use Drupal\competition\CompetitionEntryInterface;
use Drupal\competition\CompetitionJudgingSetup;

/**
 * Defines the Competition entry entity.
 *
 * @ingroup competition
 *
 * @ContentEntityType(
 *   id = "competition_entry",
 *   label = @Translation("Competition entry"),
 *   label_plural = @Translation("Competition entries"),
 *   bundle_label = @Translation("Competition"),
 *   handlers = {
 *     "storage" = "Drupal\competition\CompetitionEntryStorage",
 *     "storage_schema" = "Drupal\competition\CompetitionEntryStorageSchema",
 *     "access" = "Drupal\competition\CompetitionEntryAccessControlHandler",
 *     "views_data" = "Drupal\competition\Entity\CompetitionEntryViewsData",
 *     "view_builder" = "Drupal\competition\CompetitionEntryViewBuilder",
 *     "list_builder" = "Drupal\competition\CompetitionEntryListBuilder",
 *     "form" = {
 *       "default" = "Drupal\competition\Form\CompetitionEntryForm",
 *       "add" = "Drupal\competition\Form\CompetitionEntryForm",
 *       "reenter" = "Drupal\competition\Form\CompetitionEntryForm",
 *       "edit" = "Drupal\competition\Form\CompetitionEntryForm",
 *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm"
 *     }
 *   },
 *   base_table = "competition_entry",
 *   admin_permission = "administer competition entries",
 *   entity_keys = {
 *     "id" = "ceid",
 *     "bundle" = "type",
 *     "label" = "ceid",
 *     "uid" = "uid",
 *     "status" = "status",
 *     "uuid" = "uuid"
 *   },
 *   bundle_entity_type = "competition",
 *   field_ui_base_route = "entity.competition.edit_form",
 *   common_reference_target = TRUE,
 *   constraints = {
 *     "CompetitionEntry" = {}
 *   },
 *   links = {
 *     "collection" = "/admin/content/competition",
 *     "canonical" = "/entry/{competition_entry}",
 *     "add-form" = "/competition/{competition}/enter",
 *     "reenter-form" = "/competition/{competition}/reenter",
 *     "edit-form" = "/entry/{competition_entry}/edit",
 *     "delete-form" = "/entry/{competition_entry}/delete"
 *   }
 * )
 */
class CompetitionEntry extends ContentEntityBase implements CompetitionEntryInterface {

  use EntityChangedTrait;
  use CompetitionEntryJudgingTrait;

  /**
   * The Competition entry's Competition cycle.
   *
   * @var string
   */
  protected $cycle;

  /**
   * {@inheritdoc}
   */
  public static function preCreate(EntityStorageInterface $storage_controller, array &$values) {
    parent::preCreate($storage_controller, $values);
  }

  /**
   * {@inheritdoc}
   */
  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    parent::postSave($storage, $update);

    $data = $this->getData();
    if (!empty($data['judging']['rounds'])) {

      // Add record for each round to index table.
      $db = \Drupal::service('database');
      foreach ($data['judging']['rounds'] as $round_num => $round) {

        $db->delete(CompetitionJudgingSetup::INDEX_TABLE)
          ->condition('ceid', $this->id(), '=')
          ->condition('scores_round', $round_num, '=')
          ->execute();

        if (isset($round['votes'])) {

          // Voting round.
          $db->insert(CompetitionJudgingSetup::INDEX_TABLE)
            ->fields([
              'ceid',
              'scores_round',
              'scores_finalized',
              'scores_computed',
              'votes',
            ])
            ->values([
              $this->id(),
              $round_num,
              1,
              1,
              $round['votes'],
            ])
            ->execute();

        }
        elseif (!empty($round['scores'])) {

          // Scored round (pass/fail or criteria)
          $scores_finalized = 0;
          foreach ($round['scores'] as $score) {
            if ($score->finalized) {
              $scores_finalized++;
            }
          }

          $average_score = $this->getAverageScore($round_num);

          $db->insert(CompetitionJudgingSetup::INDEX_TABLE)
            ->fields([
              'ceid',
              'scores_round',
              'scores_finalized',
              'scores_computed',
            ])
            ->values([
              $this->id(),
              $round_num,
              $scores_finalized,
              $average_score,
            ])
            ->execute();

        }

      }
    }

  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {

    $fields = parent::baseFieldDefinitions($entity_type);

    $fields['ceid'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('ID'))
      ->setDescription(t('The ID of the Competition entry.'))
      ->setReadOnly(TRUE);

    $fields['type'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Type'))
      ->setDescription(t('The Competition type/bundle.'))
      ->setSetting('target_type', 'competition')
      ->setRequired(TRUE);

    $fields['cycle'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('Cycle'))
      ->setSettings([
        'allowed_values' => \Drupal::configFactory()
          ->get('competition.settings')
          ->get('cycles'),
      ])
      ->setDefaultValue("2015")
      ->setRequired(TRUE)
      ->setReadOnly(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'string',
        'weight' => -4,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => -100,
      ])
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['status'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('Status'))
      ->setSettings([
        'allowed_values' => \Drupal::configFactory()
          ->get('competition.settings')
          ->get('statuses'),
      ])
      ->setDefaultValue(CompetitionEntryInterface::STATUS_CREATED)
      ->setRequired(TRUE)
      ->setReadOnly(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'string',
        'weight' => -4,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => -100,
      ])
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Entry owner'))
      ->setRevisionable(FALSE)
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setTranslatable(FALSE)
      ->setRequired(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'entity_reference_label',
        'weight' => 0,
        'settings' => [
          'link' => TRUE,
        ],
      ])
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'weight' => -90,
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ],
      ])
      ->setDisplayConfigurable('form', FALSE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['weight'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Weight'))
      ->setDescription(t('A weight value for custom sorting, to be assigned by admins.'))
      ->setDefaultValue(0)
      ->setDisplayOptions('form', [
        'weight' => -80,
      ]);

    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('The UUID of the Competition entry.'))
      ->setReadOnly(TRUE);

    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'));

    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the entity was last edited.'));

    $fields['ceid_referrer'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Referrer ID'))
      ->setDescription(t('Indicates if the Competition entry is a referral triggered by another entry.'));

    $fields['data'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Data'))
      ->setDescription(t('Storage data'));

    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getType() {
    return $this->bundle();
  }

  /**
   * {@inheritdoc}
   */
  public function getCompetition() {
    return \Drupal::entityTypeManager()
      ->getStorage($this->getEntityType()->getBundleEntityType())
      ->load($this->bundle());
  }

  /**
   * {@inheritdoc}
   */
  public function getCycle() {
    return $this->get('cycle')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function getStatus() {
    return $this->get('status')->value;
  }

  /**
   * Retrieve the keys/labels for all configured statuses.
   *
   * @return array
   *   Keys are integers; labels are translated strings.
   *
   * @see CompetitionSettingsForm::submitForm()
   *
   * TODO: inject string translation service and use StringTranslationTrait?
   * t() is not already a method.
   */
  public static function getStatusLabels() {
    // These can be customized on the competition settings admin form.
    // However, the four defined by and used in this module's code - 'Created'
    // 'Updated', 'Finalized', 'Archived' - are ensured to be included with the
    // correct integer keys via the submit handler.
    $statuses = \Drupal::configFactory()
      ->get('competition.settings')
      ->get('statuses');

    $return = [];

    foreach ($statuses as $key => $label) {
      // Escaping is an extra precaution - labels are already sanitized on
      // settings form submit.
      $return[$key] = (string) Html::Escape($label);
    }

    return $return;
  }

  /**
   * {@inheritdoc}
   *
   * TODO: inject string translation service and use StringTranslationTrait?
   * t() is not already a method.
   */
  public function getStatusLabel() {
    $label_status = t('Created');

    switch ($this->getStatus()) {
      case CompetitionEntryInterface::STATUS_UPDATED:
        $label_status = t('Updated');
        break;

      case CompetitionEntryInterface::STATUS_FINALIZED:
        $label_status = t('Finalized');
        break;

      case CompetitionEntryInterface::STATUS_ARCHIVED:
        $label_status = t('Archived');
        break;
    }

    return $label_status;
  }

  /**
   * {@inheritdoc}
   */
  public function setStatus($status) {
    $this->set('status', $status);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getCreatedTime() {
    return $this->get('created')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setCreatedTime($timestamp) {
    $this->set('created', $timestamp);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwner() {
    return $this->get('uid')->entity;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwnerId() {
    return $this->get('uid')->target_id;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwnerId($uid) {
    $this->set('uid', $uid);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwner(UserInterface $account) {
    $this->set('uid', $account->id());
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getData() {
    return unserialize($this->get('data')->value);
  }

  /**
   * {@inheritdoc}
   */
  public function setData($data) {
    $this->set('data', serialize($data));
    return $this;
  }

  /**
   * Get data blob and alter it as desired for storing into reporting table.
   *
   * @see CompetitionReporter::flattenEntities()
   *
   * TODO: is this a good way to do this?
   * - all other field values are adjusted by field type or name directly in
   *   CompetitionReporter::flattenEntities()
   * - could use existing alter hook in flattenEntities() (on entire entry,
   *   before pulling its field values), implement it by competition module
   *   and call this
   * - could do a custom alter hook in there, but that's pretty one-off
   * - would event subscriber be appropriate? (maybe not, if we're altering)
   */
  public function getDataReportingAltered() {

    $data = $this->getData();

    // TODO: splitting out judging data alteration seems right - but how is
    // best to check if that's relevant?
    if (!empty($data['judging'])) {
      $this->dataReportingAlterJudging($data);
    }

    return $data;

  }

  /**
   * {@inheritdoc}
   */
  public function getTempData() {
    $data = \Drupal::service('user.data')
      ->get('competition', $this->getOwnerId(), 'entry');

    if (!empty($data)) {
      // 2017-02-05, Tory:
      // Previously, this data was keyed only by the entry's cycle. To support
      // multiple entries per user and multiple simultaneous competitions, use
      // entry ID going forward.
      $key = 'id:' . $this->id();

      // After this code change, temp data is always saved with entry ID as key;
      // check cycle key as fallback, to pull data that was saved before it.
      $key_old = $this->getCycle();

      if (isset($data[$key])) {
        $data = $data[$key];
      }
      elseif (isset($data[$key_old])) {
        $data = $data[$key_old];
      }
    }

    return (!empty($data) ? $data : FALSE);
  }

  /**
   * {@inheritdoc}
   */
  public function setTempData(array $input) {
    $user_data_service = \Drupal::service('user.data');

    // 2017-02-05, Tory:
    // Note that entry's cycle was previously used as this key. To support
    // multiple entries per user and multiple simultaneous competitions, use
    // entry ID going forward.
    // @see ::getTempData()
    $key = 'id:' . $this->id();

    $temp = [
      $key => [],
    ];

    if (empty($input)) {
      $user_data_service
        ->set('competition', $this->getOwnerId(), 'entry', $temp);

      return $this;
    }

    foreach ($input as $field_name => $data) {
      if (stripos($field_name, 'field_') !== 0) {
        continue;
      }

      $parents = [$field_name];
      $value = $input[$field_name];

      while (is_array($value)) {
        $value = NestedArray::getValue($input, $parents);

        if (is_array($value)) {
          if (count($value) == 1) {
            // If only one element in array, this should be nesting - collect
            // the key to continue traveling down.
            reset($value);
            $parents[] = key($value);
          }
          else {
            $value = array_filter($value);
            break;
          }
        }
      }

      // Remove the field name from parents, as we'll be working within each
      // field's widget when re-populating.
      array_shift($parents);

      // If this field value is an uploaded file, set file status to permanent
      // so Drupal will not automatically delete it during cleanup.
      if (!empty($value['fids'])) {
        // ('fids' is not an array at this point.)
        $fid = $value['fids'];
        if (is_numeric($value['fids'])) {
          if ($file = File::load((int) $fid)) {
            $file->setPermanent();
            $file->save();
            $file_usage = \Drupal::service('file.usage');
            $file_usage->add($file, 'competition', 'competition_entry', $this->id());
          }
        }
      }

      $temp[$key][$field_name] = [
        'input' => $value,
        'parents' => $parents,
      ];
    }

    // Set temp entry data to be copied on to entity on full submission.
    $user_data_service
      ->set('competition', $this->getOwnerId(), 'entry', $temp);

    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setReferrerEntry(CompetitionEntryInterface $entry) {
    $this->set('ceid_referrer', $entry->id());
    return $this;
  }

  /**
   * Send submission confirmation email.
   *
   * Utility method to send an email confirming entry submission. This should
   * be called when status changes to Finalized.
   *
   * Email will ONLY be sent if the "Entry confirmation email" text set on the
   * competition is non-empty.
   *
   * @see competition_mail()
   * @see CompetitionEntryForm::save()
   *
   * TODO: does this method make sense on the entity class? It requires a
   * service; is there a practice to inject service to an entity?
   */
  public function sendSubmissionConfirmEmailIfConfigured() {

    /** @var \Drupal\competition\CompetitionInterface $competition */
    $competition = $this->getCompetition();

    // Check competition entity - is email confirmation text non-empty?
    $longtext = $competition->getLongtext();
    if (empty($longtext->confirmation_email)) {
      return;
    }

    /** @var \Drupal\Core\Mail\MailManager $mailManager */
    $mailManager = \Drupal::service('plugin.manager.mail');

    $owner = $this->getOwner();

    $module = 'competition';
    $key = 'competition_entry_submit_confirm';

    $to = $owner->getEmail();
    $langcode = $owner->getPreferredLangcode();

    // Collect params needed by competition_mail().
    $params = [
      'entry_id' => $this->id(),
      'competition_title' => $competition->getLabel(),
      'body_text' => $longtext->confirmation_email,
    ];

    $send = TRUE;
    $message = $mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send);

    if ($message['result'] !== TRUE) {
      // Log error for admins.
      \Drupal::logger('competition')->error(t("Error attempting to send entry submission confirmation email:<br/><br/>
        Recipient email address(es): %to<br/>
        User ID: %uid<br/>
        Entry ID: %ceid",
        [
          '%to' => $message['to'],
          '%uid' => $owner->id(),
          '%ceid' => $this->id(),
        ]
      ));
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc