/** * Initializes the criteria object with the slice based on the given parameter. * * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object * @param \stdClass $params Object that may contain the properties "condition", "sort", "dir", "start" and "limit" */ protected function initCriteriaSlice(\Aimeos\MW\Criteria\Iface $criteria, \stdClass $params) { if (isset($params->start) && isset($params->limit)) { $start = isset($params->start) ? $params->start : 0; $size = isset($params->limit) ? $params->limit : 1000; $criteria->setSlice($start, $size); } }
/** * Returns a sorted list of required criteria keys. * * @param \Aimeos\MW\Criteria\Iface $criteria Search criteria object * @param string[] $required List of prefixes of required search conditions * @return string[] Sorted list of criteria keys */ protected function getCriteriaKeyList(\Aimeos\MW\Criteria\Iface $criteria, array $required) { $keys = array_merge($required, $this->getCriteriaKeys($required, $criteria->getConditions())); foreach ($criteria->getSortations() as $sortation) { $keys = array_merge($keys, $this->getCriteriaKeys($required, $sortation)); } $keys = array_unique(array_merge($required, $keys)); sort($keys); return $keys; }
/** * Adds the conditions for the selected attributes to the given search filter. * * @param array $params Associative list of parameters that should be used for filtering * @param \Aimeos\MW\Criteria\Iface $filter Criteria object for searching */ protected function addAttributeFilterByParam(array $params, \Aimeos\MW\Criteria\Iface $filter) { $attrids = isset($params['f_attrid']) ? (array) $params['f_attrid'] : array(); if (!empty($attrids)) { $func = $filter->createFunction('index.attributeaggregate', array(array_keys($attrids))); $expr = array($filter->getConditions(), $filter->compare('==', $func, count($attrids))); $filter->setConditions($filter->combine('&&', $expr)); } }
/** * Adds the conditions for the selected attributes to the given search filter. * * @param array $params Associative list of parameters that should be used for filtering * @param \Aimeos\MW\Criteria\Iface $filter Criteria object for searching */ protected function addAttributeFilterByParam(array $params, \Aimeos\MW\Criteria\Iface $filter) { $attrids = array(); if (isset($params['f_attrid'])) { foreach ((array) $params['f_attrid'] as $attrid) { if ($attrid != '') { $attrids[] = (int) $attrid; } } } if (!empty($attrids)) { $func = $filter->createFunction('index.attributeaggregate', array($attrids)); $expr = array($filter->getConditions(), $filter->compare('==', $func, count($attrids))); $filter->setConditions($filter->combine('&&', $expr)); } }
/** * Search for currency items matching the given criteria. * * @param \Aimeos\MW\Criteria\Iface $search Search object * @param array $ref List of domains to fetch list items and referenced items for * @param integer &$total Number of items that are available in total * @return array List of items implementing \Aimeos\MShop\Locale\Item\Currency\Iface */ public function searchItems(\Aimeos\MW\Criteria\Iface $search, array $ref = array(), &$total = null) { $context = $this->getContext(); $config = $context->getConfig(); $dbm = $context->getDatabaseManager(); $dbname = $this->getResourceName(); $conn = $dbm->acquire($dbname); $items = array(); try { $attributes = $this->getSearchAttributes(); $types = $this->getSearchTypes($attributes); $translations = $this->getSearchTranslations($attributes); $find = array(':cond', ':order', ':start', ':size'); $replace = array($search->getConditionString($types, $translations), $search->getSortationString($types, $translations), $search->getSliceStart(), $search->getSliceSize()); /** mshop/locale/manager/currency/standard/search * Retrieves the records matched by the given criteria in the database * * Fetches the records matched by the given criteria from the attribute * database. The records must be from one of the sites that are * configured via the context item. If the current site is part of * a tree of sites, the SELECT statement can retrieve all records * from the current site and the complete sub-tree of sites. * * As the records can normally be limited by criteria from sub-managers, * their tables must be joined in the SQL context. This is done by * using the "internaldeps" property from the definition of the ID * column of the sub-managers. These internal dependencies specify * the JOIN between the tables and the used columns for joining. The * ":joins" placeholder is then replaced by the JOIN strings from * the sub-managers. * * To limit the records matched, conditions can be added to the given * criteria object. It can contain comparisons like column names that * must match specific values which can be combined by AND, OR or NOT * operators. The resulting string of SQL conditions replaces the * ":cond" placeholder before the statement is sent to the database * server. * * If the records that are retrieved should be ordered by one or more * columns, the generated string of column / sort direction pairs * replaces the ":order" placeholder. In case no ordering is required, * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/" * markers is removed to speed up retrieving the records. Columns of * sub-managers can also be used for ordering the result set but then * no index can be used. * * The number of returned records can be limited and can start at any * number between the begining and the end of the result set. For that * the ":size" and ":start" placeholders are replaced by the * corresponding values from the criteria object. The default values * are 0 for the start and 100 for the size value. * * The SQL statement should conform to the ANSI standard to be * compatible with most relational database systems. This also * includes using double quotes for table and column names. * * @param string SQL statement for searching items * @since 2014.03 * @category Developer * @see mshop/locale/manager/currency/standard/insert * @see mshop/locale/manager/currency/standard/update * @see mshop/locale/manager/currency/standard/delete * @see mshop/locale/manager/currency/standard/count */ $path = 'mshop/locale/manager/currency/standard/search'; $sql = $this->getSqlConfig($path); $results = $this->getSearchResults($conn, str_replace($find, $replace, $sql)); try { while (($row = $results->fetch()) !== false) { $items[$row['locale.currency.id']] = $this->createItemBase($row); } } catch (\Exception $e) { $results->finish(); throw $e; } if ($total !== null) { /** mshop/locale/manager/currency/standard/count * Counts the number of records matched by the given criteria in the database * * Counts all records matched by the given criteria from the attribute * database. The records must be from one of the sites that are * configured via the context item. If the current site is part of * a tree of sites, the statement can count all records from the * current site and the complete sub-tree of sites. * * As the records can normally be limited by criteria from sub-managers, * their tables must be joined in the SQL context. This is done by * using the "internaldeps" property from the definition of the ID * column of the sub-managers. These internal dependencies specify * the JOIN between the tables and the used columns for joining. The * ":joins" placeholder is then replaced by the JOIN strings from * the sub-managers. * * To limit the records matched, conditions can be added to the given * criteria object. It can contain comparisons like column names that * must match specific values which can be combined by AND, OR or NOT * operators. The resulting string of SQL conditions replaces the * ":cond" placeholder before the statement is sent to the database * server. * * Both, the strings for ":joins" and for ":cond" are the same as for * the "search" SQL statement. * * Contrary to the "search" statement, it doesn't return any records * but instead the number of records that have been found. As counting * thousands of records can be a long running task, the maximum number * of counted records is limited for performance reasons. * * The SQL statement should conform to the ANSI standard to be * compatible with most relational database systems. This also * includes using double quotes for table and column names. * * @param string SQL statement for counting items * @since 2014.03 * @category Developer * @see mshop/locale/manager/currency/standard/insert * @see mshop/locale/manager/currency/standard/update * @see mshop/locale/manager/currency/standard/delete * @see mshop/locale/manager/currency/standard/search */ $path = 'mshop/locale/manager/currency/standard/count'; $sql = $this->getSqlConfig($path); $results = $this->getSearchResults($conn, str_replace($find, $replace, $sql)); $row = $results->fetch(); $results->finish(); if ($row === false) { throw new \Aimeos\MShop\Locale\Exception('No total results value found'); } $total = $row['count']; } $dbm->release($conn, $dbname); } catch (\Exception $e) { $dbm->release($conn, $dbname); throw $e; } return $items; }
/** * Initializes the criteria object with sortations based on the given parameter. * * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object * @param \stdClass $params Object that may contain the properties "condition", "sort", "dir", "start" and "limit" */ private function initCriteriaSortations(\Aimeos\MW\Criteria\Iface $criteria, \stdClass $params) { if (isset($params->sort) && isset($params->dir)) { $sortation = array(); switch ($params->dir) { case 'ASC': $sortation[] = $criteria->sort('+', $params->sort); break; case 'DESC': $sortation[] = $criteria->sort('-', $params->sort); break; default: throw new \Aimeos\Controller\ExtJS\Exception(sprintf('Invalid sort direction "%1$s"', $params->sort)); } $criteria->setSortations($sortation); } if ($this->sort !== null) { $sort = $criteria->getSortations(); $sort[] = $criteria->sort('+', $this->sort); $criteria->setSortations($sort); } }
/** * Returns the search result of the statement combined with the given criteria. * * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection * @param \Aimeos\MW\Criteria\Iface $search Search criteria object * @param string $cfgPathSearch Path to SQL statement in configuration for searching * @param string $cfgPathCount Path to SQL statement in configuration for counting * @param string[] $required Additional search keys to add conditions for even if no conditions are available * @param integer|null $total Contains the number of all records matching the criteria if not null * @param integer $sitelevel Constant from \Aimeos\MShop\Locale\Manager\Base for defining which site IDs should be used for searching * @param array $plugins Associative list of item keys and plugin objects implementing \Aimeos\MW\Criteria\Plugin\Iface * @return \Aimeos\MW\DB\Result\Iface SQL result object for accessing the found records * @throws \Aimeos\MShop\Exception if no number of all matching records is available */ protected function searchItemsBase(\Aimeos\MW\DB\Connection\Iface $conn, \Aimeos\MW\Criteria\Iface $search, $cfgPathSearch, $cfgPathCount, array $required, &$total = null, $sitelevel = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL, array $plugins = array()) { $joins = array(); $conditions = $search->getConditions(); $attributes = $this->getSearchAttributes(); $siteIds = $this->getSiteIds($sitelevel); $keys = $this->getCriteriaKeyList($search, $required); $basekey = array_shift($required); foreach ($keys as $key) { if ($key !== $basekey) { $joins = array_merge($joins, $this->getJoins($attributes, $key)); } } $cond = $this->getSearchSiteConditions($search, $keys, $attributes, $siteIds); if ($conditions !== null) { $cond[] = $conditions; } $search = clone $search; $search->setConditions($search->combine('&&', $cond)); $types = $this->getSearchTypes($attributes); $translations = $this->getSearchTranslations($attributes); $find = array(':joins', ':cond', ':start', ':size'); $replace = array(implode("\n", array_unique($joins)), $search->getConditionString($types, $translations, $plugins), $search->getSliceStart(), $search->getSliceSize()); if (count($search->getSortations()) > 0) { $keys[] = 'orderby'; $find[] = ':order'; $replace[] = $search->getSortationString($types, $translations); $keys[] = 'columns'; $find[] = ':columns'; $replace[] = $search->getColumnString($search->getSortations(), $translations); } if ($total !== null) { $sql = new \Aimeos\MW\Template\SQL($this->getSqlConfig($cfgPathCount)); $sql->replace($find, $replace)->enable($keys); $time = microtime(true); $stmt = $conn->create($sql->str()); $results = $stmt->execute(); $row = $results->fetch(); $results->finish(); $this->context->getLogger()->log(__METHOD__ . '(' . (microtime(true) - $time) * 1000 . 'ms): SQL statement: ' . $stmt, \Aimeos\MW\Logger\Base::DEBUG); if ($row === false) { throw new \Aimeos\MShop\Exception(sprintf('Total results value not found')); } $total = (int) $row['count']; } $sql = new \Aimeos\MW\Template\SQL($this->getSqlConfig($cfgPathSearch)); $sql->replace($find, $replace)->enable($keys); $time = microtime(true); $stmt = $conn->create($sql->str()); $results = $stmt->execute(); $this->context->getLogger()->log(__METHOD__ . '(' . (microtime(true) - $time) * 1000 . 'ms): SQL statement: ' . $stmt, \Aimeos\MW\Logger\Base::DEBUG); return $results; }
/** * Re-writes the index entries for all products that are search result of given criteria * * @param \Aimeos\MW\Criteria\Iface $search Search criteria * @param array $domains List of domains to be * @param integer $size Size of a chunk of products to handle at a time */ protected function writeIndex(\Aimeos\MW\Criteria\Iface $search, array $domains, $size) { $manager = \Aimeos\MShop\Factory::createManager($this->getContext(), 'product'); $submanagers = $this->getSubManagers(); $start = 0; do { $search->setSlice($start, $size); $products = $manager->searchItems($search, $domains); $prodIds = array_keys($products); try { $this->begin(); $this->deleteItems($prodIds); foreach ($submanagers as $submanager) { $submanager->rebuildIndex($products); } $this->saveSubProducts($products); $this->commit(); } catch (\Exception $e) { $this->rollback(); throw $e; } $this->clearCache($prodIds); $count = count($products); $start += $count; } while ($count == $search->getSliceSize()); }
/** * Returns the given search filter with the conditions attached for filtering by text. * * @param \Aimeos\MW\Criteria\Iface $search Criteria object used for product search * @param string $input Search string entered by the user * @param string $listtype List type of the text associated to the product, usually "default" * @return \Aimeos\MW\Criteria\Iface Criteria object containing the conditions for searching * @since 2015.08 */ public function addIndexFilterText(\Aimeos\MW\Criteria\Iface $search, $input, $listtype = 'default') { $langid = $this->getContext()->getLocale()->getLanguageId(); $expr = array($search->compare('>', $search->createFunction('index.text.relevance', array($listtype, $langid, $input)), 0)); $expr[] = $search->getConditions(); $search->setConditions($search->combine('&&', $expr)); return $search; }
/** * Initializes the criteria object with sortations based on the given parameter * * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object * @param array $params List of criteria data with condition, sorting and paging */ private function initCriteriaSortations(\Aimeos\MW\Criteria\Iface $criteria, array $params) { if (!isset($params['sort'])) { return; } $sortation = array(); foreach ((array) $params['sort'] as $sort) { if ($sort[0] === '-') { $sortation[] = $criteria->sort('-', substr($sort, 1)); } else { $sortation[] = $criteria->sort('+', $sort); break; } } $criteria->setSortations($sortation); }
/** * Initializes the criteria object with sortations based on the given parameter * * @param \Aimeos\MW\Criteria\Iface $criteria Criteria object * @param array $params List of criteria data with condition, sorting and paging */ protected function initCriteriaSortations(\Aimeos\MW\Criteria\Iface $criteria, array $params) { if (!isset($params['sort'])) { return; } $sortation = array(); foreach (explode(',', $params['sort']) as $sort) { if ($sort[0] === '-') { $sortation[] = $criteria->sort('-', substr($sort, 1)); } else { $sortation[] = $criteria->sort('+', $sort); } } $criteria->setSortations($sortation); }
/** * Adds the conditions for searching the catalog nodes * * @param \Aimeos\MW\Criteria\Iface $search Search object * @return \Aimeos\MW\Criteria\Iface Enhanced search object */ protected function addSearchConditions(\Aimeos\MW\Criteria\Iface $search, array $catIds, $catId) { $config = $this->getContext()->getConfig(); $expr = $search->compare('==', 'catalog.parentid', $catIds); $expr = $search->combine('||', array($expr, $search->compare('==', 'catalog.id', $catId))); /** client/html/catalog/filter/tree/levels-always * The number of levels in the category tree that should be always displayed * * Usually, only the root node and the first level of the category * tree is shown in the frontend. Only if the user clicks on a * node in the first level, the page reloads and the sub-nodes of * the chosen category are rendered as well. * * Using this configuration option you can enforce the given number * of levels to be always displayed. The root node uses level 0, the * categories below level 1 and so on. * * In most cases you can set this value via the administration interface * of the shop application. In that case you often can configure the * levels individually for each catalog filter. * * @param integer Number of tree levels * @since 2014.03 * @category User * @category Developer * @see client/html/catalog/filter/tree/startid * @see client/html/catalog/filter/tree/levels-only * @see client/html/catalog/filter/tree/domains */ if (($levels = $config->get('client/html/catalog/filter/tree/levels-always')) != null) { $expr = $search->combine('||', array($expr, $search->compare('<=', 'catalog.level', $levels))); } /** client/html/catalog/filter/tree/levels-only * No more than this number of levels in the category tree should be displayed * * If the user clicks on a category node, the page reloads and the * sub-nodes of the chosen category are rendered as well. * Using this configuration option you can enforce that no more than * the given number of levels will be displayed at all. The root * node uses level 0, the categories below level 1 and so on. * * In most cases you can set this value via the administration interface * of the shop application. In that case you often can configure the * levels individually for each catalog filter. * * @param integer Number of tree levels * @since 2014.03 * @category User * @category Developer * @see client/html/catalog/filter/tree/startid * @see client/html/catalog/filter/tree/levels-always * @see client/html/catalog/filter/tree/domains */ if (($levels = $config->get('client/html/catalog/filter/tree/levels-only')) != null) { $expr = $search->combine('&&', array($expr, $search->compare('<=', 'catalog.level', $levels))); } $search->setConditions($expr); return $search; }
/** * Retrieves a list of nodes from the storage matching the given search criteria. * * @param \Aimeos\MW\Criteria\Iface $search Search criteria object * @param integer|null $id Search nodes starting at the node with the given ID * @return array List of nodes implementing \Aimeos\MW\Tree\Node\Iface */ public function searchNodes(\Aimeos\MW\Criteria\Iface $search, $id = null) { $left = 1; $right = 0x7fffffff; if ($id !== null) { $node = $this->getNodeById($id); $left = $node->left; $right = $node->right; } $types = $this->getSearchTypes($this->searchConfig); $translations = $this->getSearchTranslations($this->searchConfig); $conditions = $search->getConditionString($types, $translations); $sortations = $search->getSortationString($types, $translations); $sql = str_replace(array(':cond', ':order'), array($conditions, $sortations), $this->config['search']); $conn = $this->dbm->acquire($this->dbname); try { $stmt = $conn->create($sql); $stmt->bind(1, $left, \Aimeos\MW\DB\Statement\Base::PARAM_INT); $stmt->bind(2, $right, \Aimeos\MW\DB\Statement\Base::PARAM_INT); $result = $stmt->execute(); try { $nodes = array(); while (($row = $result->fetch()) !== false) { $nodes[$row['id']] = $this->createNodeBase($row); } } catch (\Exception $e) { $result->finish(); throw $e; } $this->dbm->release($conn, $this->dbname); } catch (\Exception $e) { $this->dbm->release($conn, $this->dbname); throw $e; } return $nodes; }
/** * Searches for site items matching the given criteria. * * @param \Aimeos\MW\Criteria\Iface $search Search criteria object * @param string[] $ref List of domains to fetch list items and referenced items for * @param integer|null &$total Number of items that are available in total * @return array List of site items implementing \Aimeos\MShop\Locale\Item\Site\Iface */ public function searchItems(\Aimeos\MW\Criteria\Iface $search, array $ref = array(), &$total = null) { $items = array(); $context = $this->getContext(); $dbm = $context->getDatabaseManager(); $dbname = $this->getResourceName(); $conn = $dbm->acquire($dbname); try { $attributes = $this->getSearchAttributes(); $types = $this->getSearchTypes($attributes); $translations = $this->getSearchTranslations($attributes); $columns = $search->getColumnString($search->getSortations(), $translations); $find = array(':cond', ':order', ':columns', ':start', ':size'); $replace = array($search->getConditionString($types, $translations), $search->getSortationString($types, $translations), $columns ? ', ' . $columns : '', $search->getSliceStart(), $search->getSliceSize()); /** mshop/locale/manager/site/standard/search/mysql * Retrieves the records matched by the given criteria in the database * * @see mshop/locale/manager/site/standard/search/ansi */ /** mshop/locale/manager/site/standard/search/ansi * Retrieves the records matched by the given criteria in the database * * Fetches the records matched by the given criteria from the attribute * database. The records must be from one of the sites that are * configured via the context item. If the current site is part of * a tree of sites, the SELECT statement can retrieve all records * from the current site and the complete sub-tree of sites. * * As the records can normally be limited by criteria from sub-managers, * their tables must be joined in the SQL context. This is done by * using the "internaldeps" property from the definition of the ID * column of the sub-managers. These internal dependencies specify * the JOIN between the tables and the used columns for joining. The * ":joins" placeholder is then replaced by the JOIN strings from * the sub-managers. * * To limit the records matched, conditions can be added to the given * criteria object. It can contain comparisons like column names that * must match specific values which can be combined by AND, OR or NOT * operators. The resulting string of SQL conditions replaces the * ":cond" placeholder before the statement is sent to the database * server. * * If the records that are retrieved should be ordered by one or more * columns, the generated string of column / sort direction pairs * replaces the ":order" placeholder. In case no ordering is required, * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/" * markers is removed to speed up retrieving the records. Columns of * sub-managers can also be used for ordering the result set but then * no index can be used. * * The number of returned records can be limited and can start at any * number between the begining and the end of the result set. For that * the ":size" and ":start" placeholders are replaced by the * corresponding values from the criteria object. The default values * are 0 for the start and 100 for the size value. * * The SQL statement should conform to the ANSI standard to be * compatible with most relational database systems. This also * includes using double quotes for table and column names. * * @param string SQL statement for searching items * @since 2014.03 * @category Developer * @see mshop/locale/manager/site/standard/insert/ansi * @see mshop/locale/manager/site/standard/update/ansi * @see mshop/locale/manager/site/standard/delete/ansi * @see mshop/locale/manager/site/standard/count/ansi * @see mshop/locale/manager/site/standard/newid/ansi */ $path = 'mshop/locale/manager/site/standard/search'; $sql = $this->getSqlConfig($path); $results = $this->getSearchResults($conn, str_replace($find, $replace, $sql)); try { while (($row = $results->fetch()) !== false) { $config = $row['locale.site.config']; if (($row['locale.site.config'] = json_decode($row['locale.site.config'], true)) === null) { $msg = sprintf('Invalid JSON as result of search for ID "%2$s" in "%1$s": %3$s', 'mshop_locale.config', $row['locale.site.id'], $config); $this->getContext()->getLogger()->log($msg, \Aimeos\MW\Logger\Base::WARN); } $items[$row['locale.site.id']] = $this->createItemBase($row); } } catch (\Exception $e) { $results->finish(); throw $e; } if ($total !== null) { $total = $this->getTotal($conn, $find, $replace); } $dbm->release($conn, $dbname); } catch (\Exception $e) { $dbm->release($conn, $dbname); throw $e; } return $items; }