Pink-Crab/WPDB-Table-Builder

View on GitHub
src/Engines/WPDB_DB_Delta/DB_Delta_Translator.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

declare(strict_types=1);

/**
 * Interface for the translators.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @since 0.3.0
 * @author Glynn Quelch <glynn.quelch@gmail.com>
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
 * @package PinkCrab\Table_Builder
 */

namespace PinkCrab\Table_Builder\Engines\WPDB_DB_Delta;

use PinkCrab\Table_Builder\Index;
use PinkCrab\Table_Builder\Column;
use PinkCrab\Table_Builder\Schema;
use PinkCrab\Table_Builder\Foreign_Key;

class DB_Delta_Translator {

    /**
     * Returns the parsed strings for all columns based on the schema passed.
     *
     * @param \PinkCrab\Table_Builder\Schema $schema
     * @return array<string>
     */
    public function translate_columns( Schema $schema ): array {
        return array_map(
            function( Column $column ): string {

                return sprintf(
                    '%s %s%s%s%s%s',
                    $column->get_name(),
                    $this->type_mapper( $column->get_type() ?? '', $column->get_length(), $column->get_precision() ),
                    $column->is_unsigned() ? ' UNSIGNED' : '',
                    $column->is_nullable() ? ' NULL' : ' NOT NULL',
                    $column->is_auto_increment() ? ' AUTO_INCREMENT' : '',
                    $this->parse_default( $column->get_type() ?? '', $column->get_default() )
                );
            },
            $schema->get_columns()
        );
    }

    /**
     * Returns the parsed strings for all indexes based on the schema passed.
     * Should translate all Primary, Indexes and Foreign Keys
     *
     * @param \PinkCrab\Table_Builder\Schema $schema
     * @return array<string>
     */
    public function translate_indexes( Schema $schema ): array {
        return array_merge(
            $this->transform_primary( $schema ),
            $this->transform_indexes( $schema ),
            $this->transform_foreign_keys( $schema )
        );
    }

        /**
     * Maps types with length if set.
     *
     * @param string $type
     * @param int|null $length
     * @return string
     */
    protected function type_mapper( string $type, ?int $length, ?int $precision ): string {
        $type = strtoupper( $type );
        switch ( $type ) {
            // With length
            case 'CHAR':
            case 'VARCHAR':
            case 'BINARY':
            case 'VARBINARY':
            case 'TEXT':
            case 'BLOB':
                // Int
            case 'BIT':
            case 'TINYINT':
            case 'SMALLINT':
            case 'MEDIUMINT':
            case 'INT':
            case 'INTEGER':
            case 'BIGINT':
                // Date
            case 'DATETIME':
            case 'TIMESTAMP':
            case 'TIME':
                return is_null( $length ) ? $type : "{$type}({$length})";

            // Floats
            case 'FLOAT':
            case 'DOUBLE':
            case 'DOUBLE PRECISION':
            case 'DECIMAL':
            case 'DEC':
                $precision = $precision ?? 1;
                return is_null( $length ) ? $type : "{$type}({$length},{$precision})";

            default:
                return $type;
        }
    }

    /**
     * Parses the default value based on column type.
     *
     * @param string $type
     * @param mixed|null $default
     * @return string
     */
    protected function parse_default( string $type, $default ): string {
        if ( is_null( $default ) ) {
            return '';
        }

        $type = strtoupper( $type );

        // String values.
        if ( in_array( $type, array( 'JSON', 'CHAR', 'VARCHAR', 'BINARY', 'VARBINARY', 'TEXT', 'BLOB' ), true ) ) {
            return " DEFAULT '{$default}'";
        }

        return " DEFAULT {$default}";
    }

        /**
     * Parses the primary key from the defined schema.
     *
     * This should only return an array with a single value if any.
     *
     * @param \PinkCrab\Table_Builder\Schema $schema
     * @return array<string>
     */
    protected function transform_primary( Schema $schema ): array {
        return array_map(
            function( $index ) {
                return "PRIMARY KEY  ({$index->get_column()})";
            },
            array_filter(
                $schema->get_indexes(),
                function( $index ): bool {
                    return $index->is_primary();
                }
            )
        );
    }

    /**
     * Parses the indexes into a SQL strings.
     *
     * @param \PinkCrab\Table_Builder\Schema $schema
     * @return array<string>
     */
    protected function transform_indexes( Schema $schema ): array {
        return array_map(
            /** @param Index[] $index_group */
            function( array $index_group ): string {

                // Extract all parts from group.
                $key_name   = $index_group[0]->get_key_name();
                $index_type = $index_group[0]->get_type();
                $columns    = array_map(
                    function( $e ) {
                        return $e->get_column();
                    },
                    $index_group
                );

                return sprintf(
                    '%sINDEX %s (%s)',
                    \strlen( $index_type ) !== 0 ? \strtoupper( $index_type ) . ' ' : '',
                    $key_name,
                    join( ', ', $columns )
                );

            },
            $this->group_indexes( $schema )
        );
    }

    /**
     * Parses the foreign key index to SQL strings
     *
     * @param \PinkCrab\Table_Builder\Schema $schema
     * @return array<string>
     */
    protected function transform_foreign_keys( Schema $schema ): array {
        return array_map(
            function( Foreign_Key $foreign_key ): string {
                return \sprintf(
                    'FOREIGN KEY %s(%s) REFERENCES %s(%s)%s%s',
                    $foreign_key->get_key_name(),
                    $foreign_key->get_column(),
                    $foreign_key->get_reference_table(),
                    $foreign_key->get_reference_column(),
                    \strlen( $foreign_key->get_on_update() ) ? " ON UPDATE {$foreign_key->get_on_update()}" : '',
                    \strlen( $foreign_key->get_on_delete() ) ? " ON DELETE {$foreign_key->get_on_delete()}" : ''
                );
            },
            $schema->get_foreign_keys()
        );
    }

    /**
     * Groups the indexes by key_name and type.
     *
     * @param \PinkCrab\Table_Builder\Schema $schema
     * @return array<string, Index[]>
     */
    protected function group_indexes( Schema $schema ): array {
        return array_reduce(
            $schema->get_indexes(),
            function( array $carry, Index $index ): array {
                // Remove all primiary keys.
                if ( $index->is_primary() ) {
                    return $carry;
                }

                $carry[ $index->get_key_name() . '_' . $index->get_type() ][] = $index;

                return $carry;
            },
            array()
        );
    }
}