tags:

views:

53

answers:

2

Hi, currently I have this client code in my PHP MVC web app:

try {
  BookMapper::insert($book);
} catch (DbUniqueConstraintViolationException $e) {
  $errorList->addMessage($book . " already exists!");
}

I'm wondering is it bad practice to refer to the low-level framework Db* exceptions to my client code? If so, should I adjust my model code like so:

class BookAlreadyExistsException extends Exception { }

class BookMapper {
  public static function insert($book) {
    try {
      // call to DB-layer to insert $book
      // (not relevant to the question)
    } catch (DbUniqueConstraintViolationException $e) {
      throw new BookAlreadyExistsException();
    }
  }
}

and then use this new client-code...

try {
  BookMapper::insert($book);
} catch (BookAlreadyExistsException $e) {
  $errorList->addMessage($book . " already exists!");
}

Or something else? Or is the original method fine?

Thanks!

EDIT: Just want to add, the latter method reads the best IMO, but it comes with the object creation / rethrowing overhead and more significantly, it requires duplicating the rethrowing code in every mapper's insert() method. The former method is easy to implement and catch and works for any model but I remember reading somewhere that you shouldn't do it this way?

+2  A: 

I think you should definitly throw your own exception.

But I would also consider a third option and that is letting the insert method return true for success and false for failure. Exceptions should be used for exceptions and the fact that a book already exists might actually be an expected/predictable case.

And if duplicate books are truly excetions that should not be possible (unless for programming errors), then you could as well stick with the database exception but in that case don't catch it. Let it bubble all the way up.

Cellfish
With the returning true/false thing. What if in some other client code, I need to know more? For example, which constraint was violated. With exceptions, this info is available in the exception object. With true/false how would I go about getting it? Would you recommend I add a "getLastSqlStateError()" or something to the DB layer?
+1 For recommending return true/false instead of exception.
too much php
Can anyone offer any advice on my first comment?
If you need to know the specific failure, you can also use 'return 0' for success and 'return 1+' to indicate a specific error condition (this is how shell commands work and the reason why C programs' `main()` functions return 0 when the program exits without an error).
too much php
Yeah, as "too much php" said, I would rather have the insert method return a status code if there are several different reasons for failure that are expected. Sometimes a "GetLastError" method on the BookMapper would make sense (and keeping true/false for the insert). In all circumstances it is generally a better design to have BookMapper return errors that the user of BookMapper can understand. The user of BookMapper should not need to be aware of the fact that there is a DB with the books. It could be anything like a file or just an in memory structure.
Cellfish
A: 

I highly recommend this article. Although it is written for Java, the principles are quite applicable to PHP as well. It has good guidelines for what types of exceptions you should be throwing and catching.

too much php
Thanks that's the article I was talking about in my post! Seems I skipped the part about only throwing exceptions in exceptional cases!