MVC Developers Part 5 – Config Resources

The following is part of a longer series on Magento aimed at developers familiar with PHP MVC development. Although each article can be read stand alone, each article is based on concepts and code covered in previous articles. If you are confused, especially to catch up on the earlier work first.

Last time we created a model for a weblog. At the moment, we have our CREATE TABLE statements directly against the database. This time we will setup a resource for our module that will create the table for us. We also create an upgrade script for our module already installed one module will operate. The steps we have to be

1. Add our config Setup Resource
2. Take our resource class file
3. Make our install script
4. Take our upgrade script

Adding the Setup Resource

So let’s continue with the blog module, we have before. In our section <resources />, add the following

<resources>
    <!-- ... -->
    <weblog_setup>
        <setup>
            <module>Alanstormdotcom_Weblog</module>
            <class>Alanstormdotcom_Weblog_Model_Resource_Mysql4_Setup</class>
        </setup>
        <connection>
            <use>core_setup</use>
        </connection>
    </weblog_setup>
    <!-- ... -->
</resources>

The tag <weblog_setup> will be used to identify this Setup Resource. It is encouraged, but not necessary that you use modelname_setup naming convention. The <module> Alanstormdotcom_Weblog </ section> tag block should contain Packagename_Modulename of your module. Finally <class> Alanstormdotcom_Weblog_Model_Resource_Mysql4_Setup </ class> contains the name of the class that we will create for our Resource Setup. For simple setup script is not necessary to create a custom class, but by doing so yourself more flexibility down the line.

After adding the above section to your config, clear your cache and try Magento Magento every page of your site to load. You see something like an exception

Fatal error: Class 'Alanstormdotcom_Weblog_Model_Resource_Mysql4_Setup' not found in

Magento has just tried to get the class you specified in your config to instantiate, but could not find him. You want the file to make the following content.

File: app/code/local/Alanstormdotcom/Weblog/Model/Resource/Mysql4/Setup.php

class Alanstormdotcom_Weblog_Model_Resource_Mysql4_Setup extends Mage_Core_Model_Resource_Setup {
}

Now, each page re-loading your Magento site. The exception should be made, your page should load as expected.

Creating our Installer Script

Next we want our installer script. This is the script that each CREATE TABLE SQL or other code that needs to be conducted will include our module to initialize.

First, take a look at the config.xml file

<modules>
    <Alanstormdotcom_Weblog>
        <version>0.1.0</version>
    </Alanstormdotcom_Weblog>
</modules>

This section is required in all config.xml files and identify the module and the version number. Your installer script name will be based on that version number. The following account of the current version of the module is 0.1.0.

Create the following file in the following location

File: app/code/local/Alanstormdotcom/Weblog/sql/weblog_setup/mysql4-install-0.1.0.php

echo 'Running This Upgrade: '.get_class($this)."\n <br /> \n";
die("Exit for now");  

Weblog_setup the portion of the path must match the tag you created in the config.xml file (<weblog_setup />). 0.1.0 The part of the filename must match the start version of your module. Clear your cache and Magento loading a page in your Magento site and you should see something like this

Running This Upgrade: Alanstormdotcom_Weblog_Model_Resource_Mysql4_Setup
Exit for now
 ...

This means that your update script ran. Eventually we put our SQL scripts update, but for now we are going to concentrate on the installation mechanism itself. Remove the “die” statement of your script so it looks like the following

echo 'Running This Upgrade: '.get_class($this)."\n <br /> \n";

Reload your page. You should upgrade your message at the top of the page. Reload again, and your page should be displayed as normal.

Resource Versions

Magento’s Resources Setup can just drop your scripts to install (and upgrade scripts, which we get in a bit) on the server, and have automated control system. You can have your database migration scripts stored in the system in a consistent format.

Using your favorite database client, take a look at the table of core_setup

