views:

3309

answers:

5

I'm toying with Zend Framework and trying to use the "QuickStart" guide against a website I'm making just to see how the process would work. Forgive me if this answer is obvious, hopefully someone experienced can shed some light on this.

I have three database tables:

CREATE TABLE `users` (
  `id` int(11) NOT NULL auto_increment,
  `email` varchar(255) NOT NULL,
  `username` varchar(255) NOT NULL default '',
  `first` varchar(128) NOT NULL default '',
  `last` varchar(128) NOT NULL default '',
  `gender` enum('M','F') default NULL,
  `birthyear` year(4) default NULL,
  `postal` varchar(16) default NULL,
  `auth_method` enum('Default','OpenID','Facebook','Disabled') NOT NULL default 'Default',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `email` (`email`),
  UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

CREATE TABLE `user_password` (
  `user_id` int(11) NOT NULL,
  `password` varchar(16) NOT NULL default '',
  PRIMARY KEY  (`user_id`),
  UNIQUE KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

CREATE TABLE `user_metadata` (
  `user_id` int(11) NOT NULL default '0',
  `signup_date` datetime default NULL,
  `signup_ip` varchar(15) default NULL,
  `last_login_date` datetime default NULL,
  `last_login_ip` varchar(15) default NULL,
  PRIMARY KEY  (`user_id`),
  UNIQUE KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

I want to create a User model that uses all three tables in certain situations. E.g., the metadata table is accessed if/when the meta data is needed. The user_password table is accessed only if the 'Default' auth_method is set. I'll likely be adding a profile table later on that I would like to be able to access from the user model.

What is the best way to do this with ZF and why?

+1  A: 

Basically instead of using Zend_Db_Table use more general Zend_Db_Select or Zend_Db_Statement to retrieve data.

BTW. You might want to access password data not directly in User model, but rather in your User auth class derived from Zend_Auth_Adapter.

vartec
I don't plan on accessing password data via user model, except to update it. Doesn't using Zend_Db_Select completely circumvent the idea of modeling user data as a single object?
Andy Baird
It's not ORM, there's no need for 1-to-1 class-table mapping.
vartec
BTW. the 1=1 mapping would be possible if your DB was in 3NF, your problem comes from the fact, that you denormalized your DB, splitting user data into 3 tables.
vartec
A: 

I would say that the best way to do this with ZF is by using Doctrine.

Adrian Grigore
A: 

Just a few notes straight off the bat:

The first User table, seems more like a Person table, except for the auth method, but you could put that in the User_Password table, which you could probably rename User. The User_Metadata table seems like it could just be amalgamated with the User_Password/User table.

Now, even without those changes, you have three distinct tables, with three distinct concepts, if you model each of those separate as different classes, and then had a UserModel class as a facade of sorts to access each, it would likely make it easier.

Saem
A: 

In a nutshell I would create a model for each table, not one model that accesses all three. I would then define relationships between the tables.

To be honest it seems not very "DRY" to have to create a model for each table but that is what I see done repeatedly in the various examples online and it is what I have done in the handful of projects that I have created with the Zend Framework. If anyone has a better way of handling this I hope the will post it here.

gaoshan88
+5  A: 
class Users extends Zend_Db_Table_Abstract
{
    protected $_name = 'users';
    protected $_rowClass = 'User';
    protected $_dependentTables = array ('UserMetadata', 'UserPassword');

...

class UserMetadata extends Zend_Db_Table_Abstract
{
    protected $_name = 'user_metadata';
    protected $_referenceMap = array (
    'Users'=> array (
    'columns'=>'user_id',
    'refTableClass'=>'Users',
    'refColumns'=>'id'
    )
    );

...

class UserPassword extends Zend_Db_Table_Abstract
{
    protected $_name = 'user_password';
    protected $_referenceMap = array (
    'Users'=> array (
    'columns'=>'user_id',
    'refTableClass'=>'Users',
    'refColumns'=>'id'
    )
    );

Fetching data:

$id = //get your user id from somewhere

$users = new Users();
$user = $users->fetchRow('id=?', $id);
if ($user->authMethod == 0)
{
    $metadata = $user->findDependentRowset('UserMetadata')->current();
}

or

$user = $users->fetchRow($users->select()
              ->where('gender=?, 'M')
              ->order('email ASC');

... etc.

Inserting data:

$newRow = $users->fetchNew();
$newRow->email = [email protected];
$newRow->save();

or

$users = new Users();
$data = array('email'     => '[email protected]',
              'firstname' => 'Me');
$users->insert($data);

Updating:

$user->email = '[email protected]';
$user->save();

Deleting a row:

$user->delete();

Using transaction:

$db->beginTransaction();
$db->commit();
$db->rollback();

etc... it's all in the ZF Manual!

tharkun
This makes the most sense to me - so how do you handle an insert into the users table? Logically, there should be a user_metadata row created as well. Can I handle that on the model level somehow?
Andy Baird
The logics to create a meta-data row when creating a user-row you have to write yourself. but that's easy. if you want to do it the clean way use transactions...
tharkun
This is exactly what I ended up doing, and it worked quite well. Thank you.
Andy Baird