Solar Models vs. Zend Framework Models

Today, Michelangelo van Dam posted this narrative relating his experience with Zend Framework models:

http://www.dragonbe.com/2010/01/zend-framework-data-models.html

I read the article, and wondered how hard it would be to replicate his narrative using the Solar Framework model system. Turns out it’s pretty easy: there’s a lot of work that Solar does for you.

  1. Download a Solar system, move it to your localhost directory and make sure that the tmp and sqlite directories are accessible to the web server.

    $ wget http://svn.solarphp.com/system/download/solar-system-1.0.0beta2.tgz
    $ tar -zxf solar-system-1.0.0beta2.tgz
    $ cp -r solar/* /var/www/htdocs # or your localhost directory
    $ cd /var/www/htdocs         # or your localhost directory
    $ chmod -R 777 tmp sqlite
    
  2. Create tables and data in sqlite/example.sq3. There are two changes from Michelangelo’s code: the table names are plural, and rename the column type_id to address_type_id. (These are easy enough to change in the model class definitions, but I’d like to minimize the amount of customization for this example.)

    CREATE TABLE "users" (
        "id" integer primary key autoincrement,
        "username" varchar(255) not null,
        "password" varchar(255) not null
    );
    
    
    CREATE TABLE "contacts" (
        "id" integer primary key autoincrement,
        "user_id" integer not null,
        "email" varchar(255) not null,
        "phone" varchar(255) null,
        "fax" varchar(255) null
    );
    
    
    CREATE TABLE "addresses" (
        "id" integer primary key autoincrement,
        "address_type_id" integer not null default 1,
        "user_id" integer not null,
        "address1" varchar(255) not null,
        "address2" varchar(255) null,
        "city" varchar(255) not null,
        "state" varchar(255) null,
        "zip" varchar(255) not null,
        "country" varchar(255) not null
    );
    
    
    CREATE TABLE "address_types" (
        "id" integer primary key autoincrement,
        "type" varchar(255) not null
    );
    
    
    -- Data for users table --
    INSERT INTO "users" 
        VALUES (1, 'testuser1', 'test123');
    INSERT INTO "users" 
        VALUES (2, 'testuser2', 'test234');
    
    
    -- Data for contacts table --
    INSERT INTO "contacts" 
        VALUES (1, 1, 'test1@example.com', '1-800-555-1234', '1-800-555-1230');
    INSERT INTO "contacts" 
        VALUES (2, 2, 'test2@example.com', '1-800-555-2234', '1-800-555-2230');
    
    
    -- Data for addresses table --
    INSERT INTO "addresses"
        VALUES (1, 1, 1, '1 Test Home', '', 'Testtown', 'ZF', '1234', 'PHP');
    INSERT INTO "addresses"
        VALUES (2, 1, 2, '2 Test Home', '', 'Testtown', 'ZF', '1234', 'PHP');
    INSERT INTO "addresses"
        VALUES (3, 2, 2, 'Test Corp, LTD', '4 Test Ave', 'Testtown', 'ZF', '1234', 'PHP');
    
    
    -- Data for address_types table --
    INSERT INTO "address_types"
        VALUES (1, 'Home address');
    INSERT INTO "address_types"
        VALUES (2, 'Billing address');
    
  3. Make sure the SQLite database is accessible to the web server.

    $ chmod 777 sqlite/example.sq3
    
  4. Make a vendor space for the controllers, models, suppory libraries, etc. For this example the vendor will be named Example.

    $ ./script/solar make-vendor Example
    
  5. Edit the system configuration in config.php. Change the front-controller class prefixes and model catalog class prefixes, and tell the SQLite adapter which file name to use.

    // front controller
    $config['Solar_Controller_Front'] = array(
        'classes' = array('Example_App'),
        // ... the rest of the front-controller array ...
    );
    
    
    // model catalog
    $config['Solar_Sql_Model_Catalog']['classes'] = 'Example_Model';
    
    
    // add sqlite config
    $config['Solar_Sql_Adapter_Sqlite'] = array(
        'name' => "$system/sqlite/example.sq3",
    );
    
  6. Make the model classes. Note that this avoids a ton of effort that Zend Framework requires: Michelangelo had to hand-create table, model, and mapper classes for each of the following single-line commands.

    $ ./script/solar make-model Example_Model_Users
    $ ./script/solar make-model Example_Model_Contacts
    $ ./script/solar make-model Example_Model_Addresses
    $ ./script/solar make-model Example_Model_AddressTypes
    
  7. Edit the model classes to express what the related models are:

    /** source/example/Example/Model/Users.php */
    protected function _setup()
    {
        // chain to parent
        parent::_setup();
    
    
        // relateds
        $this->_hasOne('contact');
        $this->_hasMany('addresses');
    }
    
    
    /** source/example/Example/Model/Contacts.php */
    protected function _setup()
    {
        // chain to parent
        parent::_setup();
    
    
        // relateds
        $this->_belongsTo('user');
    }
    
    
    /** source/example/Example/Model/AddressTypes.php */
    protected function _setup()
    {
        // chain to parent
        parent::_setup();
    
    
        // relateds
        $this->_hasMany('addresses');
    }
    
    
    /** source/example/Example/Model/Addresses.php */
    protected function _setup()
    {
        // chain to parent
        parent::_setup();
    
    
        // relateds
        $this->_belongsTo('address_type');
        $this->_belongsTo('user');
    }
    
    
  8. Make a skeleton app (i.e., a controller with its associated actions and views):

    $ ./script/solar make-app Example_App_Accounts
    
  9. Edit the controller action to fetch user account data:

    /** source/example/Example/App/Accounts.php */
    public $list;
    public function actionIndex()
    {
        // get a model catalog
        $model = Solar_Registry::get('model_catalog');
    
    
        // populate $this->list with all users.
        // eager-fetch the contact record and addresses collection.
        $this->list = $model->users->fetchAll(array(
            'eager' => array(
                'contact',
                'addresses',
            ),
        ));
    }
    
  10. Edit the view for the index action. This could be a view and a partial, but let’s keep it straightforward for now.

    /** source/example/Example/App/Accounts/View/index.php */
    <table>
        <tr>
            <th>Username</th>
            <th>E-Mail</th>
            <th>Phone</th>
            <th>Fax</th>
            <th># addresses</th>
        </tr>
    
    
        <?php foreach ($this->list as $user): ?>
    
    
        <tr>
            <td><?php echo $this->escape($user->username); ?></td>
            <td><?php echo $this->escape($user->contact->email); ?></td>
            <td><?php echo $this->escape($user->contact->phone); ?></td>
            <td><?php echo $this->escape($user->contact->fax); ?></td>
            <td><?php echo count($user->addresses); ?></td>
        </tr>
    
    
        <?php endforeach; ?>
    
    
    </table>
    
    

You’re done. Browse to http://localhost/index.php/accounts/index to see the results of your handiwork.

Total elapsed time from when I read Michelangelo’s article to come up with the Solar conversion and write this article: under two hours.

Solar makes model creation and interaction very easy and very powerful. Maybe your next project should use Solar instead of Zend Framework?

This entry was posted in PHP, Solar. Bookmark the permalink.