joomla-platform / libraries / joomla / application / component / modellist.php

<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Application
 *
 * @copyright   Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

jimport('joomla.application.component.model');

/**
 * Model class for handling lists of items.
 *
 * @package     Joomla.Platform
 * @subpackage  Application
 * @since       11.1
 */
class JModelList extends JModel
{
    /**
     * Internal memory based cache array of data.
     *
     * @var     array
     * @since   11.1
     */
    protected $cache = array();

    /**
     * Context string for the model type.  This is used to handle uniqueness
     * when dealing with the getStoreId() method and caching data structures.
     *
     * @var     string
     * @since   11.1
     */
    protected $context = null;

    /**
     * Valid filter fields or ordering.
     *
     * @var     array
     * @since   11.1
     */
    protected $filter_fields = array();

    /**
     * An internal cache for the last query used.
     *
     * @var     JDatabaseQuery
     * @since   11.1
     */
    protected $query = array();

    /**
     * Constructor.
     *
     * @param   array   An optional associative array of configuration settings.
     * @see     JController
     */
    public function __construct($config = array())
    {
        parent::__construct($config);

        // Add the ordering filtering fields white list.
        if (isset($config['filter_fields'])) {
            $this->filter_fields = $config['filter_fields'];
        }

        // Guess the context as Option.ModelName.
        if (empty($this->context)) {
            $this->context = strtolower($this->option.'.'.$this->getName());
        }
    }

    /**
     * Method to cache the last query constructed.
     *
     * This method ensures that the query is contructed only once for a given state of the model.
     *
     * @return  JDatabaseQuery
     * @since   11.1
     */
    private function _getListQuery()
    {
        // Capture the last store id used.
        static $lastStoreId;

        // Compute the current store id.
        $currentStoreId = $this->getStoreId();

        // If the last store id is different from the current, refresh the query.
        if ($lastStoreId != $currentStoreId || empty($this->query)) {
            $lastStoreId = $currentStoreId;
            $this->query = $this->getListQuery();
        }

        return $this->query;
    }

    /**
     * Method to get an array of data items.
     *
     * @return  mixed   An array of data items on success, false on failure.
     * @since   11.1
     */
    public function getItems()
    {
        // Get a storage key.
        $store = $this->getStoreId();

        // Try to load the data from internal storage.
        if (!empty($this->cache[$store])) {
            return $this->cache[$store];
        }

        // Load the list items.
        $query  = $this->_getListQuery();
        $items  = $this->_getList($query, $this->getStart(), $this->getState('list.limit'));

        // Check for a database error.
        if ($this->_db->getErrorNum()) {
            $this->setError($this->_db->getErrorMsg());
            return false;
        }

        // Add the items to the internal cache.
        $this->cache[$store] = $items;

        return $this->cache[$store];
    }

    /**
     * Method to get a JDatabaseQuery object for retrieving the data set from a database.
     *
     * @return  object  A JDatabaseQuery object to retrieve the data set.
     * @since   11.1
     */
    protected function getListQuery()
    {
        $db     = $this->getDbo();
        $query  = $db->getQuery(true);

        return $query;
    }

    /**
     * Method to get a JPagination object for the data set.
     *
     * @return  object  A JPagination object for the data set.
     * @since   11.1
     */
    public function getPagination()
    {
        // Get a storage key.
        $store = $this->getStoreId('getPagination');

        // Try to load the data from internal storage.
        if (!empty($this->cache[$store])) {
            return $this->cache[$store];
        }

        // Create the pagination object.
        jimport('joomla.html.pagination');
        $limit = (int) $this->getState('list.limit') - (int) $this->getState('list.links');
        $page = new JPagination($this->getTotal(), $this->getStart(), $limit);

        // Add the object to the internal cache.
        $this->cache[$store] = $page;

        return $this->cache[$store];
    }

    /**
     * Method to get a store id based on the model configuration state.
     *
     * This is necessary because the model is used by the component and
     * different modules that might need different sets of data or different
     * ordering requirements.
     *
     * @param   string  An identifier string to generate the store id.
     * @return  string  A store id.
     * @since   11.1
     */
    protected function getStoreId($id = '')
    {
        // Add the list state to the store id.
        $id .= ':'.$this->getState('list.start');
        $id .= ':'.$this->getState('list.limit');
        $id .= ':'.$this->getState('list.ordering');
        $id .= ':'.$this->getState('list.direction');

        return md5($this->context.':'.$id);
    }

