php - URL 重写 OpenCart 产品 SEO

标签 php .htaccess url mod-rewrite opencart

我想从我的 opencart 网上商店重写我的产品 url。 Opencart 本身有一个非常糟糕的 seo 实现。我已经更新了 seo 实现,以便能够对多个类别使用相同的关键字,请参阅:Opencart duplicate URL keywords 但这仅适用于类别。对于我认为的产品,我只需要一个 htaccess 重写规则。

原始网址如下所示:

http://domain.com/index.php?route=product/product&path=25_28_93&product_id=1759

我的网址现在看起来像这样:

http://domain.com/In-Stock/Retreaded-Tires/Agricultural?product_id=1759

如您所见,类别确实已经发生了变化。

我希望它是这样的:

http://domain.com/In-Stock/Retreaded-Tires/Agricultural/1759/1050-50R32-Mega-X-Bib

然后为了分页(在一个类别中)我有这个 url:

http://domain.com/index.php?route=product/category&path=36_70_67&page=2

我已经把它变成了:

http://domain.com/Tire-Retreading/Equalizing/&page=2

但我希望这样

http://domain.com/Tire-Retreading/Equalizing/2

我的 htaccess 文件如下所示:

Options +FollowSymlinks
Options -Indexes
<FilesMatch "(?i)((\.tpl|\.ini|\.log|(?<!robots)\.txt))">
 Order deny,allow
 Deny from all
</FilesMatch>
RewriteEngine On
RewriteBase /
RewriteRule ^sitemap.xml$ index.php?route=feed/google_sitemap [L]
RewriteRule ^googlebase.xml$ index.php?route=feed/google_base [L]
RewriteRule ^download/(.*) /index.php?route=error/not_found [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]

我的 seo_url.php 文件稍作改动,如下所示:

public function index() {
    $this->load->model('catalog/category');
    // Add rewrite to url class
    if ($this->config->get('config_seo_url')) {
        $this->url->addRewrite($this);
    }

    // Decode URL
    if (isset($this->request->get['_route_'])) {
        $parts = explode('/', $this->request->get['_route_']);

        // remove any empty arrays from trailing
        if (utf8_strlen(end($parts)) == 0) {
            array_pop($parts);
        }

        $categories = array();

        for ($i = 0; $i < count($parts); $i++) {
            $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE keyword = '" . $this->db->escape($parts[$i]) . "'");

            if ($query->num_rows) {
                $url = explode('=', $query->row['query']);

                if ($url[0] == 'product_id') {
                    $this->request->get['product_id'] = $url[1];
                }

                if ($url[0] == 'category_id') {
                    $categories[$i] = $this->model_catalog_category->getCategory($url[1]);

                    if (!isset($this->request->get['path'])) {
                        $this->request->get['path'] = $categories[$i]['category_id'];
                    } else {
                        foreach ($query->rows as $row) {
                            $url = explode('=', $row['query']);
                            $category_id = $url[1];

                            $category = $this->model_catalog_category->getCategory($category_id);

                            if ($category['parent_id'] == $categories[$i - 1]['category_id']) {
                                $this->request->get['path'] .= '_' . $category['category_id'];
                            }
                        }
                    }
                }

                if ($url[0] == 'manufacturer_id') {
                    $this->request->get['manufacturer_id'] = $url[1];
                }

                if ($url[0] == 'information_id') {
                    $this->request->get['information_id'] = $url[1];
                }

                if ($query->row['query'] && $url[0] != 'information_id' && $url[0] != 'manufacturer_id' && $url[0] != 'category_id' && $url[0] != 'product_id') {
                    $this->request->get['route'] = $query->row['query'];
                }

            } else {
                $this->request->get['route'] = 'error/not_found';

                break;
            }
        }

        if (!isset($this->request->get['route'])) {
            if (isset($this->request->get['product_id'])) {
                $this->request->get['route'] = 'product/product';
            } elseif (isset($this->request->get['path'])) {
                $this->request->get['route'] = 'product/category';
            } elseif (isset($this->request->get['manufacturer_id'])) {
                $this->request->get['route'] = 'product/manufacturer/info';
            } elseif (isset($this->request->get['information_id'])) {
                $this->request->get['route'] = 'information/information';
            }
        }

        if (isset($this->request->get['route'])) {
            return new Action($this->request->get['route']);
        }
    }
}

