You are here

abstract class IntColumnHandler in Dynamic Entity Reference 8.2

Per database implementation of denormalizing into integer columns.

Hierarchy

Expanded class hierarchy of IntColumnHandler

1 string reference to 'IntColumnHandler'
dynamic_entity_reference.services.yml in ./dynamic_entity_reference.services.yml
dynamic_entity_reference.services.yml
1 service uses IntColumnHandler
dynamic_entity_reference.storage.create_column in ./dynamic_entity_reference.services.yml
Drupal\dynamic_entity_reference\Storage\IntColumnHandler

File

src/Storage/IntColumnHandler.php, line 11

Namespace

Drupal\dynamic_entity_reference\Storage
View source
abstract class IntColumnHandler implements IntColumnHandlerInterface {

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

  /**
   * IntColumnHandler constructor.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection.
   */
  public function __construct(Connection $connection) {
    $this->connection = $connection;
  }

  /**
   * Checks whether all columns exist.
   *
   * @param \Drupal\Core\Database\Schema $schema
   *   The database Schema object for this connection.
   * @param string $table
   *   The name of the table in drupal (no prefixing).
   * @param string[] $columns
   *   The names of the columns.
   *
   * @return bool
   *   TRUE if all the given columns exists, otherwise FALSE.
   */
  public static function allColumnsExist(Schema $schema, $table, array $columns) {
    foreach ($columns as $column) {

      // When a new module adds more than one new basefields in
      // hook_entity_base_field_info() then the entity system will report those
      // but they won't exist yet in the database. It's enough to fire when
      // called for the last one.
      if (!$schema
        ->fieldExists($table, $column)) {
        return FALSE;
      }
    }
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function create($table, array $columns, array $index_columns = []) {
    $schema = $this->connection
      ->schema();

    // The integer column specification.
    $spec = [
      'type' => 'int',
      'unsigned' => TRUE,
      'not null' => FALSE,
    ];

    // Before MySQL 5.7.2, there cannot be multiple triggers for a given table
    // that have the same trigger event and action time so set all involved
    // columns in one go. See
    // https://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html for more.
    // In SQLite, it's cheaper to run one query instead on per column.
    $body = [];
    $new = [];
    if (!static::allColumnsExist($schema, $table, $columns)) {
      return [];
    }
    foreach ($columns as $column) {
      $column_int = $column . '_int';

      // Make sure the integer columns exist.
      if (!$schema
        ->fieldExists($table, $column_int)) {
        $index_fields = [
          $column_int,
        ];
        $full_spec = [
          'fields' => [
            $column_int => $spec,
          ],
        ];
        if (!empty($index_columns)) {
          $full_spec['fields'] = array_merge($full_spec['fields'], $index_columns);
          $index_fields = array_merge($index_fields, array_keys($index_columns));
        }
        $new[] = $column_int;
        $schema
          ->addField($table, $column_int, $spec);
        $schema
          ->addIndex($table, $column_int, $index_fields, $full_spec);
      }

      // This is the heart of this function: before an UPDATE/INSERT, set the
      // value of the integer column to the integer value of the string column.
      $body[] = $this
        ->createBody($column_int, $column);
    }
    if ($new) {
      $body = implode(', ', $body);
      $prefixed_name = str_replace('"', '', $this->connection
        ->prefixTables('{' . $table . '}'));
      foreach ([
        'update',
        'insert',
      ] as $op) {
        $trigger = $prefixed_name . '_der_' . $op;
        if (strlen($trigger) > 64) {
          $trigger = substr($trigger, 0, 56) . substr(hash('sha256', $trigger), 0, 8);
        }
        $this->connection
          ->query("DROP TRIGGER IF EXISTS {$trigger}");
        if ($body) {
          $this
            ->createTrigger($trigger, $op, $prefixed_name, $body);
        }
      }
    }
    return $new;
  }

  /**
   * Creates the body of the trigger.
   *
   * Creates a part of the statement to set the value of the integer column to
   * the integer value of the string column.
   *
   * @param string $column_int
   *   The name of the target_id_int column.
   * @param string $column
   *   The name of the target_id column.
   */
  protected abstract function createBody($column_int, $column);

  /**
   * Actually creates the trigger.
   *
   * @param string $trigger
   *   The name of the trigger.
   * @param string $op
   *   Either UPDATE or INSERT.
   * @param string $prefixed_name
   *   The already prefixed table table.
   * @param string $body
   *   The body of the trigger.
   */
  protected abstract function createTrigger($trigger, $op, $prefixed_name, $body);

  /**
   * Removes the trigger.
   *
   * @param string $table
   *   Name of the table.
   * @param string $column
   *   Name of the column.
   *
   * @todo not sure whether we want to bother with deleting.
   */
  public function delete($table, $column) {
  }

}

Members

Namesort descending Modifiers Type Description Overrides
IntColumnHandler::$connection protected property The database connection.
IntColumnHandler::allColumnsExist public static function Checks whether all columns exist.
IntColumnHandler::create public function Creates the _int columns and the triggers for them. Overrides IntColumnHandlerInterface::create
IntColumnHandler::createBody abstract protected function Creates the body of the trigger. 2
IntColumnHandler::createTrigger abstract protected function Actually creates the trigger. 2
IntColumnHandler::delete public function Removes the trigger.
IntColumnHandler::__construct public function IntColumnHandler constructor.