brokencube/automatorm

View on GitHub
src/Database/Query.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php
namespace Automatorm\Database;

use Automatorm\Exception;
use Automatorm\Interfaces\Connection as ConnectionInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;

class Query implements LoggerAwareInterface
{
    use LoggerAwareTrait;
    
    protected $connection; // Connection object
    protected $error;
    protected $sql = []; // Array of SQL queries to run
    protected $lock = false;
    protected $debug;

    // Readonly access to object properties
    public function __get($var)
    {
        switch ($var) {
            case 'connection':
            case 'error':
            case 'sql':
            case 'debug':
            case 'lock':
                return $this->{$var};
        }
        return $this->debug[$var];
    }
    
    public static function run($sql, ConnectionInterface $connection)
    {
        $query = new static($connection, $sql);
        return $query->execute();
    }
    
    // Create a new query container
    public function __construct(ConnectionInterface $connection, $sql = null, array $data = [])
    {
        $this->connection = $connection;
        
        if ($sql) {
            $this->sql($sql, $data);
        }
        
        // Default Logger
        $this->logger = $this->connection->getLogger();
    }
    
    // Add arbitary SQL to the query queue
    public function sql($sql, array $data = [])
    {
        if ($sql instanceof QueryBuilder) {
            $this->sql[] = Sql::build($sql);
        } elseif ($sql instanceof Sql) {
            $this->sql[] = $sql;
        } else {
            $this->sql[] = new Sql(trim($sql), $data);
        }
        
        return $this;
    }
    
    ////////////////
    
    public function insertId($position = 0)
    {
        return $this->debug[$position]['insert_id'];
    }
    
    public function affectedRows($position = 0)
    {
        return $this->debug[$position]['affected_rows'];
    }
    
    /////////////////
    
    public function escape($string)
    {
        $pdo = $this->connection->connect();
        return $pdo->quote($string);
    }
    
    public function transaction()
    {
        $pdo = $this->connection->connect();
        $pdo->beginTransaction();
    }
    
    public function commit()
    {
        $pdo = $this->connection->connect();
        $pdo->commit();
    }
    
    public function execute()
    {
        $pdo = $this->connection->connect();
        
        // We are only allowed to execute each Query object once!
        if ($this->lock) {
            throw new Exception\Database("QUERY_LOCKED: This query has already been executed", $this);
        }
        $this->lock = true;
        
        $count = 0;
        $return = [];
        
        try {
            foreach ($this->sql as $query) {
                $time = microtime(true);
                $result = $query->execute($pdo);
                if ($result->columnCount()) {
                    $return[$count] = $result->fetchAll(\PDO::FETCH_ASSOC);
                } else {
                    $return[$count] = [];
                }
                
                // Store some useful data about this set of results
                $this->debug[$count]['insert_id'] = $pdo->lastInsertId();
                $this->debug[$count]['affected_rows'] = $result->rowCount();
                $this->debug[$count]['time'] = microtime(true) - $time;
                $count++;
            }
        } catch (\PDOException $e) {
            $this->debug[$count]['time'] = microtime(true) - $time;
            $this->error = $e->getMessage();
            throw new Exception\Query($this, $e);
        } finally {
            // Log the query with Psr3 Logger
            $this->logQuery($this);
        }
        
        // Finally, return the results of the query
        return $return;
    }
    
    public function logQuery(Query $query)
    {
        if (!$this->logger) {
            return;
        }
        
        $count = 0;
        foreach ($query->sql as $sql) {
            $preview = preg_replace('/\s+/m', ' ', substr($sql->sql, 0, 100));
            $time = number_format($query->debug[$count]['time'] * 1000, 2);
            
            $message = "{$time}ms Con:{$query->connection->name} | $preview";
            $this->logger->notice(
                $message,
                [
                    'query' => $sql->sql,
                    'data' => $sql->data,
                    'debug' => $query->debug[$count],
                    'error' => $query->error
                ]
            );
            $count++;
        }
    }
}