<?php
abstract class Core_Validate_DB_Abstract extends Core_Validate_Abstract
{
    /**
     *
     * @var Doctrine_Table
     */
    protected $_doctrineTable;

    /**
     * Name of Doctrine entity
     *
     * @var string
     */
    protected $_table;

    /**
     * Name of field we're selecting and searching for
     *
     * @var string
     */
    protected $_field;

    /**
     * Array with field / value pairs to exclude or SQL string
     *
     * @var string|array
     */
    protected $_exclude;

    /**
     *
     * @param Doctrine_Table $db
     *
     * @return Core_Validate_DB_Abstract
     */
    public function setDoctrineTable(Doctrine_Table $db)
    {
        $this->_doctrineTable = $db;

        return $this;
    }

    /**
     *
     * @return Doctrine_Table
     */
    public function getDoctrineTable()
    {
        if (null === $this->_table) {
            throw new Exception('Error fetching Doctrine table, no table set');
        }

        return Doctrine_Core::getTable($this->getTable());
    }

    /**
     *
     * @param string $table
     *
     * @return \Core_Validate_DB_Abstract
     */
    public function setTable($table)
    {
        $this->_table = $table;

        return $this;
    }

    /**
     *
     * @return string
     */
    public function getTable()
    {
        return $this->_table;
    }

    /**
     *
     * @param string $field
     *
     * @return Core_Validate_DB_Abstract
     */
    public function setField($field)
    {
        $this->_field = $field;

        return $this;
    }

    /**
     *
     * @return string
     */
    public function getField()
    {
        return $this->_field;
    }

    /**
     *
     * @param string|array $exclude
     *
     * @return Core_Validate_DB_Abstract
     */
    public function setExclude($exclude)
    {
        $this->_exclude = $exclude;

        return $this;
    }

    /**
     *
     * @return string|array
     */
    public function getExclude()
    {
        return $this->_exclude;
    }

    /**
     *
     * @param string $value
     *
     * @return array|boolean
     * @throws Exception
     * @throws InvalidArgumentException
     */
    protected function _query($value)
    {
        $dt      = $this->getDoctrineTable();

        // lookup $value set in $table.$field
        $table   = $this->getTable();
        $field   = $this->getField();

        if (empty($table) || empty($field)) {
            throw new Exception(sprintf('%s expects atleast two params, "table" and "field"', get_class($this)));
        }

        // Build query
        $query  = $dt->createQuery()
            ->select($field)
            ->andWhere($field .' = ?', $value)
        ;

        // and optionally exclude stuff with $exclude
        if (null !== ($exclude = $this->getExclude())) {
            if (is_string($exclude)) {
                // exclude is a string value the aforementioned field should not match
                $query->andWhere($exclude);
            } elseif (!is_array($exclude)) {
                // Unknown data type
                throw new InvalidArgumentException(sprintf(
                    'Error while setting exclude query, the exclude variable is of "%s" type, should be string or array',
                    gettype($exclude)
                ));
            } else {
                // exclude is in format array(field => values, field => values...)
                foreach ($exclude as $field => $values) {
                    if (is_null($values)) {
                        // Value not set?
                        continue;
                    }

                    if (is_array($values)) {
                        $query->andWhereNotIn($field, $values);
                    } else {
                        $query->andWhere($field .' != ?', $values);
                    }
                }
            }
        }

        return $query->fetchOne(array(), Doctrine_Core::HYDRATE_ARRAY);
    }
}