<?php
/**
 * Article Dao
 *
 * @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
 */

/**
 * Article Dao class based on the Zend_Db_Table  
 *
 * @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
 */
class Dao_Article extends Dao
{
    /** table name */
    const TABLE_NAME = "article";

    /* format for the text constansts, these corresponds with the article :: format 
     * column   
     */  
    const FORMAT_HTML = "html"; 
    const FORMAT_WIKI = "wiki";

    /**
     * Paginator if pagination is used
     * @var Zend_Paginator
     */
    private $_paginator;
    
    /** table name
     * @var string
     */
    protected $_name = self::TABLE_NAME;

    /** primary key column
     * @var string
     */
    protected $_primary = "id";

    /** reference map for the relationship
     * @var array
     */
    protected $_referenceMap = array(
        'Author' => array(
            'columns' => 'author_id',
            'refTableClass' => 'Dao_Author',
            'refColumns' => 'id'
        )
    );

    /**
     * Get all articles, with some extended information like comments count, author
     * name etc. Has the pagination support.
     *
     * @param int $pageNo current page number, first page has number 1
     * @param int $perPageItems number of items per page
     * @param array $extendInfo what extend information we want, supported keys are
     *  - comment : for the comment count
     *  - author : for the author username
     * @return array|Zend_Paginator the sequential array of rows with article properties
     * is returned if no pagination is used, Zend_Paginator otherwise
     */
    public function fetchAllItems($pageNo=null,$perPageItems=null,$extendInfo=null)
    {
        $commentT=Dao_Comment::TABLE_NAME;
        $authorT=Dao_Author::TABLE_NAME;

        $select=$this->getAdapter()->select()
            ->from($this->_name)
            ->order("$this->_name.posted desc");

        if (is_array($extendInfo)) {
            $select->group("$this->_name.id");
            if (in_array("comment",$extendInfo))
                $select->joinLeft($commentT,"$commentT.article_id = $this->_name.id",array("commentCount"=>"count($commentT.id)"));
            if (in_array("author",$extendInfo))
                $select->joinLeft($authorT,"$authorT.id = $this->_name.author_id",array("authorName"=>"username"));
        }

        if (!($pageNo && $perPageItems)) {
            return $select->query()->fetchAll();
        }

        $this->_paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbSelect($select));
        $this->_paginator->setCurrentPageNumber($pageNo);
        $this->_paginator->setItemCountPerPage($perPageItems);
        
