Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 63
SqlTable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 12
1190.00
0.00% covered (danger)
0.00%
0 / 63
 __construct
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 11
 selectByPrimaryKey
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 primaryKeyValues
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 3
 primaryKeyWhere
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 uniqueIndexWhere
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 3
 uniqueIndexesValues
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 6
 selectCountEachUniqueIndex
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 7
 uniqueIndexesWhere
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 7
 selectCountRowUniqueIndexesDuplicates
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 selectCountRowUniqueAndPrimaryKeyDuplicates
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 1
 countUnique
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 8
 update
0.00% covered (danger)
0.00%
0 / 1
72.00
0.00% covered (danger)
0.00%
0 / 14
<?php
/** @noinspection SqlNoDataSourceInspection */
/** @noinspection SqlResolve */
namespace ia\Sql;
use ia\Sql\Mysql\SqlBuilder;
use ia\Util\Str;
class SqlTable {
    /** @var string */
    private $tableName;
    /** @var string */
    private $tableNameQuoted;
    /** @var array $primaryKey ['pkColumn1', ...] */
    private $primaryKey;
    /** @var array $uniqueIndexes [ 'uniqueKeyName'=>['column1', ...], ...  ]*/
    private $uniqueIndexes;
    /** @var string */
    private $registrationDateColumn = null;
    /** @var string */
    private $lastUpdatedDateColumn = null;
    /** @var string */
    private $lastUpdatedByUserNameColumn = null;
    /** @var SqlBuilder */
    private $sqlBuilder;
    /**
     * SqlTable constructor.
     * @param string $tableName table name or database.table_name, ...
     * @param array $primaryKey ['column1', ....]
     * @param array $uniqueIndexes [ 'UniqueIndexName1' => ['column1', ....], ... ]
     * @param string|null $registrationDateColumn columnName
     * @param string|null $lastUpdatedDateColumn columnName
     * @param string|null $lastUpdatedByUserNameColumn
     * @param SqlBuilder|null $sqlBuilder On null it creates a default SqlBuilder (for not quoted columns/values)
     */
    public function __construct(
        string $tableName,
        array $primaryKey,
        array $uniqueIndexes = [],
        $registrationDateColumn = null,
        $lastUpdatedDateColumn = null,
        $lastUpdatedByUserNameColumn = null,
        $sqlBuilder = null)
    {
        $this->tableName = $tableName;
        $this->tableNameQuoted = Str::fieldit($this->tableName);
        $this->primaryKey = array_flip( $primaryKey );
        $this->uniqueIndexes = [];
        foreach($uniqueIndexes as $indexName => $uniqueIndex) {
            $this->uniqueIndexes[$indexName] = array_flip($uniqueIndexes);
        }
        $this->registrationDateColumn = $registrationDateColumn;
        $this->lastUpdatedDateColumn = $lastUpdatedDateColumn;
        $this->lastUpdatedByUserNameColumn = $lastUpdatedByUserNameColumn;
        $this->sqlBuilder = $sqlBuilder === null ? new SqlBuilder() : $sqlBuilder;
    }
/* PRIMARY KEY */
    /**
     * Query String SELECT * FROM table WHERE primaryKey = $columnValuesInPrimaryKey
     *
     * @param array $columnValues
     * @return string
     */
    public function selectByPrimaryKey(array $columnValues) {
        return 'SELECT * FROM ' . $this->tableNameQuoted . ' WHERE ' . $this->primaryKeyWhere($columnValues);
    }
    /**
     * ['primaryKeyColumn1'=>value,...]
     *
     * @param array $columnValues
     * @return array
     */
    public function primaryKeyValues(array $columnValues) {
        if(empty($this->primaryKey)) {
            return [];
        }
        return array_intersect_key($columnValues, $this->primaryKey);
    }
    /**
     * returns the WHERE clause (primaryKeyColumn1 = 'value' AND ...]  (without the WHERE)
     *
     * @param array $columnValues
     * @return string
     */
    public function primaryKeyWhere(array $columnValues) {
        return
            '(' . $this->sqlBuilder->where( array_intersect_key($columnValues, $this->primaryKey) ) . ')';
    }
/* UNIQUE KEYS */
    /**
     * Query String SELECT * FROM table WHERE
     *
     * @param array $columnValues
     * @param string $indexName
     * @return string
     */
    public function uniqueIndexWhere(array $columnValues, string $indexName) {
        if(array_key_exists($indexName, $this->uniqueIndexes)) {
            return '(' . $this->sqlBuilder->where( array_intersect_key($columnValues, $this->uniqueIndexes[$indexName]) ) . ')';
        }
        return "SELECT 1 FROM dual WHERE 1=0";
    }
    /**
     * Values foreach Unique Index (primary key not included) ['uniqueIndexName'=>[column1=>value,...], ...]
     *
     * @param array $columnValues
     * @param string|null $indexName
     * @return array ['uniqueIndexName'=>[column1=>value,...], ...]
     */
    public function uniqueIndexesValues(array $columnValues, $indexName = null) {
        $uniqueIndexValues = [];
        if($indexName != null && array_key_exists($indexName, $this->uniqueIndexes)) {
            $uniqueIndexValues[$indexName] = array_intersect_key($columnValues, $this->uniqueIndexes[$indexName]);
        } else {
            foreach($this->uniqueIndexes as $indexName => $uniqueIndex) {
                $uniqueIndexValues[$indexName] = array_intersect_key($columnValues, $uniqueIndex);
            }
        }
        return $uniqueIndexValues;
    }
    /**
     * An array of query strings SELECT COUNT(*) FROM table WHERE ((uniqueIndex_1_Col_1=values AND ...) AND NOT(primaryKey = $columnValuesInPrimaryKey) ),
     *  indexed by each unique index's name. If there is no primary key AND NOT clause is omitted
     * @param array $columnValues
     * @return array ['uniqueIndexName1'=>'SELECT COUNT(*)...', ...]
     */
    public function selectCountEachUniqueIndex(array $columnValues) {
        $primaryKeyWhere = $this->primaryKeyWhere($columnValues);
        $primaryKeyWhere = $primaryKeyWhere === '' ? '' : "AND NOT $primaryKeyWhere";
        $selectsCount = [];
        foreach($this->uniqueIndexes as $indexName => $uniqueIndex) {
            $selectsCount[$indexName] = "SELECT COUNT(*) FROM $this->tableNameQuoted WHERE (("
                . $this->uniqueIndexesWhere($columnValues, $indexName) . "$primaryKeyWhere)";
        }
        return $selectsCount;
    }
    /**
     * WHERE clause ((uniqueIndex_1_Col_1=values AND ...) OR ...)) (primary key not included)  (without the WHERE)
     *
     * @param array $columnValues
     * @param string|null $indexName
     * @return string
     */
    public function uniqueIndexesWhere(array $columnValues, $indexName = null) {
        if($indexName != null && array_key_exists($indexName, $this->uniqueIndexes)) {
            $uniqueIndexWhere[] = '(' .
                '(' . $this->sqlBuilder->where( array_intersect_key($columnValues, $this->uniqueIndexes[$indexName]) ) . ')'; 
        } else {
            $uniqueIndexWhere = [];
            foreach($this->uniqueIndexes as $uniqueIndex) {
                $uniqueIndexWhere[] = '(' . $this->sqlBuilder->where( array_intersect_key($columnValues, $uniqueIndex) ) . ')';
            }            
        }
        return '(' . implode(' OR ', $uniqueIndexWhere) . ')';
    }
    
