public function execute($n = 1)
 {
     $routes = $this->getRoutes();
     $this->app_id = wa()->getApp();
     $category_model = new shopCategoryModel();
     $product_model = new shopProductModel();
     $page_model = new shopPageModel();
     $count = 0;
     foreach ($routes as $route) {
         $this->routing->setRoute($route);
         $domain = $this->routing->getDomain(null, true);
         $route_url = $domain . '/' . $this->routing->getRoute('url');
         if ($n == 1) {
             // categories
             $sql = "SELECT c.id,c.parent_id,c.left_key,c.url,c.full_url,c.create_datetime,c.edit_datetime\n                        FROM shop_category c\n                        LEFT JOIN shop_category_routes cr ON c.id = cr.category_id\n                        WHERE c.status = 1 AND (cr.route IS NULL OR cr.route = '" . $category_model->escape($route_url) . "')\n                        ORDER BY c.left_key";
             $categories = $category_model->query($sql)->fetchAll('id');
             $category_url = $this->routing->getUrl($this->app_id . '/frontend/category', array('category_url' => '%CATEGORY_URL%'), true);
             foreach ($categories as $c_id => $c) {
                 if ($c['parent_id'] && !isset($categories[$c['parent_id']])) {
                     unset($categories[$c_id]);
                     continue;
                 }
                 if (isset($route['url_type']) && $route['url_type'] == 1) {
                     $url = $c['url'];
                 } else {
                     $url = $c['full_url'];
                 }
                 $this->addUrl(str_replace('%CATEGORY_URL%', $url, $category_url), $c['edit_datetime'] ? $c['edit_datetime'] : $c['create_datetime'], self::CHANGE_WEEKLY, 0.6);
             }
             $main_url = $this->getUrl('');
             // pages
             $sql = "SELECT full_url, url, create_datetime, update_datetime FROM " . $page_model->getTableName() . '
                     WHERE status = 1 AND domain = s:domain AND route = s:route';
             $pages = $page_model->query($sql, array('domain' => $domain, 'route' => $route['url']))->fetchAll();
             foreach ($pages as $p) {
                 $this->addUrl($main_url . $p['full_url'], $p['update_datetime'] ? $p['update_datetime'] : $p['create_datetime'], self::CHANGE_MONTHLY, 0.6);
             }
             /**
              * @event sitemap
              * @param array $route
              * @return array $urls
              */
             $plugin_urls = wa()->event(array($this->app_id, 'sitemap'), $route);
             if ($plugin_urls) {
                 foreach ($plugin_urls as $urls) {
                     foreach ($urls as $url) {
                         $this->addUrl($url['loc'], ifset($url['lastmod'], time()), ifset($url['changefreq']), ifset($url['priority']));
                     }
                 }
             }
             // main page
             $this->addUrl($main_url, time(), self::CHANGE_DAILY, 1);
         }
         // products
         $c = $this->countProductsByRoute($route);
         if ($count + $c <= ($n - 1) * $this->limit) {
             $count += $c;
             continue;
         } else {
             if ($count >= ($n - 1) * $this->limit) {
                 $offset = 0;
             } else {
                 $offset = ($n - 1) * $this->limit - $count;
             }
             $count += $offset;
             $limit = min($this->limit, $n * $this->limit - $count);
         }
         $sql = "SELECT p.url, p.create_datetime, p.edit_datetime";
         if (isset($route['url_type']) && $route['url_type'] == 2) {
             $sql .= ', c.full_url category_url';
         }
         $sql .= " FROM " . $product_model->getTableName() . ' p';
         if (isset($route['url_type']) && $route['url_type'] == 2) {
             $sql .= " LEFT JOIN " . $category_model->getTableName() . " c ON p.category_id = c.id";
         }
         $sql .= ' WHERE p.status = 1';
         if (!empty($route['type_id'])) {
             $sql .= ' AND p.type_id IN (i:type_id)';
         }
         $sql .= ' LIMIT ' . $offset . ',' . $limit;
         $products = $product_model->query($sql, $route);
         $count += $products->count();
         $product_url = $this->routing->getUrl($this->app_id . '/frontend/product', array('product_url' => '%PRODUCT_URL%', 'category_url' => '%CATEGORY_URL%'), true);
         foreach ($products as $p) {
             if (!empty($p['category_url'])) {
                 $url = str_replace(array('%PRODUCT_URL%', '%CATEGORY_URL%'), array($p['url'], $p['category_url']), $product_url);
             } else {
                 $url = str_replace(array('%PRODUCT_URL%', '/%CATEGORY_URL%'), array($p['url'], ''), $product_url);
             }
             $this->addUrl($url, $p['edit_datetime'] ? $p['edit_datetime'] : $p['create_datetime'], self::CHANGE_MONTHLY, 0.8);
         }
         if ($count >= $n * $this->limit) {
             break;
         }
     }
 }
    /**
     * @param $current_stage
     * @param $count
     * @param $processed
     *
     * @usedby shopYandexmarketPluginRunController::step()
     */
    private function stepCategory(&$current_stage, &$count, &$processed)
    {
        static $categories = null;
        static $model;
        if ($categories === null) {
            $model = new shopCategoryModel();
            if (empty($this->data['export']['hidden_categories'])) {
                $categories = $model->getTree(0, null, false, $this->data['domain']);
            } else {
                $sql = <<<SQL
SELECT c.*
FROM shop_category c
LEFT JOIN shop_category_routes cr
ON (c.id = cr.category_id)
WHERE
(cr.route IS NULL) OR (cr.route = s:domain)
ORDER BY c.left_key
SQL;
                $categories = $model->query($sql, $this->data)->fetchAll($model->getTableId());
            }
            // экспортируется только список статических категорий, поэтому фильтруем данные
            foreach ($categories as $id => $category) {
                if ($category['type'] != shopCategoryModel::TYPE_STATIC) {
                    unset($categories[$id]);
                }
            }
            if ($current_stage) {
                $categories = array_slice($categories, $current_stage);
            }
        }
        $chunk = 100;
        while (--$chunk >= 0 && ($category = reset($categories))) {
            if (empty($category['parent_id']) || isset($this->data['categories'][$category['parent_id']])) {
                $category_xml = $this->dom->createElement("category", str_replace('&', '&amp;', $category['name']));
                $category['id'] = intval($category['id']);
                $category_xml->setAttribute('id', $category['id']);
                if ($category['parent_id']) {
                    $category_xml->setAttribute('parentId', $category['parent_id']);
                }
                $nodes = $this->dom->getElementsByTagName('categories');
                $nodes->item(0)->appendChild($category_xml);
                $this->data['categories'][$category['id']] = $category['id'];
                if (!empty($category['include_sub_categories']) && $category['right_key'] - $category['left_key'] > 1) {
                    //remap hidden subcategories
                    $descendants = $model->descendants($category['id'], true)->where('type = i:type', array('type' => shopCategoryModel::TYPE_STATIC))->fetchAll('id');
                    if ($descendants) {
                        $remap = array_fill_keys(array_map('intval', array_keys($descendants)), (int) $category['id']);
                        $this->data['categories'] += $remap;
                    }
                }
                ++$processed;
            }
            array_shift($categories);
            ++$current_stage;
        }
    }
 /**
  * Method triggered when deleting product through shopProductModel
  * @param array $product_ids
  */
 public function deleteByProducts(array $product_ids)
 {
     $category_model = new shopCategoryModel();
     foreach ($this->query("SELECT category_id, count(product_id) cnt FROM {$this->table}\n            WHERE product_id IN (" . implode(',', $product_ids) . ")\n            GROUP BY category_id") as $item) {
         $category_model->query("UPDATE " . $category_model->getTableName() . " SET count = count - {$item['cnt']}\n                WHERE id = {$item['category_id']}");
     }
     $this->deleteByField('product_id', $product_ids);
 }