Search code examples
magento-1.4magento-1.5magento

How to change the URL identifier of a custom module from backend i.e. system configuration


I want to give admin the option to change the URL identifier of MyCustomModule from backend.

E.g.: www.mydomain.com/identifier

What I did is the following:

In etc/system.xml

<identifier translate="label">
    <label>SELF URL Identifier</label>
    <frontend_type>text</frontend_type>
**<backend_model>press/config_identifier</backend_model>** // edited after answer
    <sort_order>1</sort_order>
    <show_in_default>1</show_in_default>
    <show_in_website>1</show_in_website>
    <show_in_store>1</show_in_store>
    <comment>(eg: domain.com/identifier)</comment>
</identifier>

In helper/data.php

public function getUrl($identifier = null)
{

    if (is_null($identifier)) {
        $url = Mage::getUrl('').self::getListIdentifier();
    } else {
        //$url = Mage::getUrl(self::getListIdentifier()).$identifier;
**$url = Mage::getUrl(self::getListIdentifier(), array('identifier' => $identifier,'_use_rewrites'=>true)); //edited
        }**
    return $url;
}

after that i created a model file identifier.php :

class FME_Press_Model_Config_Identifier extends Mage_Core_Model_Config_Data

{

    protected function _afterSave()

    {

        if ($this->isValueChanged()) {

            $path = $this->getValue();



            // for each $store and $id combination...



            Mage::getModel('core/url_rewrite')

                ->loadByIdPath('press/'.$store.'/'.$identifier)

                ->setRequestPath($path.'/'.$identifier)

                ->save();

        }

    }

}

in config.xml i wrote this:

<events>
    <controller_front_init_routers>
        <observers>
            <press>
                <type>singleton</type>
                <class>FME_Pres_Controller_Router</class>
                <method>initControllerRouters</method>
            </press>
        </observers>
    </controller_front_init_routers>
</events>

and also this is present in my file, m not sure whether it is relevant :

    <adminhtml>
        <args>
            <modules>
                <FME_Press_Override before="Mage_Adminhtml">FME_Press_Override_Admin</FME_Press_Override>
            </modules>
        </args>
    </adminhtml>

NOTE: I had been told to make some changes in Controller/Router.php but I don't know what changes to make.

If you want I can add that code also?

Now, what else should I do?


Solution

  • I feel changing the application's router is entirely the wrong approach to take. It is messy and can be easily broken if another module overrode it for a similar purpose. The clean way is with URL rewrites.

    You want it to be alterable so you cannot use a fixed XML based rewrite. Instead let's look at the built in rewrite system.

    First in your module's etc/config.xml file set up a normal controller.

    <frontend>
        <routers>
            <MyCustomModule>
                <use>standard</use>
                <args>
                    <module>Example_MyCustomModule</module>
                    <frontName>customlist</frontName>
                </args>
            </MyCustomModule>
        </routers>
    </frontend>
    

    Here the front name used is customlist, that will always work and shouldn't conflict with any other front name, the rewritten name shall be in addition to this. Now whenever you generate an URL (perhaps in a helper function) you do so to this apparently fixed front name.

    $url = Mage::getUrl('customlist', array(
        'id' => $id,    // 'id' will get used in the "target path" later
        '_use_rewrites' => true
    ));
    

    Note that the variable identifier ($id) is passed to the getUrl function rather than simply appending to it's result. If the function returns an URL with a query (&) or fragment (#) the identifier could have been appended to the wrong part.

    The next step is to create rewrite records for every possible combination of identifier and store. You probably have a finite number of lists so this is possible, perhaps identifiers are particular to stores so only need to be defined once each. Either loop through all your lists in an installer script or have each list create rewrites when it is saved.

    $path = Mage::getStoreConfig('custom/config/identifier', $storeId);
    // Change 'custom/config/identifier' to match the path used in system.xml
    
    $rewrite = Mage::getModel('core/url_rewrite')
                   ->loadByIdPath('customlist/'.$store.'/'.$id);
    if ($rewrite->getId()) {
        // A rewrite already exists, you might want to skip creating another
        continue;
    }
    
    Mage::getModel('core/url_rewrite')
        ->setStoreId($storeId)
        ->setIsSystem(true) // set to false to allow admin to edit directly
        ->setOptions('RP') // Redirect Permanent 301
        ->setIdPath('customlist/'$storeId.'/'.$id) // should never change
        ->setTargetPath('customlist/index/index/id/'.$id) // what gets used
        ->setRequestPath($path.'/'.$id) // the path used in the browser
        ->save();
    

    So now if the admin sets the URL path to be "foo/bar" and requests the page "www.mydomain.com/foo/bar/3" it will be rewritten to "customlist/index/index/id/3" and the method Example_MyCustomModule_IndexController::indexAction() will be called. The file containing that will of course be app/code/local/Example/MyCustomModule/controllers/IndexController.php and the 3 value is retrieved there:

    public function indexAction()
    {
        $id = $this->getRequest()->getParam('id'); // 'id' was specified in getUrl()
        // use $id here...
    }
    

    It should work by now but what if a list is removed? The rewrites need to be updated for each store. Models have a _beforeDelete method, override it for your list objects.

    protected function _beforeDelete()
    {
        Mage::getModel('core/url_rewrite')
            ->loadByIdPath('customlist/'.$storeId.'/'.$this->getId())
            ->delete();
        return parent::_beforeDelete();
    }
    

    Similarly they need to be updated to match changes in configuration.

    etc/system.xml

    <identifier translate="label">
        <label>SELF URL Identifier</label>
        <frontend_type>text</frontend_type>
        <backend_model>myCustomModule/config_identifier</backend_model>
        ...
    </identifier>
    

    Model/Config/Identifier.php

    class Example_MyCustomModule_Model_Config_Identifier
        extends Mage_Core_Model_Config_Data
    {
        protected function _afterSave()
        {
            if ($this->isValueChanged()) {
                $path = $this->getValue();
                // for each $store and $id combination...
                Mage::getModel('core/url_rewrite')
                    ->loadByIdPath('customlist/'.$store.'/'.$id)
                    ->setRequestPath($path.'/'.$id)
                    ->save();
            }
        }
    }