mysql> select * from core_resource;
+-------------------------+---------+
| code                    | version |
+-------------------------+---------+
| adminnotification_setup | 1.0.0   |
| admin_setup             | 0.7.1   |
| amazonpayments_setup    | 0.1.2   |
| api_setup               | 0.8.1   |
| backup_setup            | 0.7.0   |
| bundle_setup            | 0.1.7   |
| catalogindex_setup      | 0.7.10  |
| cataloginventory_setup  | 0.7.5   |
| catalogrule_setup       | 0.7.7   |
| catalogsearch_setup     | 0.7.6   |
| catalog_setup           | 0.7.69  |
| checkout_setup          | 0.9.3   |
| chronopay_setup         | 0.1.0   |
| cms_setup               | 0.7.8   |
| compiler_setup          | 0.1.0   |
| contacts_setup          | 0.8.0   |
| core_setup              | 0.8.13  |
| cron_setup              | 0.7.1   |
| customer_setup          | 0.8.11  |
| cybermut_setup          | 0.1.0   |
| cybersource_setup       | 0.7.0   |
| dataflow_setup          | 0.7.4   |
| directory_setup         | 0.8.5   |
| downloadable_setup      | 0.1.14  |
| eav_setup               | 0.7.13  |
| eway_setup              | 0.1.0   |
| flo2cash_setup          | 0.1.1   |
| giftmessage_setup       | 0.7.2   |
| googleanalytics_setup   | 0.1.0   |
| googlebase_setup        | 0.1.1   |
| googlecheckout_setup    | 0.7.3   |
| googleoptimizer_setup   | 0.1.2   |
| ideal_setup             | 0.1.0   |
| log_setup               | 0.7.6   |
| newsletter_setup        | 0.8.0   |
| oscommerce_setup        | 0.8.10  |
| paybox_setup            | 0.1.3   |
| paygate_setup           | 0.7.0   |
| payment_setup           | 0.7.0   |
| paypaluk_setup          | 0.7.0   |
| paypal_setup            | 0.7.2   |
| poll_setup              | 0.7.2   |
| productalert_setup      | 0.7.2   |
| protx_setup             | 0.1.0   |
| rating_setup            | 0.7.2   |
| reports_setup           | 0.7.7   |
| review_setup            | 0.7.4   |
| salesrule_setup         | 0.7.7   |
| sales_setup             | 0.9.38  |
| sendfriend_setup        | 0.7.2   |
| shipping_setup          | 0.7.0   |
| sitemap_setup           | 0.7.2   |
| strikeiron_setup        | 0.9.1   |
| tag_setup               | 0.7.2   |
| tax_setup               | 0.7.8   |
| usa_setup               | 0.7.0   |
| weblog_setup            | 0.1.0   |
| weee_setup              | 0.13    |
| wishlist_setup          | 0.7.4   |
+-------------------------+---------+
59 rows in set (0.00 sec)

This table contains a list of all installed modules, together with the installed version number. You can see our module near the end

| weblog_setup            | 0.1.0   | 

This is how Magento not know how to re-run your script on the second and all successive page loads. The weblog_setup is already installed, so it will not be updated. If you wish to re-run your installer script (useful if you develop), but the row to remove your module from this table. Let’s do that now, and actually add to our SQL table creation. So first, run the following SQL.

DELETE from core_resource where code = 'weblog_setup';

We also want the table that we created manually in the previous article to be dropped.

DROP TABLE blog_posts;

Then add the following code on your setup script.

