<?php
/**
 * Common blog database searching functionality. 
 *
 * @package koubel homepage
 * @author Miroslav Kubelik (koubel@volny.cz)
 * @copyright Copyright (c) 2007-2009 Miroslav Kubelik
 * @license http://opensource.org/licenses/gpl-license.php GNU GPL
 */

/**
 * Common blog database searching functionality. 
 *
 * @package koubel homepage
 * @author Miroslav Kubelik (koubel@volny.cz)
 * @copyright Copyright (c) 2007-2009 Miroslav Kubelik
 * @license http://opensource.org/licenses/gpl-license.php GNU GPL
 */
abstract class Blog_Model_Search extends HomeWebApp_Model_Search
{
    /**
     * Factory method for the searching for different things.
     *
     * @param string $searchObjType name for the search class, which we want create. If is
     * empty, name is loaded from the session.
     * @return Search search instance reference
     */
    public static function factorySearch($searchObjType = "")
    {
        self::_logMessage(__METHOD__.": searchObjType: $searchObjType");

        //starts session, if necessary, don't start session in the cli environment
        if (php_sapi_name() != 'cli')
            self::$_searchSession = new Zend_Session_Namespace(self::SESSION_NAME);

        $className = !empty($searchObjType) ? $searchObjType : "";
        if (empty($className)) {
            if (self::$_searchSession->searchClassName) {
                $className = self::$_searchSession->searchClassName;
            }
        }

        if (empty($className))
            throw new RuntimeException("Cannot create search object, empty className");

        switch ($className) {
            case "SearchBlogArticle" :
                self::_logMessage("create Blog_Model_Search_Article");
                return new Blog_Model_Search_Article();
            break;
            case "SearchBlogComment" :
                self::_logMessage("create Blog_Model_Search_Comment");
                return new Blog_Model_Search_Comment();
            break;
            case "SearchBlogTrackBack" :
                self::_logMessage("create Blog_Model_Search_TrackBack");
                return new Blog_Model_Search_TrackBack();
            break;
            default:
                throw new Exception("Cannot create search object, unknow classname: $className");
        }
    }

    /**
     * Builds a SQL search command.
     * @param bool $withPages use LIMIT for pagination
     * @param bool $countOnly SQL for count of rows will be returned
     * @return Zend_Db_Select
     */
    protected function _buildSQL($countOnly=false)
    {
        $select = HomeWebApp_Model_Dao::getDefaultAdapter()->select();
        
        //columns and from parts
        $select->from(implode(",",$this->_fromTables),
            $countOnly ? $this->_buildCountSql() : "*"
        );

        //where part
        $this->_sqlWhereCond($select);

        //add group by aggregation columns
        //don't add group by if we make distint count select
        if (!empty($this->_groupByColums) && !$countOnly) {
            $select->group($this->_groupByColums);
        }

        //add statements for ordering
        $this->_buildOrderSQL($select);

        //self::log("SQL for search: $sql");
        return $select;
    }

    /**
     * Builds string for the count function.
     * @return string
     */
    private function _buildCountSQL()
    {
        $countParams = empty($this->_countDistinct) ? "(*)" : "(distinct ".implode(",",$this->_countDistinct).")";
        return "count" . $countParams;
    }

    /**
     * Builds string for the ORDER statement in the SQL command.
     * @return string
     */
    protected function _buildOrderSQL(Zend_Db_Select $select)
    {
        throw new RuntimeException(__METHOD__ . " isn't implemented");
    }

    /**
     * Builds string for a condition after WHERE in SQL command.
     * Conditions are in the array from the {@link _getSqlCondition()} 
     * @return string
     */
    abstract protected function _sqlWhereCond(Zend_Db_Select $select);

    /**
     * Returns set of rows, which are matched by "code" conditions. 
     * 
     * It is used in
     * that case where we cannot use the condition via SQL database. Match criteria 
     * are specified via {@link _rowMatch()} method.
     *  
     * @return array searched rows
     */
    protected function _selectRowsByCode()
    {
        $select = $this->_buildSQL();

        //all searched items - result of sql query with filter by requested
        //text
        $searchItems = array();
        $stmt = $select->query();
        while ($row = $stmt->fetch()) {
            if ($this->_rowMatch($row)) {
                $searchItems[]=$row;
            }
        }

        //works only for mysqli adapter
        //$stmt->close();

        if (!$this->_usePagination()) {
            return $searchItems;
        }

        $this->_paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($searchItems));
        $this->_paginator->setCurrentPageNumber($this->_pageNumber);
        $this->_paginator->setItemCountPerPage($this->_perPageItems);

        return $this->_paginator;
    }

    /** Checks if specific row corresponds with our search criteria.
     * @param mixed row
     * @return bool 
     * @throws RuntimeException when this method isn't overloaded in the descendant 
     */ 
    protected function _rowMatch($row)
    {
        throw new RuntimeException("_rowMatch function isn't implemented");
    }
}
