257 lines
6.0 KiB
PHP
257 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace Yoast\WP\Lib\Migrations;
|
|
|
|
use Exception;
|
|
|
|
/**
|
|
* Yoast migrations table class.
|
|
*/
|
|
class Table {
|
|
|
|
/**
|
|
* The adapter.
|
|
*
|
|
* @var Adapter
|
|
*/
|
|
private $adapter;
|
|
|
|
/**
|
|
* The name
|
|
*
|
|
* @var string
|
|
*/
|
|
private $name;
|
|
|
|
/**
|
|
* The options
|
|
*
|
|
* @var array
|
|
*/
|
|
private $options;
|
|
|
|
/**
|
|
* The SQL representation of this table.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $sql = '';
|
|
|
|
/**
|
|
* Whether or not the table has been initialized.
|
|
*
|
|
* @var bool
|
|
*/
|
|
private $initialized = false;
|
|
|
|
/**
|
|
* The columns
|
|
*
|
|
* @var Column[]
|
|
*/
|
|
private $columns = [];
|
|
|
|
/**
|
|
* The primary keys.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
private $primary_keys = [];
|
|
|
|
/**
|
|
* Whether or not to auto generate the id.
|
|
*
|
|
* @var bool
|
|
*/
|
|
private $auto_generate_id = true;
|
|
|
|
/**
|
|
* Creates an instance of the Adapter.
|
|
*
|
|
* @param Adapter $adapter The current adapter.
|
|
* @param string $name The table name.
|
|
* @param array $options The options.
|
|
*
|
|
* @throws Exception If invalid arguments are passed.
|
|
*/
|
|
public function __construct( $adapter, $name, $options = [] ) {
|
|
// Sanity checks.
|
|
if ( ! $adapter instanceof Adapter ) {
|
|
throw new Exception( 'Invalid MySQL Adapter instance.' );
|
|
}
|
|
if ( ! $name ) {
|
|
throw new Exception( "Invalid 'name' parameter" );
|
|
}
|
|
$this->adapter = $adapter;
|
|
$this->name = $name;
|
|
$this->options = $options;
|
|
$this->init_sql( $name, $options );
|
|
if ( \array_key_exists( 'id', $options ) ) {
|
|
if ( \is_bool( $options['id'] ) && $options['id'] === false ) {
|
|
$this->auto_generate_id = false;
|
|
}
|
|
|
|
// If its a string then we want to auto-generate an integer-based
|
|
// primary key with this name.
|
|
if ( \is_string( $options['id'] ) ) {
|
|
$this->auto_generate_id = true;
|
|
$this->primary_keys[] = $options['id'];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a column
|
|
*
|
|
* @param string $column_name The column name.
|
|
* @param string $type The column type.
|
|
* @param array $options The options.
|
|
*/
|
|
public function column( $column_name, $type, $options = [] ) {
|
|
// If there is already a column by the same name then silently fail and continue.
|
|
foreach ( $this->columns as $column ) {
|
|
if ( $column->name === $column_name ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
$column_options = [];
|
|
if ( \array_key_exists( 'primary_key', $options ) ) {
|
|
if ( $options['primary_key'] ) {
|
|
$this->primary_keys[] = $column_name;
|
|
}
|
|
}
|
|
if ( \array_key_exists( 'auto_increment', $options ) ) {
|
|
if ( $options['auto_increment'] ) {
|
|
$column_options['auto_increment'] = true;
|
|
}
|
|
}
|
|
$column_options = \array_merge( $column_options, $options );
|
|
$column = new Column( $this->adapter, $column_name, $type, $column_options );
|
|
$this->columns[] = $column;
|
|
}
|
|
|
|
/**
|
|
* Shortcut to create timestamps columns (default created_at, updated_at)
|
|
*
|
|
* @param string $created_column_name Created at column name.
|
|
* @param string $updated_column_name Updated at column name.
|
|
*/
|
|
public function timestamps( $created_column_name = 'created_at', $updated_column_name = 'updated_at' ) {
|
|
$this->column( $created_column_name, 'datetime' );
|
|
$this->column(
|
|
$updated_column_name,
|
|
'timestamp',
|
|
[
|
|
'null' => false,
|
|
'default' => 'CURRENT_TIMESTAMP',
|
|
'extra' => 'ON UPDATE CURRENT_TIMESTAMP',
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get all primary keys
|
|
*
|
|
* @return string
|
|
*/
|
|
private function keys() {
|
|
if ( \count( $this->primary_keys ) > 0 ) {
|
|
$lead = ' PRIMARY KEY (';
|
|
$quoted = [];
|
|
foreach ( $this->primary_keys as $key ) {
|
|
$quoted[] = \sprintf( '%s', $this->adapter->identifier( $key ) );
|
|
}
|
|
$primary_key_sql = ",\n" . $lead . \implode( ',', $quoted ) . ')';
|
|
return $primary_key_sql;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Table definition
|
|
*
|
|
* @param bool $wants_sql Whether or not to return SQL or execute the query. Defaults to false.
|
|
*
|
|
* @return bool|string
|
|
*
|
|
* @throws Exception If the table definition has not been intialized.
|
|
*/
|
|
public function finish( $wants_sql = false ) {
|
|
if ( ! $this->initialized ) {
|
|
throw new Exception( \sprintf( "Table Definition: '%s' has not been initialized", $this->name ) );
|
|
}
|
|
$opt_str = '';
|
|
if ( \is_array( $this->options ) && \array_key_exists( 'options', $this->options ) ) {
|
|
$opt_str = $this->options['options'];
|
|
}
|
|
else {
|
|
if ( isset( $this->adapter->db_info['charset'] ) ) {
|
|
$opt_str = ' DEFAULT CHARSET=' . $this->adapter->db_info['charset'];
|
|
}
|
|
else {
|
|
$opt_str = ' DEFAULT CHARSET=utf8';
|
|
}
|
|
}
|
|
$close_sql = \sprintf( ') %s;', $opt_str );
|
|
$create_table_sql = $this->sql;
|
|
if ( $this->auto_generate_id === true ) {
|
|
$this->primary_keys[] = 'id';
|
|
$primary_id = new Column(
|
|
$this->adapter,
|
|
'id',
|
|
'integer',
|
|
[
|
|
'unsigned' => true,
|
|
'null' => false,
|
|
'auto_increment' => true,
|
|
]
|
|
);
|
|
$create_table_sql .= $primary_id->to_sql() . ",\n";
|
|
}
|
|
$create_table_sql .= $this->columns_to_str();
|
|
$create_table_sql .= $this->keys() . $close_sql;
|
|
if ( $wants_sql ) {
|
|
return $create_table_sql;
|
|
}
|
|
return $this->adapter->execute_ddl( $create_table_sql );
|
|
}
|
|
|
|
/**
|
|
* Get SQL for all columns.
|
|
*
|
|
* @return string The SQL.
|
|
*/
|
|
private function columns_to_str() {
|
|
$fields = [];
|
|
$len = \count( $this->columns );
|
|
for ( $i = 0; $i < $len; $i++ ) {
|
|
$c = $this->columns[ $i ];
|
|
$fields[] = $c->__toString();
|
|
}
|
|
return \implode( ",\n", $fields );
|
|
}
|
|
|
|
/**
|
|
* Init create sql statement.
|
|
*
|
|
* @param string $name The name.
|
|
* @param array $options The options.
|
|
*/
|
|
private function init_sql( $name, $options ) {
|
|
// Are we forcing table creation? If so, drop it first.
|
|
if ( \array_key_exists( 'force', $options ) && $options['force'] === true ) {
|
|
$this->adapter->drop_table( $name );
|
|
}
|
|
$temp = '';
|
|
if ( \array_key_exists( 'temporary', $options ) ) {
|
|
$temp = ' TEMPORARY';
|
|
}
|
|
$create_sql = \sprintf( 'CREATE%s TABLE ', $temp );
|
|
$create_sql .= \sprintf( "%s (\n", $this->adapter->identifier( $name ) );
|
|
$this->sql .= $create_sql;
|
|
$this->initialized = true;
|
|
}
|
|
}
|