The steps I ended up using were as follows, and the comments and answers so far got me started in the right direction.
First I added a row to the "sitemap" table. Since we have multi-store set up, and because I want to keep my module store agnostic, I didn't hard code this INSERT into a MySQL migration, but just ran it on the store manually:
INSERT INTO sitemap (sitemap_type, sitemap_filename, sitemap_path, store_id)
VALUES ('people', 'people.xml', '/sitemap/', 2);
Then I overwrote the Mage_Sitemap_Model_Sitemap
model inside the global/models section in my own module's config.xml file:
<global>
<models>
<sitemap>
<rewrite>
<sitemap>Mymod_People_Model_Sitemap</sitemap>
</rewrite>
</sitemap>
</models>
</global>
This overwrites any calls to Mage_Sitemap_Model_Sitemap
site-wide with my custom model, but I didn't want to copy and paste too much code there. Using Petar Dzhambazov's suggestion, I used a conditional to defer to the parent class unless the sitemap_type
is "people".
class Mymod_People_Model_Sitemap extends Mage_Sitemap_Model_Sitemap
{
const PAGE_REFRESH_FREQUENCY = 'weekly';
const PAGE_PRIORITY = '1.0';
public function generateXml()
{
if ($this->getSitemapType() != 'people') {
return parent::generateXml();
}
$io = new Varien_Io_File();
$io->setAllowCreateFolders(true);
$io->open(array('path' => $this->getPath()));
if ($io->fileExists($this->getSitemapFilename()) && !$io->isWriteable($this->getSitemapFilename())) {
Mage::throwException(Mage::helper('sitemap')->__('File "%s" cannot be saved. Please, make sure the directory "%s" is writeable by web server.', $this->getSitemapFilename(), $this->getPath()));
}
$io->streamOpen($this->getSitemapFilename());
$io->streamWrite('<?xml version="1.0" encoding="UTF-8"?>' . "\n");
$io->streamWrite('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
$storeId = $this->getStoreId();
$date = Mage::getSingleton('core/date')->gmtDate('Y-m-d');
$baseUrl = Mage::app()->getStore($storeId)->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_LINK);
/**
* Generate people sitemap
*/
$changefreq = Mymod_People_Model_Sitemap::PAGE_REFRESH_FREQUENCY;
$priority = Mymod_People_Model_Sitemap::PAGE_PRIORITY;
$collection = Mage::getModel('people/person')->getCollection();
foreach ($collection as $item) {
$xml = sprintf('<url><loc>%s</loc><lastmod>%s</lastmod><changefreq>%s</changefreq><priority>%.1f</priority></url>',
htmlspecialchars($item->getUrl()),
$date,
$changefreq,
$priority
);
$io->streamWrite($xml);
}
unset($collection);
$io->streamWrite('</urlset>');
$io->streamClose();
$this->setSitemapTime(Mage::getSingleton('core/date')->gmtDate('Y-m-d H:i:s'));
$this->save();
return $this;
}
}
Is there a better way, that avoids copying and pasting so much from the parent class?
You cam move preferred product first by changing the search result internal sorting.
For this
1. Create a new attribute, type price, called "Preferred Rate"
2. Add to current attribute set and fill in values. For top product insert, say 99, for next 80 and so on.
4. Override the Mage_Catalog_Block_Product_List_Toolbar
block to join this attribute if the current sort order is relevance.
5. Sort by rate DESC, relevance DESC
6. You can import/export this attribute or mass change it value via admin panel
Example code:
public function setCollection($collection)
{
parent::setCollection($collection);
if ($this->getCurrentOrder() != 'relevance'){
return $this;
}
$collection->addAttributeToSort("rate", "desc");
// move to the first position
$select = $collection->getSelect();
$orders = $select->getPart(Zend_Db_Select::ORDER);
if (count($orders) > 1){
$last = array_pop($orders);
array_unshift($orders, $last);
$select->setPart(Zend_Db_Select::ORDER, $orders);
}
return $this;
}
Customer will only see relevance, and there will be no changes for catalog or sort by price/name.
Also you can consider some search extension that gives priority to some attributes, like product name. So if the search matches the name of product A and description of product B the product A goes first.
Best Answer
Out of the box, no, there's no way to exclude certain products from the sitemap generated by Magento's
Catalog -> Google Sitemap
feature.If I was going to go about doing this programmatically, modern versions of Magento (checked in the 1.7.x branch, this might be around in earlier/EE versions) use the following resource model class
to fetch a list of products.
This is not a standard Magento CRUD model, and
getCollection
does not return a collection object. Instead,getCollection
manually queries the database for these products.If I was going to implement functionality that prevented certain products from showing up in the site map, I'd try to either
A class rewrite the
getCollection
method which calls theparent::getCollection
, and then manually filters out any products from the arrayA class rewrite on
_addFilter
which calls the parent::_addFilter method, and then adds an additional WHERE clause(s) to the_select
to exclude the specific product(s). Sort of a hack, but it's the only method where you have access to the_select
object used to query the database. Ideally you'd want to have some sort of global/static flag so you only added your new WHERE clause(s) once.