public function rewrite($link) {
    $url_info = parse_url(str_replace('&amp;', '&', $link));

    $url = '';

    $data = array();

    parse_str($url_info['query'], $data);

    foreach ($data as $key => $value) {
        if (isset($data['route'])) {
            if (($data['route'] == 'product/product' && $key == 'product_id') || (($data['route'] == 'product/manufacturer/info' || $data['route'] == 'product/product') && $key == 'manufacturer_id') || ($data['route'] == 'information/information' && $key == 'information_id')) {
                $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = '" . $this->db->escape($key . '=' . (int)$value) . "'");

                if ($query->num_rows && $query->row['keyword']) {
                    $url .= '/' . $query->row['keyword'];

                    unset($data[$key]);
                }
            } elseif ($key == 'path') {
                $categories = explode('_', $value);

                foreach ($categories as $category) {
                    $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = 'category_id=" . (int)$category . "'");

                    if ($query->num_rows && $query->row['keyword']) {
                        $url .= '/' . $query->row['keyword'];
                    } else {
                        $url = '';

                        break;
                    }
                }

                unset($data[$key]);
            } else  {
                $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE `query` = '" .$data['route'] . "'");

                if ($query->num_rows && $query->row['keyword']) {
                    $url .= '/' . $query->row['keyword'];

                    unset($data[$key]);
                }
            }
        }
    }

    if ($url) {
        unset($data['route']);

        $query = '';

        if ($data) {
            foreach ($data as $key => $value) {
                $query .= '&' . rawurlencode((string)$key) . '=' . rawurlencode((string)$value);
            }

            if ($query) {
                $query = '?' . str_replace('&', '&amp;', trim($query, '&'));
            }
        }

        return $url_info['scheme'] . '://' . $url_info['host'] . (isset($url_info['port']) ? ':' . $url_info['port'] : '') . str_replace('/index.php', '', $url_info['path']) . $url . $query;
    } else {
        return $link;
    }
}

分页代码是这样的:

class Pagination {
    public $total = 0;
    public $page = 1;
    public $limit = 20;
    public $num_links = 8;
    public $url = '';
    public $text_first = '|&lt;';
    public $text_last = '&gt;|';
    public $text_next = '&gt;';
    public $text_prev = '&lt;';

    public function render() {
        $total = $this->total;

        if ($this->page < 1) {
            $page = 1;
        } else {
            $page = $this->page;
        }

        if (!(int)$this->limit) {
            $limit = 10;
        } else {
            $limit = $this->limit;
        }

        $num_links = $this->num_links;
        $num_pages = ceil($total / $limit);

        $this->url = str_replace('%7Bpage%7D', '{page}', $this->url);

        $output = '<ul class="pagination">';

        if ($page > 1) {
            $output .= '<li><a href="' . str_replace('{page}', 1, $this->url) . '">' . $this->text_first . '</a></li>';
            $output .= '<li><a href="' . str_replace('{page}', $page - 1, $this->url) . '">' . $this->text_prev . '</a></li>';
        }

        if ($num_pages > 1) {
            if ($num_pages <= $num_links) {
                $start = 1;
                $end = $num_pages;
            } else {
                $start = $page - floor($num_links / 2);
                $end = $page + floor($num_links / 2);

                if ($start < 1) {
                    $end += abs($start) + 1;
                    $start = 1;
                }

                if ($end > $num_pages) {
                    $start -= ($end - $num_pages);
                    $end = $num_pages;
                }
            }

            for ($i = $start; $i <= $end; $i++) {
                if ($page == $i) {
                    $output .= '<li class="active"><span>' . $i . '</span></li>';
                } else {
                    $output .= '<li><a href="' . str_replace('{page}', $i, $this->url) . '">' . $i . '</a></li>';
                }
            }
        }

        if ($page < $num_pages) {
            $output .= '<li><a href="' . str_replace('{page}', $page + 1, $this->url) . '">' . $this->text_next . '</a></li>';
            $output .= '<li><a href="' . str_replace('{page}', $num_pages, $this->url) . '">' . $this->text_last . '</a></li>';
        }

        $output .= '</ul>';

        if ($num_pages > 1) {
            return $output;
        } else {
            return '';
        }
    }
}

编辑:

我的所有页面都重定向到 http://domain.com/index.php 从那里它决定使用路由参数的目录/文件。 所以 route=product/product 告诉我们去目录 product 中的 product.php。目录 product 还包含 categories.php,它通向一条路线:route=product/category

