Php – Zend: Creating models for a products table

MySQLPHPzend-framework

I'm trying to create two models, products and product_manufacturers so that I can pull in manufacturers as well as products, edit them in the admin if necessary, and the usual CRUD stuff. Here's the schema for the tables ( it's not finalized so if you have any suggestions go ahead ).

CREATE TABLE `product_manufacturers` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `manufacturer_name` varchar(100) default NULL,
  `active` tinyint(1) default '1',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

CREATE TABLE `products` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `product_name` varchar(100) default NULL,
  `manufacturer_id` int(11) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

Example data:

( products ) –

id product_name manufacturer_id
1  iPod Nano 4g 1

( product_manufacturers ) –

id manufacturer_name active
1  Apple             1

I've created two models, but I'm having trouble pulling in the products based on the active product manufacturers ( and yes, I'm using a model autoloader ).

protected function _initAutoload ()
{
    // Add autoloader empty namespace
    $autoLoader = Zend_Loader_Autoloader::getInstance();
$resourceLoader = new Zend_Loader_Autoloader_Resource(
    array(
    'basePath' => APPLICATION_PATH ,
    'namespace' => '' ,
    'resourceTypes' => array(
        'form' => array('path' => 'forms/' ,
        'namespace' => 'Form_') ,
        'model' => array('path' => 'models/' ,
        'namespace' => 'Model_')
    )
    ));
    // Return it so that it can be stored by the bootstrap
    return $autoLoader;
}

application/models/Product.php:

<?php
require_once 'Zend/Db/Table/Abstract.php';

class Model_Product extends Zend_Db_Table_Abstract
{
    /**
     * The default table name 
     */
    protected $_name = 'products';
    protected $_dependentTables = array('Model_Manufacturer');
}

application/models/Manufacturer.php:

class Model_Manufacturer extends Zend_Db_Table_Abstract
{
    /**
     * The default table name 
     */
    protected $_name = 'product_manufacturers';
    protected $_referenceMap = array(
 'Model_Product' => array(
     'manufacturer_id' => array('id'),
     'id' => array('manufacturer_id'),
 ),
    );
}

I have a feeling I'm completely doing things wrong, as I'm creating an object from the Products Model and trying to print out all products which have a corresponding manufacturer, and that manufacturer is active.

 $model = new Model_Product();
 $results = $model->fetchAll();
 foreach ($results as $result) {
     //print_r($result);
     echo $result->product_name;
 }

I'm new to model creation, could anyone provide advice on how I could properly change my current code so that the products model properly relies on the manufacturers model?

I have a feeling that I'm improperly setting the dependentTables/referenceMap, and instead of using the generic fetchAll method I should be creating a custom method in either of the models to do the fetching and maybe a manual join with the manufacturers table, or would a proper referenceMap and dependentTables property solve that issue?

Best Answer

There are a few things to fix up here, but here are a few tips.

i. Table models work a little better if their names are pluralized. They sound better in the magic methods (e.g. $product_set = findProducts() instead of findModel_Product())

ii. The reference map is the important one and allows for those nifty magic selection methods (e.g. findParentX()) to work. It belongs in the dependent table, which is the product table here because it references the product_manufacturer.

  // in Model_Product
  protected $_referenceMap = array(

    // rule name
    'Manufacturer' => array(

      'columns'       => 'manufacturer_id',     // this column
      'refTableClass' => 'Model_Manufacturer',  // references that table object
      'refColumns'    => 'id'                   // and references that column
    )
  );



iii. Move dependent tables entry to your product_manufacturers table model, if you'd like: it's used only to replicate ON DELETE/UPDATE CASCADE database commands, though. (Leave it for now)

// in manufacturer table model
protected $_dependentTables = array('Model_Product');



Fetching

To get a product's manufacturer, do this:

$manufacturer = $product_row->findParentManufacturer();


To get a manufacturer's products, do this:

$product_set = $manufacturer_row->findProducts(); // Well, findModel_Product(), which is why you should rename your table ;)


For other magic methods originating from each single row, dig into the source code for Zend_Db_Table_Row_Abstract (especially public function __call).

Related Topic