    /**
     * Query String to count number of rows where $columnValues have unique indexes conflict and is not this primary key
     * 
     * @param array $columnValues
     * @return string SELECT COUNT(*) FROM table WHERE ((uniqueKey1 = $columnValuesInUniqueKey1) OR ...) AND NOT (primaryKey = $columnValuesInPrimaryKey))
     */
    public function selectCountRowUniqueIndexesDuplicates(array $columnValues) {
        return $this->countUnique($columnValues, 'AND NOT');
    }
    /**
     * Query String to count number of rows where $columnValues have primary key AND unique indexes conflict
     * 
     * @param array $columnValues
     * @return string SELECT COUNT(*) FROM table WHERE ((uniqueIndexCol=values) OR ...) OR (primaryKey=values))
     */
    public function selectCountRowUniqueAndPrimaryKeyDuplicates(array $columnValues) {
        return $this->countUnique($columnValues, 'OR');
    }
    /**
     * Query String to count number of duplicate
     * 
     * @param array $columnValues
     * @param string $operator
     * @return string
     */
    private function countUnique(array $columnValues, string $operator) {
        $uniqueWhere = $this->uniqueIndexesWhere($columnValues);
        $primaryKeyWhere = $this->primaryKeyWhere($columnValues);
        if($uniqueWhere === '' && $primaryKeyWhere === '') {
            return 'SELECT 0 FROM dual';
        }
        $select = 'SELECT COUNT(*) FROM ' . $this->tableNameQuoted . ' WHERE ';
        if($primaryKeyWhere === '') {
            return $select . $this->sqlBuilder->where($uniqueWhere);
        }
        return $select . $this->sqlBuilder->where($uniqueWhere) . " $operator " . $primaryKeyWhere;        
    }
    /* REFERENCES TO OTHER TABLES */
    /* FIELD DEFINITION */
    /* UPDATE */
    /**
     * Array with update statement and if rows are modified an update for lastUpdatedDateColumn and lastUpdatedByUserNameColumn
     * for the defined columns
     *
     * @param string $table
     * @param array $values array('col_1'=>'value', ...)
     * @param array $where array('col_1'=>'3',...)  col_1 = 3 AND ...  or string col_1=1
     * @param array $fieldNameDontQuote  array('col_2',...) col_2's value wont be quoted in the set clause
     * @param array $whereDontQuoteFieldName array('col_1',...) col_1's value wont be quoted in the where clause
     * @return array
     *
     * @see sqlBuilder->update
     *
     * Assumes on mysql connection CLIENT_FOUND_ROWS is not set, connection default value is not set
     * Assumes queries will execute inside a transaction
     */
    public function update(string $table, array $values, array $where=[], array $fieldNameDontQuote=[], array $whereDontQuoteFieldName=[] ) {
        if($this->lastUpdatedDateColumn === null && $this->lastUpdatedByUserNameColumn === null) {
            return [$this->sqlBuilder->update($table, $values, $where, $fieldNameDontQuote, $whereDontQuoteFieldName)];
        }
        $ifChangeFields = [];
        if($this->lastUpdatedDateColumn !== null &&array_key_exists('lastUpdatedDateColumn', $values)) {
            $ifChangeFields[$this->lastUpdatedDateColumn] = 'NOW()';
            unset($values[$this->lastUpdatedDateColumn]);
        }
        if($this->lastUpdatedByUserNameColumn !== null && array_key_exists('lastUpdatedByUserNameColumn', $values)) {
            $ifChangeFields[$this->lastUpdatedByUserNameColumn] = $values[$this->lastUpdatedByUserNameColumn];
            unset($values[$this->lastUpdatedByUserNameColumn]);
        }
        $sql[] = $this->sqlBuilder->update($table, $values, $where, $fieldNameDontQuote, $whereDontQuoteFieldName );
        if(!empty($ifChangeFields)) {
            $where[] = 'ROW_COUNT() > 0';
            $sql[] = $this->sqlBuilder->update($table, $ifChangeFields, $where, $fieldNameDontQuote, $whereDontQuoteFieldName);
        }
        return $sql;
    }
}