views:

36

answers:

2

Example:

class UserStorage {
    public function addUser(User $user) { //saves to db }
}

class User {
    public function setName($name);
}

What if I add a user to the user storage and later change that user object? In this case you might argue that user objects only should be stored on __destruct. But sometimes this isn't an option (eg imagine the user is displayed and updated afterwards).

+1  A: 

Implicit writes to the database are probably a bad idea. That should be an explicit, controlled operation.

Your pattern is a little weird to me, but I think this how you'd want to do it

class UserStorage
{
  const ACTION_INSERT = 'INSERT';
  const ACTION_UPDATE = 'UDPATE';

  public function addUser( User $user )
  {
    $this->saveUser( $user, self::ACTION_INSERT );
  }

  public function updateUser( User $user )
  {
    $this->saveUser( $user, self::ACTION_UPDATE );
  }

  protected function saveUser( User $user, $action )
  {
    switch ( $action )
    {
      case self::ACTION_INSERT:
        //  INSERT queyr
        break;
      case self::ACTION_UPDATE:
        //  UPDATE query
        break;
      default:
        throw new Exception( 'Unsupported action' );
    }
  }
}

class User
{
  public function setName( $name )
  {
    // whatever
  }
}

$userStorage = new UserStorage();
$user = new User();

$userStorage->addUser( $user );

$user->setName( 'Peter' );

try {
  $userStorage->updateUser( $user );
}
catch ( Exception $e )
{
  echo "There was an error saving this user: " . $e->getMessage();
}

But Personally I'm not crazy about this class design. There are some well-established patterns for this that are less confusing, such as ActiveRecord.

Peter Bailey
I'm not a great fan of the active record. But it probably has the advantage here that $object->save() clearly saves the state at a particular moment (e.i. the client code knows what it has done with it). Though maybe a disadvantage is, too, that when there are multiple pieces of code working with $object it's not clear who is responsible for triggering save().
koen
ActiveRecord isn't the only pattern. ZF uses (Table/Row) Data Gateway, for example.
Peter Bailey
I think it has the same problem. The model will hide away the gateway.
koen
+1  A: 

I agree with Peter, the above model seems a little quirky to me and I would recommend against implicit save to the datastore.

Additionally, a pattern to use is something like:

class UserStorage {
   $_user;

   function addUser(User user, commit = true) {
      if (commit) {
        // save to db
      } else {
        // populate your internal instance
        $_user = user;
      }
   }
}

So if you have multiple updates of a User object in the execution of your PHP application, you can use

addUser(user,false)

all the way until the very last call to

addUser(user)

This will alleviate the need for multiple inserts/updates to the DB.

However, your problem of where in the application to decide to finally save to db remains, and is more about logical flow than object representation. It may be helpful to have a end() function in the script that persists all your objects to the db.

Pras
I actually don't get why it's implicit. Client code creates a user object and adds it to the user store.
koen
It's implicit in the sense that the storage object is not being saved explicitly by other objects that use it i.e. if you choose to save to db in your __destruct method. If you continue to use addUser() in your code, it is explicit and no cause for worry.
Pras