        return $this->_paginator;
    }

    /**
     * Gets paginator. Paginator is initialized if the pagination is used,
     * see {@link Dao_Article::getAll()}
     * @return Zend_Paginator
     */
    public function getPaginator()
    {
        return $this->_paginator;
    }

    /**
     * Returns month statistic for the articles.
     * @return array result table as an array with columns
     *  - article counts
     *  - month name
     *  - year
     */
    public function getMonthStat()
    {
        $select=$this->getAdapter()->select()
            ->from($this->_name,
                array(
                    "artcnt"=>"count(*)",
                    "yearMonth"=>"EXTRACT(YEAR_MONTH FROM posted)",
                    "year"=>"YEAR(posted)",
                    "month"=>"MONTH(posted)"
                ))
                ->group("yearMonth")
                ->order("year desc")
                ->order("month desc");

        return $select->query()->fetchAll();
    }

    /**
     * Return month statistic for the next month, only statistic for a month with
     * some articles are considered.
     *
     * E.g. if we have some articles in the march, no articles in the april, some
     * in the may. We call getNextMonthStat for the march, may statistic is returned.
     * @param int $year (1970 - 2038 :-))
     * @param int $month (1 - 12)
     * @return array|null null if no next non article empty month exists
     */
    public function getNextMonthStat($year,$month)
    {
        if ($month<10) $monthStr="0".$month;
        else $monthStr=$month;

        $select=$this->getAdapter()->select()
            ->from($this->_name,
                array(
                    "artcnt"=>"count(*)",
                    "yearMonth"=>"EXTRACT(YEAR_MONTH FROM posted)",
                    "year"=>"YEAR(posted)",
                    "month"=>"MONTH(posted)"
                ))
                ->group("yearMonth")
                ->where("posted > (LAST_DAY(\"$year-$monthStr-01\") + INTERVAL '23:59:59' HOUR_SECOND)")
                ->order("posted asc");

        return $this->getAdapter()->fetchRow($select);
    }

    /**
     * Return month statistic for the next month, only statistic for a month with
     * some articles are considered.
     *
     * E.g. if we have some articles in the march, no articles in the april, some
     * in the may. We call getNextMonthStat for the march, may statistic is returned.
     * @param int $year (1970 - 2038 :-))
     * @param int $month (1 - 12)
     * @return array|null null if no previous non article empty month exists
     */
    public function getPrevMonthStat($year,$month)
    {
        if ($month<10) $monthStr="0".$month;
        else $monthStr=$month;

        $select=$this->getAdapter()->select()
            ->from($this->_name,
                array(
                    "artcnt"=>"count(*)",
                    "yearMonth"=>"EXTRACT(YEAR_MONTH FROM posted)",
                    "year"=>"YEAR(posted)",
                    "month"=>"MONTH(posted)"
                ))
                ->group("yearMonth")
                ->where("posted < \"$year-$monthStr-01\"")
                ->order("posted desc");

        return $this->getAdapter()->fetchRow($select);
    }

    /**
     * Gets count of articles for the specific month in the specific year.
     * @param int $year (1970-1038 :-))
     * @param int $month (1-12)
     * @param int $day (1-31)
     * @returns int
     */
    public function getArchiveItemsCount($year,$month,$day=null)
    {
        $sql="SELECT count(*)"
            . "FROM  $this->_name "
            . "WHERE YEAR(posted)=$year AND MONTH(posted)=$month";

        if ($day) $sql.=" AND DAY(posted)=$day";

        return $this->getAdapter()->fetchOne($sql);
    }

    /**
     * Returns article from the archive, it means articles for the the specific
     * month, year an date.
     * Just proxy for the dao method call. A pagination is supported.
     * @param int $year (1970-1038 :-))
     * @param int $month (1-12)
     * @param int $day (1-31)
     * @param int $pageNo current page number, firts page has no. 1
     * @param int $perPageItems items per page
     * @return array|Zend_Paginator result table as array if no pagination required
     * Zend_Pagination otherwise
     */
    public function getArchiveItems($year,$month,$day=null,$pageNo=null,
        $perPageItems=null,$columns="*")
    {
        $select = $this->getAdapter()->select()
            ->from($this->_name,$columns)
            ->where("YEAR(posted)=$year")
            ->where("MONTH(posted)=$month")
            ->order("posted desc");

        if ($day) $select->where("DAY(posted)=$day");

        if (!($pageNo && $perPageItems)) {
            return $select->query()->fetchAll();
        }

        $this->_paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbSelect($select));
        $this->_paginator->setCurrentPageNumber($pageNo);
        $this->_paginator->setItemCountPerPage($perPageItems);

        return $this->_paginator;
    }

    /**
     * Finds article by a title url.
     * @param string $titleUrl
     * @return null|Zend_Db_Table_Row_Abstract
     * @throws new Zend_Db_Exception when some database operation fails
     */
    public function findByTitleUrl($titleUrl)
    {
        $whereSQL=$this->getAdapter()->quoteInto('titleURL = ?',$titleUrl);
        return $this->fetchRow($whereSQL);
    }

    /**
     * Gets previous article by posted date, one older article.
     * @param string $currentPosted datetime string in the mysql datime format
     * e.g. 2007-11-27 13:43:08
     * @return null|Zend_Db_Table_Row_Abstract
     */
    public function getPrevious($currentPosted)
    {
        return $this->fetchRow("posted < '$currentPosted'","posted desc");
    }

    /**
     * Gets next article by posted date, one younger article.
     * @param string $currentPosted datetime string in the mysql datime format
     * e.g. 2007-11-27 13:43:08
     * @return null|Zend_Db_Table_Row_Abstract
     */
    public function getNext($currentPosted)
    {
        return $this->fetchRow("posted > '$currentPosted'","posted asc");
    }

    /**
     * Update view count
     * @param int $articleId article Id
     * @param int $newCount new view count, if it's null, current view count
     * is incremented
     */
    public function updateViewCount($articleId,$newCount=null)
    {
        $where = $this->getAdapter()->quoteInto('id = ?',$articleId);
        if ($newCount) {
            $this->update(array("viewCount"=>$newCount),$where);
        } else {
            $this->update(array("viewCount"=>new Zend_Db_Expr('viewCount+1')),$where);
        }
    }
}
