[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/administrator/components/com_associations/src/Model/ -> AssociationsModel.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Administrator
   5   * @subpackage  com_associations
   6   *
   7   * @copyright   (C) 2017 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\Associations\Administrator\Model;
  12  
  13  use Joomla\CMS\Factory;
  14  use Joomla\CMS\Language\Text;
  15  use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
  16  use Joomla\CMS\MVC\Model\ListModel;
  17  use Joomla\CMS\Table\Table;
  18  use Joomla\Component\Associations\Administrator\Helper\AssociationsHelper;
  19  use Joomla\Database\Exception\ExecutionFailureException;
  20  use Joomla\Database\ParameterType;
  21  
  22  // phpcs:disable PSR1.Files.SideEffects
  23  \defined('_JEXEC') or die;
  24  // phpcs:enable PSR1.Files.SideEffects
  25  
  26  /**
  27   * Methods supporting a list of article records.
  28   *
  29   * @since  3.7.0
  30   */
  31  class AssociationsModel extends ListModel
  32  {
  33      /**
  34       * Override parent constructor.
  35       *
  36       * @param   array                $config   An optional associative array of configuration settings.
  37       * @param   MVCFactoryInterface  $factory  The factory.
  38       *
  39       * @see     \Joomla\CMS\MVC\Model\BaseDatabaseModel
  40       * @since   3.7
  41       */
  42      public function __construct($config = array(), MVCFactoryInterface $factory = null)
  43      {
  44          if (empty($config['filter_fields'])) {
  45              $config['filter_fields'] = array(
  46                  'id',
  47                  'title',
  48                  'ordering',
  49                  'itemtype',
  50                  'language',
  51                  'association',
  52                  'menutype',
  53                  'menutype_title',
  54                  'level',
  55                  'state',
  56                  'category_id',
  57                  'category_title',
  58                  'access',
  59                  'access_level',
  60              );
  61          }
  62  
  63          parent::__construct($config, $factory);
  64      }
  65  
  66      /**
  67       * Method to auto-populate the model state.
  68       *
  69       * Note. Calling getState in this method will result in recursion.
  70       *
  71       * @param   string  $ordering   An optional ordering field.
  72       * @param   string  $direction  An optional direction (asc|desc).
  73       *
  74       * @return  void
  75       *
  76       * @since   3.7.0
  77       */
  78      protected function populateState($ordering = 'ordering', $direction = 'asc')
  79      {
  80          $app = Factory::getApplication();
  81  
  82          $forcedLanguage = $app->input->get('forcedLanguage', '', 'cmd');
  83          $forcedItemType = $app->input->get('forcedItemType', '', 'string');
  84  
  85          // Adjust the context to support modal layouts.
  86          if ($layout = $app->input->get('layout')) {
  87              $this->context .= '.' . $layout;
  88          }
  89  
  90          // Adjust the context to support forced languages.
  91          if ($forcedLanguage) {
  92              $this->context .= '.' . $forcedLanguage;
  93          }
  94  
  95          // Adjust the context to support forced component item types.
  96          if ($forcedItemType) {
  97              $this->context .= '.' . $forcedItemType;
  98          }
  99  
 100          $this->setState('itemtype', $this->getUserStateFromRequest($this->context . '.itemtype', 'itemtype', '', 'string'));
 101          $this->setState('language', $this->getUserStateFromRequest($this->context . '.language', 'language', '', 'string'));
 102  
 103          $this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string'));
 104          $this->setState('filter.state', $this->getUserStateFromRequest($this->context . '.filter.state', 'filter_state', '', 'cmd'));
 105          $this->setState('filter.category_id', $this->getUserStateFromRequest($this->context . '.filter.category_id', 'filter_category_id', '', 'cmd'));
 106          $this->setState('filter.menutype', $this->getUserStateFromRequest($this->context . '.filter.menutype', 'filter_menutype', '', 'string'));
 107          $this->setState('filter.access', $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', '', 'string'));
 108          $this->setState('filter.level', $this->getUserStateFromRequest($this->context . '.filter.level', 'filter_level', '', 'cmd'));
 109  
 110          // List state information.
 111          parent::populateState($ordering, $direction);
 112  
 113          // Force a language.
 114          if (!empty($forcedLanguage)) {
 115              $this->setState('language', $forcedLanguage);
 116          }
 117  
 118          // Force a component item type.
 119          if (!empty($forcedItemType)) {
 120              $this->setState('itemtype', $forcedItemType);
 121          }
 122      }
 123  
 124      /**
 125       * Method to get a store id based on model configuration state.
 126       *
 127       * This is necessary because the model is used by the component and
 128       * different modules that might need different sets of data or different
 129       * ordering requirements.
 130       *
 131       * @param   string  $id  A prefix for the store id.
 132       *
 133       * @return  string  A store id.
 134       *
 135       * @since  3.7.0
 136       */
 137      protected function getStoreId($id = '')
 138      {
 139          // Compile the store id.
 140          $id .= ':' . $this->getState('itemtype');
 141          $id .= ':' . $this->getState('language');
 142          $id .= ':' . $this->getState('filter.search');
 143          $id .= ':' . $this->getState('filter.state');
 144          $id .= ':' . $this->getState('filter.category_id');
 145          $id .= ':' . $this->getState('filter.menutype');
 146          $id .= ':' . $this->getState('filter.access');
 147          $id .= ':' . $this->getState('filter.level');
 148  
 149          return parent::getStoreId($id);
 150      }
 151  
 152      /**
 153       * Build an SQL query to load the list data.
 154       *
 155       * @return  \Joomla\Database\DatabaseQuery|boolean
 156       *
 157       * @since  3.7.0
 158       */
 159      protected function getListQuery()
 160      {
 161          $type         = null;
 162  
 163          list($extensionName, $typeName) = explode('.', $this->state->get('itemtype'), 2);
 164  
 165          $extension = AssociationsHelper::getSupportedExtension($extensionName);
 166          $types     = $extension->get('types');
 167  
 168          if (\array_key_exists($typeName, $types)) {
 169              $type = $types[$typeName];
 170          }
 171  
 172          if (\is_null($type)) {
 173              return false;
 174          }
 175  
 176          // Create a new query object.
 177          $user     = Factory::getUser();
 178          $db       = $this->getDatabase();
 179          $query    = $db->getQuery(true);
 180  
 181          $details = $type->get('details');
 182  
 183          if (!\array_key_exists('support', $details)) {
 184              return false;
 185          }
 186  
 187          $support = $details['support'];
 188  
 189          if (!\array_key_exists('fields', $details)) {
 190              return false;
 191          }
 192  
 193          $fields = $details['fields'];
 194  
 195          // Main query.
 196          $query->select($db->quoteName($fields['id'], 'id'))
 197              ->select($db->quoteName($fields['title'], 'title'))
 198              ->select($db->quoteName($fields['alias'], 'alias'));
 199  
 200          if (!\array_key_exists('tables', $details)) {
 201              return false;
 202          }
 203  
 204          $tables = $details['tables'];
 205  
 206          foreach ($tables as $key => $table) {
 207              $query->from($db->quoteName($table, $key));
 208          }
 209  
 210          if (!\array_key_exists('joins', $details)) {
 211              return false;
 212          }
 213  
 214          $joins = $details['joins'];
 215  
 216          foreach ($joins as $join) {
 217              $query->join($join['type'], $db->quoteName($join['condition']));
 218          }
 219  
 220          // Join over the language.
 221          $query->select($db->quoteName($fields['language'], 'language'))
 222              ->select($db->quoteName('l.title', 'language_title'))
 223              ->select($db->quoteName('l.image', 'language_image'))
 224              ->join(
 225                  'LEFT',
 226                  $db->quoteName('#__languages', 'l'),
 227                  $db->quoteName('l.lang_code') . ' = ' . $db->quoteName($fields['language'])
 228              );
 229          $extensionNameItem = $extensionName . '.item';
 230  
 231          // Join over the associations.
 232          $query->select('COUNT(' . $db->quoteName('asso2.id') . ') > 1 AS ' . $db->quoteName('association'))
 233              ->join(
 234                  'LEFT',
 235                  $db->quoteName('#__associations', 'asso'),
 236                  $db->quoteName('asso.id') . ' = ' . $db->quoteName($fields['id'])
 237                  . ' AND ' . $db->quoteName('asso.context') . ' = :context'
 238              )
 239              ->join(
 240                  'LEFT',
 241                  $db->quoteName('#__associations', 'asso2'),
 242                  $db->quoteName('asso2.key') . ' = ' . $db->quoteName('asso.key')
 243              )
 244              ->bind(':context', $extensionNameItem);
 245  
 246          // Prepare the group by clause.
 247          $groupby = array(
 248              $fields['id'],
 249              $fields['title'],
 250              $fields['alias'],
 251              $fields['language'],
 252              'l.title',
 253              'l.image',
 254          );
 255  
 256          // Select author for ACL checks.
 257          if (!empty($fields['created_user_id'])) {
 258              $query->select($db->quoteName($fields['created_user_id'], 'created_user_id'));
 259  
 260              $groupby[] = $fields['created_user_id'];
 261          }
 262  
 263          // Select checked out data for check in checkins.
 264          if (!empty($fields['checked_out']) && !empty($fields['checked_out_time'])) {
 265              $query->select($db->quoteName($fields['checked_out'], 'checked_out'))
 266                  ->select($db->quoteName($fields['checked_out_time'], 'checked_out_time'));
 267  
 268              // Join over the users.
 269              $query->select($db->quoteName('u.name', 'editor'))
 270                  ->join(
 271                      'LEFT',
 272                      $db->quoteName('#__users', 'u'),
 273                      $db->quoteName('u.id') . ' = ' . $db->quoteName($fields['checked_out'])
 274                  );
 275  
 276              $groupby[] = 'u.name';
 277              $groupby[] = $fields['checked_out'];
 278              $groupby[] = $fields['checked_out_time'];
 279          }
 280  
 281          // If component item type supports ordering, select the ordering also.
 282          if (!empty($fields['ordering'])) {
 283              $query->select($db->quoteName($fields['ordering'], 'ordering'));
 284  
 285              $groupby[] = $fields['ordering'];
 286          }
 287  
 288          // If component item type supports state, select the item state also.
 289          if (!empty($fields['state'])) {
 290              $query->select($db->quoteName($fields['state'], 'state'));
 291  
 292              $groupby[] = $fields['state'];
 293          }
 294  
 295          // If component item type supports level, select the level also.
 296          if (!empty($fields['level'])) {
 297              $query->select($db->quoteName($fields['level'], 'level'));
 298  
 299              $groupby[] = $fields['level'];
 300          }
 301  
 302          // If component item type supports categories, select the category also.
 303          if (!empty($fields['catid'])) {
 304              $query->select($db->quoteName($fields['catid'], 'catid'));
 305  
 306              // Join over the categories.
 307              $query->select($db->quoteName('c.title', 'category_title'))
 308                  ->join(
 309                      'LEFT',
 310                      $db->quoteName('#__categories', 'c'),
 311                      $db->quoteName('c.id') . ' = ' . $db->quoteName($fields['catid'])
 312                  );
 313  
 314              $groupby[] = 'c.title';
 315              $groupby[] = $fields['catid'];
 316          }
 317  
 318          // If component item type supports menu type, select the menu type also.
 319          if (!empty($fields['menutype'])) {
 320              $query->select($db->quoteName($fields['menutype'], 'menutype'));
 321  
 322              // Join over the menu types.
 323              $query->select($db->quoteName('mt.title', 'menutype_title'))
 324                  ->select($db->quoteName('mt.id', 'menutypeid'))
 325                  ->join(
 326                      'LEFT',
 327                      $db->quoteName('#__menu_types', 'mt'),
 328                      $db->quoteName('mt.menutype') . ' = ' . $db->quoteName($fields['menutype'])
 329                  );
 330  
 331              $groupby[] = 'mt.title';
 332              $groupby[] = 'mt.id';
 333              $groupby[] = $fields['menutype'];
 334          }
 335  
 336          // If component item type supports access level, select the access level also.
 337          if (\array_key_exists('acl', $support) && $support['acl'] == true && !empty($fields['access'])) {
 338              $query->select($db->quoteName($fields['access'], 'access'));
 339  
 340              // Join over the access levels.
 341              $query->select($db->quoteName('ag.title', 'access_level'))
 342                  ->join(
 343                      'LEFT',
 344                      $db->quoteName('#__viewlevels', 'ag'),
 345                      $db->quoteName('ag.id') . ' = ' . $db->quoteName($fields['access'])
 346                  );
 347  
 348              $groupby[] = 'ag.title';
 349              $groupby[] = $fields['access'];
 350  
 351              // Implement View Level Access.
 352              if (!$user->authorise('core.admin', $extensionName)) {
 353                  $groups = $user->getAuthorisedViewLevels();
 354                  $query->whereIn($db->quoteName($fields['access']), $groups);
 355              }
 356          }
 357  
 358          // If component item type is menus we need to remove the root item and the administrator menu.
 359          if ($extensionName === 'com_menus') {
 360              $query->where($db->quoteName($fields['id']) . ' > 1')
 361                  ->where($db->quoteName('a.client_id') . ' = 0');
 362          }
 363  
 364          // If component item type is category we need to remove all other component categories.
 365          if ($typeName === 'category') {
 366              $query->where($db->quoteName('a.extension') . ' = :extensionname')
 367                  ->bind(':extensionname', $extensionName);
 368          } elseif ($typeNameExploded = explode('.', $typeName)) {
 369              if (\count($typeNameExploded) > 1 && array_pop($typeNameExploded) === 'category') {
 370                  $section = implode('.', $typeNameExploded);
 371                  $extensionNameSection = $extensionName . '.' . $section;
 372                  $query->where($db->quoteName('a.extension') . ' = :extensionsection')
 373                      ->bind(':extensionsection', $extensionNameSection);
 374              }
 375          }
 376  
 377          // Filter on the language.
 378          if ($language = $this->getState('language')) {
 379              $query->where($db->quoteName($fields['language']) . ' = :language')
 380                  ->bind(':language', $language);
 381          }
 382  
 383          // Filter by item state.
 384          $state = $this->getState('filter.state');
 385  
 386          if (is_numeric($state)) {
 387              $state = (int) $state;
 388              $query->where($db->quoteName($fields['state']) . ' = :state')
 389                  ->bind(':state', $state, ParameterType::INTEGER);
 390          } elseif ($state === '') {
 391              $query->whereIn($db->quoteName($fields['state']), [0, 1]);
 392          }
 393  
 394          // Filter on the category.
 395          $baselevel = 1;
 396  
 397          if ($categoryId = $this->getState('filter.category_id')) {
 398              $categoryTable = Table::getInstance('Category', 'JTable');
 399              $categoryTable->load($categoryId);
 400              $baselevel = (int) $categoryTable->level;
 401  
 402              $lft = (int) $categoryTable->lft;
 403              $rgt = (int) $categoryTable->rgt;
 404              $query->where($db->quoteName('c.lft') . ' >= :lft')
 405                  ->where($db->quoteName('c.rgt') . ' <= :rgt')
 406                  ->bind(':lft', $lft, ParameterType::INTEGER)
 407                  ->bind(':rgt', $rgt, ParameterType::INTEGER);
 408          }
 409  
 410          // Filter on the level.
 411          if ($level = $this->getState('filter.level')) {
 412              $queryLevel = ((int) $level + (int) $baselevel - 1);
 413              $query->where($db->quoteName('a.level') . ' <= :alevel')
 414                  ->bind(':alevel', $queryLevel, ParameterType::INTEGER);
 415          }
 416  
 417          // Filter by menu type.
 418          if ($menutype = $this->getState('filter.menutype')) {
 419              $query->where($db->quoteName($fields['menutype']) . ' = :menutype2')
 420                  ->bind(':menutype2', $menutype);
 421          }
 422  
 423          // Filter by access level.
 424          if ($access = $this->getState('filter.access')) {
 425              $access = (int) $access;
 426              $query->where($db->quoteName($fields['access']) . ' = :access')
 427                  ->bind(':access', $access, ParameterType::INTEGER);
 428          }
 429  
 430          // Filter by search in name.
 431          if ($search = $this->getState('filter.search')) {
 432              if (stripos($search, 'id:') === 0) {
 433                  $search = (int) substr($search, 3);
 434                  $query->where($db->quoteName($fields['id']) . ' = :searchid')
 435                      ->bind(':searchid', $search, ParameterType::INTEGER);
 436              } else {
 437                  $search = '%' . str_replace(' ', '%', trim($search)) . '%';
 438                  $query->where('(' . $db->quoteName($fields['title']) . ' LIKE :title'
 439                      . ' OR ' . $db->quoteName($fields['alias']) . ' LIKE :alias)')
 440                      ->bind(':title', $search)
 441                      ->bind(':alias', $search);
 442              }
 443          }
 444  
 445          // Add the group by clause
 446          $query->group($db->quoteName($groupby));
 447  
 448          // Add the list ordering clause
 449          $listOrdering  = $this->state->get('list.ordering', 'id');
 450          $orderDirn     = $this->state->get('list.direction', 'ASC');
 451  
 452          $query->order($db->escape($listOrdering) . ' ' . $db->escape($orderDirn));
 453  
 454          return $query;
 455      }
 456  
 457      /**
 458       * Delete associations from #__associations table.
 459       *
 460       * @param   string  $context  The associations context. Empty for all.
 461       * @param   string  $key      The associations key. Empty for all.
 462       *
 463       * @return  boolean  True on success.
 464       *
 465       * @since  3.7.0
 466       */
 467      public function purge($context = '', $key = '')
 468      {
 469          $app   = Factory::getApplication();
 470          $db    = $this->getDatabase();
 471          $query = $db->getQuery(true)->delete($db->quoteName('#__associations'));
 472  
 473          // Filter by associations context.
 474          if ($context) {
 475              $query->where($db->quoteName('context') . ' = :context')
 476                  ->bind(':context', $context);
 477          }
 478  
 479          // Filter by key.
 480          if ($key) {
 481              $query->where($db->quoteName('key') . ' = :key')
 482                  ->bind(':key', $key);
 483          }
 484  
 485          $db->setQuery($query);
 486  
 487          try {
 488              $db->execute();
 489          } catch (ExecutionFailureException $e) {
 490              $app->enqueueMessage(Text::_('COM_ASSOCIATIONS_PURGE_FAILED'), 'error');
 491  
 492              return false;
 493          }
 494  
 495          $app->enqueueMessage(
 496              Text::_((int) $db->getAffectedRows() > 0 ? 'COM_ASSOCIATIONS_PURGE_SUCCESS' : 'COM_ASSOCIATIONS_PURGE_NONE'),
 497              'message'
 498          );
 499  
 500          return true;
 501      }
 502  
 503      /**
 504       * Delete orphans from the #__associations table.
 505       *
 506       * @param   string  $context  The associations context. Empty for all.
 507       * @param   string  $key      The associations key. Empty for all.
 508       *
 509       * @return  boolean  True on success
 510       *
 511       * @since  3.7.0
 512       */
 513      public function clean($context = '', $key = '')
 514      {
 515          $app   = Factory::getApplication();
 516          $db    = $this->getDatabase();
 517          $query = $db->getQuery(true)
 518              ->select($db->quoteName('key') . ', COUNT(*)')
 519              ->from($db->quoteName('#__associations'))
 520              ->group($db->quoteName('key'))
 521              ->having('COUNT(*) = 1');
 522  
 523          // Filter by associations context.
 524          if ($context) {
 525              $query->where($db->quoteName('context') . ' = :context')
 526                  ->bind(':context', $context);
 527          }
 528  
 529          // Filter by key.
 530          if ($key) {
 531              $query->where($db->quoteName('key') . ' = :key')
 532                  ->bind(':key', $key);
 533          }
 534  
 535          $db->setQuery($query);
 536  
 537          $assocKeys = $db->loadObjectList();
 538  
 539          $count = 0;
 540  
 541          // We have orphans. Let's delete them.
 542          foreach ($assocKeys as $value) {
 543              $query->clear()
 544                  ->delete($db->quoteName('#__associations'))
 545                  ->where($db->quoteName('key') . ' = :valuekey')
 546                  ->bind(':valuekey', $value->key);
 547  
 548              $db->setQuery($query);
 549  
 550              try {
 551                  $db->execute();
 552              } catch (ExecutionFailureException $e) {
 553                  $app->enqueueMessage(Text::_('COM_ASSOCIATIONS_DELETE_ORPHANS_FAILED'), 'error');
 554  
 555                  return false;
 556              }
 557  
 558              $count += (int) $db->getAffectedRows();
 559          }
 560  
 561          $app->enqueueMessage(
 562              Text::_($count > 0 ? 'COM_ASSOCIATIONS_DELETE_ORPHANS_SUCCESS' : 'COM_ASSOCIATIONS_DELETE_ORPHANS_NONE'),
 563              'message'
 564          );
 565  
 566          return true;
 567      }
 568  }


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