You are here

class FlagCountManager in Flag 8.4

Class FlagCountManager.

Hierarchy

Expanded class hierarchy of FlagCountManager

1 string reference to 'FlagCountManager'
flag.services.yml in ./flag.services.yml
flag.services.yml
1 service uses FlagCountManager
flag.count in ./flag.services.yml
Drupal\flag\FlagCountManager

File

src/FlagCountManager.php, line 17

Namespace

Drupal\flag
View source
class FlagCountManager implements FlagCountManagerInterface, EventSubscriberInterface {

  /**
   * Stores flag counts per entity.
   *
   * @var array
   */
  protected $entityCounts = [];

  /**
   * Stores flag counts per flag.
   *
   * @var array
   */
  protected $flagCounts = [];

  /**
   * Stores flagged entity counts per flag.
   *
   * @var array
   */
  protected $flagEntityCounts = [];

  /**
   * Stores flag counts per flag and user.
   *
   * @var array
   */
  protected $userFlagCounts = [];

  /**
   * Database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $connection;

  /**
   * The date time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected $dateTime;

  /**
   * Constructs a FlagCountManager.
   */
  public function __construct(Connection $connection, TimeInterface $date_time) {
    $this->connection = $connection;
    $this->dateTime = $date_time;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new self($container
      ->get('database'), $container
      ->get('datetime.time'));
  }

  /**
   * {@inheritdoc}
   */
  public function getEntityFlagCounts(EntityInterface $entity) {
    $entity_type = $entity
      ->getEntityTypeId();
    $entity_id = $entity
      ->id();
    if (!isset($this->entityCounts[$entity_type][$entity_id])) {
      $this->entityCounts[$entity_type][$entity_id] = [];
      $query = $this->connection
        ->select('flag_counts', 'fc');
      $result = $query
        ->fields('fc', [
        'flag_id',
        'count',
      ])
        ->condition('fc.entity_type', $entity_type)
        ->condition('fc.entity_id', $entity_id)
        ->execute();
      foreach ($result as $row) {
        $this->entityCounts[$entity_type][$entity_id][$row->flag_id] = $row->count;
      }
    }
    return $this->entityCounts[$entity_type][$entity_id];
  }

  /**
   * {@inheritdoc}
   */
  public function getFlagFlaggingCount(FlagInterface $flag) {
    $flag_id = $flag
      ->id();
    $entity_type = $flag
      ->getFlaggableEntityTypeId();

    // We check to see if the flag count is already in the cache,
    // if it's not, run the query.
    if (!isset($this->flagCounts[$flag_id][$entity_type])) {
      $query = $this->connection
        ->select('flagging', 'f')
        ->condition('flag_id', $flag_id)
        ->condition('entity_type', $entity_type);

      // Using an expression is faster than using countQuery().
      $query
        ->addExpression('COUNT(*)');
      $this->flagCounts[$flag_id][$entity_type] = $query
        ->execute()
        ->fetchField();
    }
    return $this->flagCounts[$flag_id][$entity_type];
  }

  /**
   * {@inheritdoc}
   */
  public function getFlagEntityCount(FlagInterface $flag) {
    $flag_id = $flag
      ->id();
    if (!isset($this->flagEntityCounts[$flag_id])) {
      $query = $this->connection
        ->select('flag_counts', 'fc')
        ->condition('flag_id', $flag_id);
      $query
        ->addExpression('COUNT(*)');
      $this->flagEntityCounts[$flag_id] = $query
        ->execute()
        ->fetchField();
    }
    return $this->flagEntityCounts[$flag_id];
  }

  /**
   * {@inheritdoc}
   */
  public function getUserFlagFlaggingCount(FlagInterface $flag, AccountInterface $user, $session_id = NULL) {
    $flag_id = $flag
      ->id();
    $uid = $user
      ->id();
    $get_by_session_id = $user
      ->isAnonymous();

    // Return the flag count if it is already in the cache.
    if ($get_by_session_id) {
      if (is_null($session_id)) {
        throw new \LogicException('Anonymous users must be identified by session_id');
      }

      // Return the flag count if it is already in the cache.
      if (isset($this->userFlagCounts[$flag_id][$uid][$session_id])) {
        return $this->userFlagCounts[$flag_id][$uid][$session_id];
      }
    }
    elseif (isset($this->userFlagCounts[$flag_id][$uid])) {
      return $this->userFlagCounts[$flag_id][$uid];
    }

    // Run the query.
    $query = $this->connection
      ->select('flagging', 'f')
      ->condition('flag_id', $flag_id)
      ->condition('uid', $uid);
    if ($get_by_session_id) {
      $query
        ->condition('session_id', $session_id);
    }
    $query
      ->addExpression('COUNT(*)');
    $result = $query
      ->execute()
      ->fetchField();

    // Cache the result.
    if ($get_by_session_id) {

      // Cached by flag, by uid and by session_id.
      $this->userFlagCounts[$flag_id][$uid][$session_id] = $result;
    }
    else {

      // Cached by flag, by uid.
      $this->userFlagCounts[$flag_id][$uid] = $result;
    }
    return $result;
  }

  /**
   * Increments count of flagged entities.
   *
   * @param \Drupal\flag\Event\FlaggingEvent $event
   *   The flagging event.
   */
  public function incrementFlagCounts(FlaggingEvent $event) {
    $flagging = $event
      ->getFlagging();
    $flag = $flagging
      ->getFlag();
    $entity = $flagging
      ->getFlaggable();
    $this->connection
      ->merge('flag_counts')
      ->key([
      'flag_id' => $flag
        ->id(),
      'entity_id' => $entity
        ->id(),
      'entity_type' => $entity
        ->getEntityTypeId(),
    ])
      ->fields([
      'last_updated' => $this->dateTime
        ->getRequestTime(),
      'count' => 1,
    ])
      ->expression('count', 'count + :inc', [
      ':inc' => 1,
    ])
      ->execute();
    $this
      ->resetLoadedCounts($entity, $flag);
  }

