The data mapper pattern only tells you, what it is supposed to do, not how it should be implemented.
Therefore all the answers in this topic should be treated as subjective, because they reflect each authors personal preferences.
I usually try to keep mapper's interface as simple as possible:
fetch()
, retrieves data in the domain object or collection,
save()
, saves (updates existing or inserts new) the domain object or collection
remove()
, deletes the domain object or collection from storage medium
I keep the condition in the domain object itself:
$user = new User;
$user->setName( 'Jedediah' );
$mapper = new UserMapper;
$mapper->fetch( $user );
if ( $user->getFlags() > 5 )
{
$user->setStatus( User::STATUS_LOCKED );
}
$mapper->save( $user );
This way you can have multiple conditions for the retrieval, while keeping the interface clean.
The downside to this would be that you need a public method for retrieving information from the domain object to have such fetch()
method, but you will need it anyway to perform save()
.
There is no real way to implement the "Tell Don't Ask" rule-of-thumb for mapper and domain object interaction.
As for "How to make sure that you really need to save the domain object?", which might occur to you, it has been covered here, with extensive code examples and some useful bits in the comments.
Update
I case if you expect to deal with groups of objects, you should be dealing with different structures, instead of simple Domain Objects.
$category = new Category;
$category->setTitle( 'privacy' );
$list = new ArticleCollection;
$list->setCondition( $category );
$list->setDateRange( mktime( 0, 0, 0, 12, 9, 2001) );
// it would make sense, if unset second value for range of dates
// would default to NOW() in mapper
$mapper = new ArticleCollectionMapper;
$mapper->fetch( $list );
foreach ( $list as $article )
{
$article->setFlag( Article::STATUS_REMOVED );
}
$mapper->store( $list );
In this case the collection is glorified array, with ability to accept different parameters, which then are used as conditions for the mapper. It also should let the mapper to acquired list changed domain objects from this collection, when mapper is attempting to store the collection.
The mapper in this case should be capable of building (or using preset ones) queries with all the possible conditions (as a developer you will know all of those conditions, therefore you do not need to make it work with infinite set of conditions) and update or create new entries for all the unsaved domain object, that collection contains.
Note: In some aspect you could say, that the mapper are related to builder/factory patterns. The goal is different, but the approach to solving the problems is very similar.