字符串中的 Path 变量表示类别的 ID。在我的示例中,25 代表 In-Stock。而 25_28 代表 In-Stock/Retreaded-Tires 等。

product_id 变量表示产品的相应 ID。

page 变量表示分页,用于类别中的产品列表。这个列表可以有一个可变的长度,因为它计算一个类别中有多少产品以及他应该在一页上显示多少。 所以如果原始 url 有 &page=2 和路由 route=product/category 它应该像这样制作一个 url:http://domain.com/轮胎翻新/均衡/2

最佳答案

我仍然觉得添加的解释令人困惑,而且在我看来您不太了解 URL 重写的工作原理,所以我将尝试解释一些 URL 重写基础知识

您可以通过 URL 重写将(向用户显示的 URL 的)翻译“漂亮”URL您的实际的、长的段、“不漂亮”的 URL(使用变量来提供某些内容)。为此,您可以使用正则表达式来匹配漂亮的片段,然后将预定义值或那些匹配的值提供给您的 php 变量。

因此,您首先要做的是弄清楚您漂亮的 URL 应该是什么样子,以及它们的各个部分如何转化为您的变量。 (尽管您当然也可以使用任意数量的新变量,然后您可以转换这些变量的值并将其分配给 php 脚本中已经存在或预定义的变量。)

你举了这个例子: http://example.com/Tire-Retreading/Equalizing/2

此 URL 似乎由您要转换为变量的三个部分组成:

  • 轮胎翻新(可变路线)
  • 均衡(可变路径)
  • 2(变量页面)

您必须构建您的 RegEx 以根据各个段的所有可能拼写方式匹配您的段,包括允许的任何特殊字符等。完成后,您可以将匹配的值传递给您的变量使用反向引用(或者您可以定义您自己的值以供您的变量使用)。

使用反向引用,你可以这样使用:

RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/([0-9]+)$ index.php?route=$1&path=$2&page=$3 [NC,L]

您必须将用于段匹配的单个 RegEx(如 [A-Za-z-]+)放在圆括号中,以便能够将结果分配给您的 php 变量$1, $2 等形式

根据是否还应允许用户浏览您的产品/类别或路径的“概览”页面,您可能必须从可能的最小“漂亮”URL 开始重写到最长的 URL。

例如

RewriteRule ^([A-Za-z-]+)$ $1/ [NC,R]
RewriteRule ^([A-Za-z-]+)/$ index.php?route=$1 [NC,L]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)$ $1/$2/ [NC,R]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/$ index.php?route=$1&path=$2 [NC,L]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/([0-9]+)$ $1/$2/$3/ [NC,R]
RewriteRule ^([A-Za-z-]+)/([A-Za-z-]+)/([0-9]+)/$ index.php?route=$1&path=$2&page=$3 [NC,L]

域本身的重写在所有“路径”重写之前进行。

您还必须弄清楚需要使用哪些标志(末尾方括号中的部分),这意味着您必须继续阅读 URL rewriting更详细。在上面的示例中,用户键入的所有不以斜杠结尾的 URL 都会自动重定向(这就是方括号中的 R 所代表的含义)到相同的 URL,但添加了斜杠。因此,http://example.com/blah 将重定向到 http://example.com/blah/

根据您提供的信息,您的 URL 将以两个可能的部分之一开头:

http://example.com/In-Stock/...

对比

http://example.com/Product-or-Category/....

因此,您必须格外小心,不要混淆这两者。如果 In-Stock 被汇集到它自己的变量中并且总是拼写完全一样,您可能希望让您的第一个规则处理它并仅在以后使用 RegEx 来匹配产品/类别(所有重写规则都按时间顺序处理,一个接一个)。

希望对您有所帮助。

关于php - URL 重写 OpenCart 产品 SEO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33278642/

相关文章:

php - 使用 htaccess 和 godaddy 将 HTTP 重定向到 HTTPS

php - PDO 未在数据库中插入数据

php - jQuery 自动完成

php - htaccess 删除 www 并重定向到访问的 url

ruby-on-rails - 在 Ruby on Rails 路由中将 url 作为参数传递

jquery - 如何通过向 URL 传递参数来使用 jQuery 刷新页面

javascript - 谷歌驱动器公共(public)

php - 在方法内部调用构造函数

php - 如何在 OpenCart 中创建自定义的 SEO 友好 URL?

apache - 当我访问 HTTPS 时,网站一直重定向到 HTTP,原因不明