$installer = $this;
$installer->startSetup();
$installer->run("
    CREATE TABLE `{$installer->getTable('weblog/blogpost')}` (
      `blogpost_id` int(11) NOT NULL auto_increment,
      `title` text,
      `post` text,
      `date` datetime default NULL,
      `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
      PRIMARY KEY  (`blogpost_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    INSERT INTO `{$installer->getTable('weblog/blogpost')}` VALUES (1,'My New Title','This is a blog post','2009-07-01 00:00:00','2009-07-02 23:12:30');
");
$installer->endSetup();

Clear your cache and reload Magento every page in the system. You need a new blog_posts table with a single row.

Anatomy of a Setup Script

So, let go AOS on the script line by line. First, there is, aos this (or is that $ this?)

$installer = $this;

Each installation script runs from the context of a Setup Resource class, the class you created above. This means that any reference to $ this from a script, a reference to an object instantiated from this class. Although not necessary, most setup scripts in the core modules will alias a variable called $ this installer, that’s what we did. Although not necessary, the Convention and the AOS always best to follow the Convention, unless you have a good reason for breaking it.

Then you, all see our book-ended questions are the following two method calls.

$installer->startSetup();
//...
$installer->endSetup();

If you take a look at the Mage_Core_Model_Resource_Setup class in app / code / core / Mage / Core / Model / Resource / setup.php (which your class inherits from setup) you can see that these are a few basic SQL setup to do

  public function startSetup()
    {
        $this->_conn->multi_query("SET SQL_MODE='';
SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @[email protected]@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
");

        return $this;
    }

    public function endSetup()
    {
        $this->_conn->multi_query("
SET SQL_MODE=IFNULL(@OLD_SQL_MODE,'');
SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS,0);
");
        return $this;
    }

Finally, there is, to aos call the run method

$installer->run(...);

a string with the SQL required to accept your database table (s). Use as many questions, separated by a semicolon. You can also probably the next

$installer->getTable('weblog/blogpost')

The getTable method can be sent in a Model Magento URI and get the table name. Although not necessary, using this method to ensure that your script will keep running even if someone changes the name of their table in the configuration file. Mage_Core_Model_Resource_Setup the class contains many useful helper methods like this. The best way to familiarize yourself with everything possible aos is the study of the installation scripts used by the Magento core modules.

Module Upgrades

So, that’s how a script that will setup your initial database tables, but what if you want the structure of an existing module to change? Magento setup’s resources to support a simple versioning scheme that can automatically run scripts to upgrade your modules.

Once an installer script for a Magento module, it will never again run a different setup for this module (short manual removal of the reference in the table core_resource). Instead, you must upgrade script. Upgrade scripts are very similar to installation scripts, with a few important differences.

To begin, we will create a script to the next location, with the following content

File: app/code/local/Alanstormdotcom/Weblog/sql/weblog_setup/mysql4-upgrade-0.1.0-0.2.0.php

echo 'Testing our upgrade script (mysql4-upgrade-0.1.0-0.2.0.php) and halting execution to avoid updating the system version number <br />';
die();

Upgrade scripts are placed in the same folder as your installer script, but just renamed. First and foremost, of course, the file name contains the word upgrade. Second, you’ll notice there are two version numbers, separated by a “-“. The first (0.1.0) is the module version we upgrade. The second (0.2.0) is the module version we are upgrading to.

If we Magento cache cleared and a new page, our script would not run. We need the version number in the config.xml file to update our module to upgrade to activate

<modules>
    <Alanstormdotcom_Weblog>
        <version>0.2.0</version>
    </Alanstormdotcom_Weblog>
</modules>

With the new version in place, we have our Magento cache and load each page in our Magento site. You will see the output of your upgrade script.

Before we go further and actually perform the upgrade script, there is an important part of the behavior you want to be aware of. Create another upgrade file to the following location in the following content.

File: app/code/local/Alanstormdotcom/Weblog/sql/weblog_setup/mysql4-upgrade-0.1.0-0.1.5.php

echo 'Testing our upgrade script (mysql4-upgrade-0.1.0-0.1.5.php) and NOT halting execution <br />';

If you reload a page, you’ll notice you see both messages. If the version number of announcements Magento a module is changed, it runs through all the setup scripts needed for this version to bring up to date. Although we never really a 0.1.5 version of the blog module, Magento sees the upgrade script, and will try to run it. Scripts will be executed in order from low to high. If you look at the table core_resource,

mysql> select * from core_resource where code = 'weblog_setup';
+--------------+---------+
| code         | version |
+--------------+---------+
| weblog_setup | 0.1.5   |
+--------------+---------+
1 row in set (0.00 sec)

You’ll notice Magento believe that the version number at about 1.5. That’s because we completed the implementation of 1.0 to 1.5 upgrade, but not full implementation of the 1.0 to 2.0 upgrade.

So, with all those out of the way, the writing of our current upgrade script is identical to writing an installer script. Let’s change the script to read 0.1.0-0.2.0

$installer = $this;
$installer->startSetup();
$installer->run("
    ALTER TABLE `{$installer->getTable('weblog/blogpost')}`
    CHANGE post post text not null;
");
$installer->endSetup();
die("You'll see why this is here in a second");

Try refreshing a page within your site and Magento … nothing. The upgrade script will not run. The item field in our table is still null values, and more importantly, the call to die () does not stop execution. Here’s what happened

1. The source is version 0.1.0 weblog_setup
2. We upgraded our module to version 0.2.0
3. Magento saw the custom module, and saw there were two upgrade scripts to run, 0.1.0 to 0.1.5 and 0.1.0 to 0.2.0
4. Magento queue both scripts are executed
5. Magento 0.1.0 ran the script on 0.1.5
6. Weblog_setup the source is now at version 0.1.5
7. Magento 0.1.0 to 0.2.0 ran the script, stop the execution was put
8. On the next page to load, Magento looked at version 0.1.5 and saw no weblog_setup upgrade scripts to run, because both scripts should be carried out from 0.1.0

The correct way to achieve what we wanted would have been to our script name as follows

mysql4-upgrade-0.1.0-0.1.5.php #This goes from 0.1.0 to 0.1.5
mysql4-upgrade-0.1.5-0.2.0.php #This goes 0.1.5 to 0.2.0

Magento is smart enough to both scripts on a page to load run. You can go back in time and give it a try by modifying the table core_resource

update core_resource set version = '0.1.0' where code = 'weblog_setup';
...

It is one of the strange vagaries of the Magento system updates will run as previously configured. This means that you want to be careful with multiple developers to add scripting to update the system. You’ll either want a kind build-meister/deployment-manager responsible for the upgrade scripts or (God forbid) Developers should talk.

Wrap-up

You now know how to setup Magento sources use to create a database migration scripts version number, and understand the scripts in the core modules. Beyond a standard way for developers to write migration scripts, setup sources become more important in making and changing entity attribute price models, that’s where we head next time.

(Based on Alanstorm Turtorial)

Comments Closed