    /**
     * Method to get the total number of items for the data set.
     *
     * @return  integer The total number of items available in the data set.
     * @since   11.1
     */
    public function getTotal()
    {
        // Get a storage key.
        $store = $this->getStoreId('getTotal');

        // Try to load the data from internal storage.
        if (!empty($this->cache[$store])) {
            return $this->cache[$store];
        }

        // Load the total.
        $query = $this->_getListQuery();
        $total = (int) $this->_getListCount((string) $query);

        // Check for a database error.
        if ($this->_db->getErrorNum()) {
            $this->setError($this->_db->getErrorMsg());
            return false;
        }

        // Add the total to the internal cache.
        $this->cache[$store] = $total;

        return $this->cache[$store];
    }

    /**
     * Method to get the starting number of items for the data set.
     *
     * @return  integer The starting number of items available in the data set.
     * @since   11.1
     */
    public function getstart()
    {
        $store = $this->getStoreId('getstart');

        // Try to load the data from internal storage.
        if (!empty($this->cache[$store])) {
            return $this->cache[$store];
        }

        $start = $this->getState('list.start');
        $limit = $this->getState('list.limit');
        $total = $this->getTotal();
        if ($start > $total - $limit) {
            $start = max(0, (int)(ceil($total / $limit) - 1) * $limit);
        }

        // Add the total to the internal cache.
        $this->cache[$store] = $start;

        return $this->cache[$store];
    }

    /**
     * Method to auto-populate the model state.
     *
     * This method should only be called once per instantiation and is designed
     * to be called on the first call to the getState() method unless the model
     * configuration flag to ignore the request is set.
     *
     * Note. Calling getState in this method will result in recursion.
     *
     * @param   string  An optional ordering field.
     * @param   string  An optional direction (asc|desc).
     * @since   11.1
     */
    protected function populateState($ordering = null, $direction = null)
    {
        // If the context is set, assume that stateful lists are used.
        if ($this->context) {
            $app = JFactory::getApplication();

            $value = $app->getUserStateFromRequest('global.list.limit', 'limit', $app->getCfg('list_limit'));
            $limit = $value;
            $this->setState('list.limit', $limit);

            $value = $app->getUserStateFromRequest($this->context.'.limitstart', 'limitstart', 0);
            $limitstart = ($limit != 0 ? (floor($value / $limit) * $limit) : 0);
            $this->setState('list.start', $limitstart);

            // Check if the ordering field is in the white list, otherwise use the incoming value.
            $value = $app->getUserStateFromRequest($this->context.'.ordercol', 'filter_order', $ordering);
            if (!in_array($value, $this->filter_fields)) {
                $value = $ordering;
                $app->setUserState($this->context.'.ordercol', $value);
            }
            $this->setState('list.ordering', $value);

            // Check if the ordering direction is valid, otherwise use the incoming value.
            $value = $app->getUserStateFromRequest($this->context.'.orderdirn', 'filter_order_Dir', $direction);
            if (!in_array(strtoupper($value), array('ASC', 'DESC', ''))) {
                $value = $direction;
                $app->setUserState($this->context.'.orderdirn', $value);
            }
            $this->setState('list.direction', $value);
        }
        else {
            $this->setState('list.start', 0);
            $this->state->set('list.limit', 0);
        }
    }

    /**
     * Gets the value of a user state variable and sets it in the session
     * This is the same as the method in JApplication except that this also can optionally
     *    force you back to the first page when a filter has changed
     *
     * @param   string  The key of the user state variable.
     * @param   string  The name of the variable passed in a request.
     * @param   string  The default value for the variable if not found. Optional.
     * @param   string  Filter for the variable, for valid values see {@link JFilterInput::clean()}. Optional.
     * @param   boolean If true, the limitstart in request is set to zero
     * @return  The request user state.
     * @since   11.1
     */
    public function getUserStateFromRequest($key, $request, $default = null, $type = 'none', $resetPage = true)
    {
        $app = JFactory::getApplication();
        $old_state = $app->getUserState($key);
        $cur_state = (!is_null($old_state)) ? $old_state : $default;
        $new_state = JRequest::getVar($request, null, 'default', $type);

        if (($cur_state != $new_state) && ($resetPage)){
            JRequest::setVar('limitstart', 0);
        }

        // Save the new value only if it is set in this request.
        if ($new_state !== null) {
            $app->setUserState($key, $new_state);
        }
        else {
            $new_state = $cur_state;
        }

        return $new_state;
    }
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.