  /**
   * Decrements count of flagged entities.
   *
   * @param \Drupal\flag\Event\UnflaggingEvent $event
   *   The unflagging event.
   */
  public function decrementFlagCounts(UnflaggingEvent $event) {
    $flaggings_count = [];
    $flag_ids = [];
    $entity_ids = [];
    $flaggings = $event
      ->getFlaggings();

    // Attempt to optimize the amount of queries that need to be executed if
    // a lot of flaggings are deleted. Build a list of flags and entity_ids
    // that will need to be updated. Entity type is ignored since one flag is
    // specific to a given entity type.
    foreach ($flaggings as $flagging) {
      $flag_id = $flagging
        ->getFlagId();
      $entity_id = $flagging
        ->getFlaggableId();
      $flag_ids[$flag_id] = $flag_id;
      $entity_ids[$entity_id] = $entity_id;
      if (!isset($flaggings_count[$flag_id][$entity_id])) {
        $flaggings_count[$flag_id][$entity_id] = 1;
      }
      else {
        $flaggings_count[$flag_id][$entity_id]++;
      }
      $this
        ->resetLoadedCounts($flagging
        ->getFlaggable(), $flagging
        ->getFlag());
    }

    // Build a query that fetches the count for all flag and entity ID
    // combinations.
    $result = $this->connection
      ->select('flag_counts')
      ->fields('flag_counts', [
      'flag_id',
      'entity_type',
      'entity_id',
      'count',
    ])
      ->condition('flag_id', $flag_ids, 'IN')
      ->condition('entity_id', $entity_ids, 'IN')
      ->execute();
    $to_delete = [];
    foreach ($result as $row) {

      // The query above could fetch combinations that are not being deleted
      // skip them now.
      // Most cases will either delete flaggings of a single flag or a single
      // entity where that does not happen.
      if (!isset($flaggings_count[$row->flag_id][$row->entity_id])) {
        continue;
      }
      if ($row->count <= $flaggings_count[$row->flag_id][$row->entity_id]) {

        // If all flaggings for the given flag and entity are deleted, delete
        // the row.
        $to_delete[$row->flag_id][] = $row->entity_id;
      }
      else {

        // Otherwise, update the count.
        $this->connection
          ->update('flag_counts')
          ->expression('count', 'count - :decrement', [
          ':decrement' => $flaggings_count[$row->flag_id][$row->entity_id],
        ])
          ->condition('flag_id', $row->flag_id)
          ->condition('entity_id', $row->entity_id)
          ->execute();
      }
    }

    // Execute a delete query per flag.
    foreach ($to_delete as $flag_id => $entity_ids) {
      $this->connection
        ->delete('flag_counts')
        ->condition('flag_id', $flag_id)
        ->condition('entity_id', $entity_ids, 'IN')
        ->execute();
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events = [];
    $events[FlagEvents::ENTITY_FLAGGED][] = [
      'incrementFlagCounts',
      -100,
    ];
    $events[FlagEvents::ENTITY_UNFLAGGED][] = [
      'decrementFlagCounts',
      -100,
    ];
    return $events;
  }

  /**
   * Resets loaded flag counts.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The flagged entity.
   * @param \Drupal\flag\FlagInterface $flag
   *   The flag.
   */
  protected function resetLoadedCounts(EntityInterface $entity, FlagInterface $flag) {

    // @todo Consider updating them instead of just clearing it.
    unset($this->entityCounts[$entity
      ->getEntityTypeId()][$entity
      ->id()]);
    unset($this->flagCounts[$flag
      ->id()]);
    unset($this->flagEntityCounts[$flag
      ->id()]);
    unset($this->userFlagCounts[$flag
      ->id()]);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FlagCountManager::$connection protected property Database connection.
FlagCountManager::$dateTime protected property The date time service.
FlagCountManager::$entityCounts protected property Stores flag counts per entity.
FlagCountManager::$flagCounts protected property Stores flag counts per flag.
FlagCountManager::$flagEntityCounts protected property Stores flagged entity counts per flag.
FlagCountManager::$userFlagCounts protected property Stores flag counts per flag and user.
FlagCountManager::create public static function
FlagCountManager::decrementFlagCounts public function Decrements count of flagged entities.
FlagCountManager::getEntityFlagCounts public function Gets flag counts for all flags on an entity. Overrides FlagCountManagerInterface::getEntityFlagCounts
FlagCountManager::getFlagEntityCount public function Gets the count of entities flagged by the given flag. Overrides FlagCountManagerInterface::getFlagEntityCount
FlagCountManager::getFlagFlaggingCount public function Gets the count of flaggings for the given flag. Overrides FlagCountManagerInterface::getFlagFlaggingCount
FlagCountManager::getSubscribedEvents public static function Returns an array of event names this subscriber wants to listen to.
FlagCountManager::getUserFlagFlaggingCount public function Gets the count of the flaggings made by a user with a flag. Overrides FlagCountManagerInterface::getUserFlagFlaggingCount
FlagCountManager::incrementFlagCounts public function Increments count of flagged entities.
FlagCountManager::resetLoadedCounts protected function Resets loaded flag counts.
FlagCountManager::__construct public function Constructs a FlagCountManager.