face-orm/face

View on GitHub
lib/Face/Sql/Query/FQuery.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace Face\Sql\Query;

use Face\Config;
use Face\ContextAwareInterface;
use Face\Core\EntityFace;
use Face\Exception\BadParameterException;
use Face\Exception\QueryFailedException;
use Face\Sql\Query\Clause\Select\Column;
use Face\Sql\Query\SelectBuilder\QueryFace;
use Face\Util\StringUtils;

abstract class FQuery implements QueryInterface
{


    // when we generate sql queries
    // we have to replace the face navigation token "." by an other
    // Because "." is not compatible with alias in sql and we want to avoid conflicts with user table / column names
    // then ___ is safe enough
    public static $DOT_TOKEN = ".";

    // alias that can be replaced from an instance
    protected $dotToken;


    /**
     * @var QueryFace
     */
    protected $fromQueryFace;


    /**
     * list of binds to pass to pdo object
     * @var array
     */
    protected $valueBinds;

    function __construct(EntityFace $baseFace)
    {
        $this->fromQueryFace = new QueryFace("this", $baseFace, $this);
        $this->valueBinds=[];
    }


    /**
     * replaces the waved string by the sql-valid column name
     * @param $string
     */
    public function parseColumnNames($string, ContextAwareInterface $context = null)
    {
        $matchArray = [];
        preg_match_all("#~([a-zA-Z0-9_]\\.{0,1})+#", $string, $matchArray);
        $matchArray = array_unique($matchArray[0]);

        foreach ($matchArray as $match) {
            if($context){
                $nsMatch = $context->getNameInContext($match);
            }else{
                $nsMatch = $match;
            }

            $path=ltrim($nsMatch, "~");

            $tablePath = rtrim(substr($nsMatch, 1, strrpos($nsMatch, ".")), ".");

            $replace= $this->_doFQLTableName($tablePath, null, true)
                . "."
                . $this->getBaseFace()
                    ->getElement($path)
                    ->getSqlColumnName(true);

            $string = str_replace($match, $replace, $string);
        }

        return $string;
    }


    /**
     * @inheritdoc
     */
    public function getBaseQueryFace(){
        return $this->fromQueryFace;
    }


    /**
     * Executes the query from the given pdo object
     * @param \PDO|Config|null $pdo
     * @return \PDOStatement the pdo statement ready to fetch
     */
    public function execute($config = null)
    {

        if(null == $config){
            $pdo = Config::getDefault()->getPdo();
        }else if($config instanceof \PDO){
            $pdo = $config;
        }else if($config instanceof Config){
            $pdo = $config->getPdo();
        }else{
            throw new BadParameterException('First parameter of FQuery::execute is not correct');
        }

        $stmt = $this->getPdoStatement($pdo);

        if ($stmt->execute()) {
            return $stmt;

        } else {
            // TODO : handle errors ".__FILE__.":".__LINE__;
            throw new QueryFailedException($stmt);
            //return false;
        }

    }

    /**
     * get a statement for the given pdo object, values are already bound
     * @param \PDO $pdo
     */
    public function getPdoStatement(\PDO $pdo)
    {
        $stmt = $pdo->prepare($this->getSqlString());

        $bound = $this->getBoundValues();

        foreach ($bound as $name => $bind) {
            $stmt->bindValue($name, $bind[0], $bind[1]);
        }

        return $stmt;
    }

    public function bindValue($parameter, $value, $data_type = \PDO::PARAM_STR)
    {
        $this->valueBinds[$parameter] = [$value,$data_type];

        return $this;
    }


    /**
     * @param $name
     * @return array
     */
    public function getBoundValue($name)
    {
        return $this->getBoundValues()[$name];
    }

    /**
     * @param $name
     * @return array
     */
    public function getBoundValues()
    {
        return $this->valueBinds;
    }

    /**
     * gives all the entities which are part of the FQuery
     * @return QueryFace[] list of the face
     */
    public function getAvailableQueryFaces()
    {
        return ["this" => $this->fromQueryFace];
    }

    /**
     * convention for having the same table alias every where. E.G  "a.b" will become "this__dot_a__dot__b"
     * @param string $path
     * @param string $token the token to use for separate elements of the path. Default  $this->getDotToken() will be used
     * @return string
     */
    public function _doFQLTableName($path, $token = null, $escape = false)
    {

        if (null === $token) {
            $token = $this->dotToken;
        }

        return self::__doFQLTableNameStatic($path, $token, $escape);
    }

    /**
     * reserved for internal usage
     * @param $path
     * @param null $token
     * @return mixed|string
     */
    public static function __doFQLTableNameStatic($path, $token = null, $escape = false)
    {
        if (null===$token) {
            $token=self::$DOT_TOKEN;
        }

        if ("this"===$path || empty($path)) {
            if($escape){
                return "`this`";
            }else{
                return "this";
            }
        }

        if ( ! StringUtils::beginsWith("this.", $path)) {
            // if doesn't begin with "this." then we prepend "this."
            $path = "this.".$path;
        }

        $name = str_replace(".", $token, $path);

        if($escape){
            return "`$name`";
        }else{
            return $name;
        }
    }


    /**
     *
     * @return EntityFace
     */
    public function getBaseFace()
    {
        return $this->fromQueryFace->getFace();
    }
}