[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/administrator/components/com_categories/src/Model/ -> CategoriesModel.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Administrator
   5   * @subpackage  com_categories
   6   *
   7   * @copyright   (C) 2008 Open Source Matters, Inc. <https://www.joomla.org>
   8   * @license     GNU General Public License version 2 or later; see LICENSE.txt
   9   */
  10  
  11  namespace Joomla\Component\Categories\Administrator\Model;
  12  
  13  use Joomla\CMS\Association\AssociationServiceInterface;
  14  use Joomla\CMS\Categories\CategoryServiceInterface;
  15  use Joomla\CMS\Factory;
  16  use Joomla\CMS\Language\Associations;
  17  use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
  18  use Joomla\CMS\MVC\Model\ListModel;
  19  use Joomla\Database\DatabaseQuery;
  20  use Joomla\Database\ParameterType;
  21  use Joomla\Utilities\ArrayHelper;
  22  
  23  // phpcs:disable PSR1.Files.SideEffects
  24  \defined('_JEXEC') or die;
  25  // phpcs:enable PSR1.Files.SideEffects
  26  
  27  /**
  28   * Categories Component Categories Model
  29   *
  30   * @since  1.6
  31   */
  32  class CategoriesModel extends ListModel
  33  {
  34      /**
  35       * Does an association exist? Caches the result of getAssoc().
  36       *
  37       * @var   boolean|null
  38       * @since 4.0.5
  39       */
  40      private $hasAssociation;
  41  
  42      /**
  43       * Constructor.
  44       *
  45       * @param   array                     $config   An optional associative array of configuration settings.
  46       * @param   MVCFactoryInterface|null  $factory  The factory.
  47       *
  48       * @since   1.6
  49       */
  50      public function __construct($config = array(), MVCFactoryInterface $factory = null)
  51      {
  52          if (empty($config['filter_fields'])) {
  53              $config['filter_fields'] = array(
  54                  'id', 'a.id',
  55                  'title', 'a.title',
  56                  'alias', 'a.alias',
  57                  'published', 'a.published',
  58                  'access', 'a.access', 'access_level',
  59                  'language', 'a.language', 'language_title',
  60                  'checked_out', 'a.checked_out',
  61                  'checked_out_time', 'a.checked_out_time',
  62                  'created_time', 'a.created_time',
  63                  'created_user_id', 'a.created_user_id',
  64                  'lft', 'a.lft',
  65                  'rgt', 'a.rgt',
  66                  'level', 'a.level',
  67                  'path', 'a.path',
  68                  'tag',
  69              );
  70          }
  71  
  72          if (Associations::isEnabled()) {
  73              $config['filter_fields'][] = 'association';
  74          }
  75  
  76          parent::__construct($config, $factory);
  77      }
  78  
  79      /**
  80       * Method to auto-populate the model state.
  81       *
  82       * Note. Calling getState in this method will result in recursion.
  83       *
  84       * @param   string  $ordering   An optional ordering field.
  85       * @param   string  $direction  An optional direction (asc|desc).
  86       *
  87       * @return  void
  88       *
  89       * @since   1.6
  90       */
  91      protected function populateState($ordering = 'a.lft', $direction = 'asc')
  92      {
  93          $app = Factory::getApplication();
  94  
  95          $forcedLanguage = $app->input->get('forcedLanguage', '', 'cmd');
  96  
  97          // Adjust the context to support modal layouts.
  98          if ($layout = $app->input->get('layout')) {
  99              $this->context .= '.' . $layout;
 100          }
 101  
 102          // Adjust the context to support forced languages.
 103          if ($forcedLanguage) {
 104              $this->context .= '.' . $forcedLanguage;
 105          }
 106  
 107          $extension = $app->getUserStateFromRequest($this->context . '.filter.extension', 'extension', 'com_content', 'cmd');
 108  
 109          $this->setState('filter.extension', $extension);
 110          $parts = explode('.', $extension);
 111  
 112          // Extract the component name
 113          $this->setState('filter.component', $parts[0]);
 114  
 115          // Extract the optional section name
 116          $this->setState('filter.section', (\count($parts) > 1) ? $parts[1] : null);
 117  
 118          // List state information.
 119          parent::populateState($ordering, $direction);
 120  
 121          // Force a language.
 122          if (!empty($forcedLanguage)) {
 123              $this->setState('filter.language', $forcedLanguage);
 124          }
 125      }
 126  
 127      /**
 128       * Method to get a store id based on model configuration state.
 129       *
 130       * This is necessary because the model is used by the component and
 131       * different modules that might need different sets of data or different
 132       * ordering requirements.
 133       *
 134       * @param   string  $id  A prefix for the store id.
 135       *
 136       * @return  string  A store id.
 137       *
 138       * @since   1.6
 139       */
 140      protected function getStoreId($id = '')
 141      {
 142          // Compile the store id.
 143          $id .= ':' . $this->getState('filter.extension');
 144          $id .= ':' . $this->getState('filter.search');
 145          $id .= ':' . $this->getState('filter.published');
 146          $id .= ':' . $this->getState('filter.access');
 147          $id .= ':' . $this->getState('filter.language');
 148          $id .= ':' . $this->getState('filter.level');
 149          $id .= ':' . serialize($this->getState('filter.tag'));
 150  
 151          return parent::getStoreId($id);
 152      }
 153  
 154      /**
 155       * Method to get a database query to list categories.
 156       *
 157       * @return  \Joomla\Database\DatabaseQuery
 158       *
 159       * @since   1.6
 160       */
 161      protected function getListQuery()
 162      {
 163          // Create a new query object.
 164          $db = $this->getDatabase();
 165          $query = $db->getQuery(true);
 166          $user = Factory::getUser();
 167  
 168          // Select the required fields from the table.
 169          $query->select(
 170              $this->getState(
 171                  'list.select',
 172                  'a.id, a.title, a.alias, a.note, a.published, a.access' .
 173                  ', a.checked_out, a.checked_out_time, a.created_user_id' .
 174                  ', a.path, a.parent_id, a.level, a.lft, a.rgt' .
 175                  ', a.language'
 176              )
 177          );
 178          $query->from($db->quoteName('#__categories', 'a'));
 179  
 180          // Join over the language
 181          $query->select(
 182              [
 183                  $db->quoteName('l.title', 'language_title'),
 184                  $db->quoteName('l.image', 'language_image'),
 185              ]
 186          )
 187              ->join(
 188                  'LEFT',
 189                  $db->quoteName('#__languages', 'l'),
 190                  $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.language')
 191              );
 192  
 193          // Join over the users for the checked out user.
 194          $query->select($db->quoteName('uc.name', 'editor'))
 195              ->join(
 196                  'LEFT',
 197                  $db->quoteName('#__users', 'uc'),
 198                  $db->quoteName('uc.id') . ' = ' . $db->quoteName('a.checked_out')
 199              );
 200  
 201          // Join over the asset groups.
 202          $query->select($db->quoteName('ag.title', 'access_level'))
 203              ->join(
 204                  'LEFT',
 205                  $db->quoteName('#__viewlevels', 'ag'),
 206                  $db->quoteName('ag.id') . ' = ' . $db->quoteName('a.access')
 207              );
 208  
 209          // Join over the users for the author.
 210          $query->select($db->quoteName('ua.name', 'author_name'))
 211              ->join(
 212                  'LEFT',
 213                  $db->quoteName('#__users', 'ua'),
 214                  $db->quoteName('ua.id') . ' = ' . $db->quoteName('a.created_user_id')
 215              );
 216  
 217          // Join over the associations.
 218          $assoc = $this->getAssoc();
 219  
 220          if ($assoc) {
 221              $query->select('COUNT(asso2.id)>1 as association')
 222                  ->join(
 223                      'LEFT',
 224                      $db->quoteName('#__associations', 'asso'),
 225                      $db->quoteName('asso.id') . ' = ' . $db->quoteName('a.id')
 226                      . ' AND ' . $db->quoteName('asso.context') . ' = ' . $db->quote('com_categories.item')
 227                  )
 228                  ->join(
 229                      'LEFT',
 230                      $db->quoteName('#__associations', 'asso2'),
 231                      $db->quoteName('asso2.key') . ' = ' . $db->quoteName('asso.key')
 232                  )
 233                  ->group('a.id, l.title, uc.name, ag.title, ua.name');
 234          }
 235  
 236          // Filter by extension
 237          if ($extension = $this->getState('filter.extension')) {
 238              $query->where($db->quoteName('a.extension') . ' = :extension')
 239                  ->bind(':extension', $extension);
 240          }
 241  
 242          // Filter on the level.
 243          if ($level = (int) $this->getState('filter.level')) {
 244              $query->where($db->quoteName('a.level') . ' <= :level')
 245                  ->bind(':level', $level, ParameterType::INTEGER);
 246          }
 247  
 248          // Filter by access level.
 249          if ($access = (int) $this->getState('filter.access')) {
 250              $query->where($db->quoteName('a.access') . ' = :access')
 251                  ->bind(':access', $access, ParameterType::INTEGER);
 252          }
 253  
 254          // Implement View Level Access
 255          if (!$user->authorise('core.admin')) {
 256              $groups = $user->getAuthorisedViewLevels();
 257              $query->whereIn($db->quoteName('a.access'), $groups);
 258          }
 259  
 260          // Filter by published state
 261          $published = (string) $this->getState('filter.published');
 262  
 263          if (is_numeric($published)) {
 264              $published = (int) $published;
 265              $query->where($db->quoteName('a.published') . ' = :published')
 266                  ->bind(':published', $published, ParameterType::INTEGER);
 267          } elseif ($published === '') {
 268              $query->whereIn($db->quoteName('a.published'), [0, 1]);
 269          }
 270  
 271          // Filter by search in title
 272          $search = $this->getState('filter.search');
 273  
 274          if (!empty($search)) {
 275              if (stripos($search, 'id:') === 0) {
 276                  $search = (int) substr($search, 3);
 277                  $query->where($db->quoteName('a.id') . ' = :search')
 278                      ->bind(':search', $search, ParameterType::INTEGER);
 279              } else {
 280                  $search = '%' . str_replace(' ', '%', trim($search)) . '%';
 281                  $query->extendWhere(
 282                      'AND',
 283                      [
 284                          $db->quoteName('a.title') . ' LIKE :title',
 285                          $db->quoteName('a.alias') . ' LIKE :alias',
 286                          $db->quoteName('a.note') . ' LIKE :note',
 287                      ],
 288                      'OR'
 289                  )
 290                      ->bind(':title', $search)
 291                      ->bind(':alias', $search)
 292                      ->bind(':note', $search);
 293              }
 294          }
 295  
 296          // Filter on the language.
 297          if ($language = $this->getState('filter.language')) {
 298              $query->where($db->quoteName('a.language') . ' = :language')
 299                  ->bind(':language', $language);
 300          }
 301  
 302          // Filter by a single or group of tags.
 303          $tag       = $this->getState('filter.tag');
 304          $typeAlias = $extension . '.category';
 305  
 306          // Run simplified query when filtering by one tag.
 307          if (\is_array($tag) && \count($tag) === 1) {
 308              $tag = $tag[0];
 309          }
 310  
 311          if ($tag && \is_array($tag)) {
 312              $tag = ArrayHelper::toInteger($tag);
 313  
 314              $subQuery = $db->getQuery(true)
 315                  ->select('DISTINCT ' . $db->quoteName('content_item_id'))
 316                  ->from($db->quoteName('#__contentitem_tag_map'))
 317                  ->where(
 318                      [
 319                          $db->quoteName('tag_id') . ' IN (' . implode(',', $query->bindArray($tag)) . ')',
 320                          $db->quoteName('type_alias') . ' = :typeAlias',
 321                      ]
 322                  );
 323  
 324              $query->join(
 325                  'INNER',
 326                  '(' . $subQuery . ') AS ' . $db->quoteName('tagmap'),
 327                  $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id')
 328              )
 329                  ->bind(':typeAlias', $typeAlias);
 330          } elseif ($tag = (int) $tag) {
 331              $query->join(
 332                  'INNER',
 333                  $db->quoteName('#__contentitem_tag_map', 'tagmap'),
 334                  $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id')
 335              )
 336                  ->where(
 337                      [
 338                          $db->quoteName('tagmap.tag_id') . ' = :tag',
 339                          $db->quoteName('tagmap.type_alias') . ' = :typeAlias',
 340                      ]
 341                  )
 342                  ->bind(':tag', $tag, ParameterType::INTEGER)
 343                  ->bind(':typeAlias', $typeAlias);
 344          }
 345  
 346          // Add the list ordering clause
 347          $listOrdering = $this->getState('list.ordering', 'a.lft');
 348          $listDirn = $db->escape($this->getState('list.direction', 'ASC'));
 349  
 350          if ($listOrdering == 'a.access') {
 351              $query->order('a.access ' . $listDirn . ', a.lft ' . $listDirn);
 352          } else {
 353              $query->order($db->escape($listOrdering) . ' ' . $listDirn);
 354          }
 355  
 356          // Group by on Categories for \JOIN with component tables to count items
 357          $query->group('a.id,
 358                  a.title,
 359                  a.alias,
 360                  a.note,
 361                  a.published,
 362                  a.access,
 363                  a.checked_out,
 364                  a.checked_out_time,
 365                  a.created_user_id,
 366                  a.path,
 367                  a.parent_id,
 368                  a.level,
 369                  a.lft,
 370                  a.rgt,
 371                  a.language,
 372                  l.title,
 373                  l.image,
 374                  uc.name,
 375                  ag.title,
 376                  ua.name');
 377  
 378          return $query;
 379      }
 380  
 381      /**
 382       * Method to determine if an association exists
 383       *
 384       * @return  boolean  True if the association exists
 385       *
 386       * @since   3.0
 387       */
 388      public function getAssoc()
 389      {
 390          if (!\is_null($this->hasAssociation)) {
 391              return $this->hasAssociation;
 392          }
 393  
 394          $extension = $this->getState('filter.extension');
 395  
 396          $this->hasAssociation = Associations::isEnabled();
 397          $extension = explode('.', $extension);
 398          $component = array_shift($extension);
 399          $cname = str_replace('com_', '', $component);
 400  
 401          if (!$this->hasAssociation || !$component || !$cname) {
 402              $this->hasAssociation = false;
 403  
 404              return $this->hasAssociation;
 405          }
 406  
 407          $componentObject = $this->bootComponent($component);
 408  
 409          if ($componentObject instanceof AssociationServiceInterface && $componentObject instanceof CategoryServiceInterface) {
 410              $this->hasAssociation = true;
 411  
 412              return $this->hasAssociation;
 413          }
 414  
 415          $hname = $cname . 'HelperAssociation';
 416          \JLoader::register($hname, JPATH_SITE . '/components/' . $component . '/helpers/association.php');
 417  
 418          $this->hasAssociation = class_exists($hname) && !empty($hname::$category_association);
 419  
 420          return $this->hasAssociation;
 421      }
 422  
 423      /**
 424       * Method to get an array of data items.
 425       *
 426       * @return  mixed  An array of data items on success, false on failure.
 427       *
 428       * @since   3.0.1
 429       */
 430      public function getItems()
 431      {
 432          $items = parent::getItems();
 433  
 434          if ($items != false) {
 435              $extension = $this->getState('filter.extension');
 436  
 437              $this->countItems($items, $extension);
 438          }
 439  
 440          return $items;
 441      }
 442  
 443      /**
 444       * Method to load the countItems method from the extensions
 445       *
 446       * @param   \stdClass[]  $items      The category items
 447       * @param   string       $extension  The category extension
 448       *
 449       * @return  void
 450       *
 451       * @since   3.5
 452       */
 453      public function countItems(&$items, $extension)
 454      {
 455          $parts     = explode('.', $extension, 2);
 456          $section   = '';
 457  
 458          if (\count($parts) > 1) {
 459              $section = $parts[1];
 460          }
 461  
 462          $component = Factory::getApplication()->bootComponent($parts[0]);
 463  
 464          if ($component instanceof CategoryServiceInterface) {
 465              $component->countItems($items, $section);
 466          }
 467      }
 468  
 469      /**
 470       * Manipulate the query to be used to evaluate if this is an Empty State to provide specific conditions for this extension.
 471       *
 472       * @return DatabaseQuery
 473       *
 474       * @since 4.0.0
 475       */
 476      protected function getEmptyStateQuery()
 477      {
 478          $query = parent::getEmptyStateQuery();
 479  
 480          // Get the extension from the filter
 481          $extension = $this->getState('filter.extension');
 482  
 483          $query->where($this->getDatabase()->quoteName('extension') . ' = :extension')
 484              ->bind(':extension', $extension);
 485  
 486          return $query;
 487      }
 488  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer