EDITED
I've just joined the chat to get some insights on this question. Let's make a resume:
- only Doctrine 2.x uses some of the DataMapper ideas
- Developers still think that ActiveRecord is the ambrosia from gods
- The inability to test separately from DB
- Dependency Injection enemy
See: https://softwareengineering.stackexchange.com/questions/119352/does-the-activerecord-pattern-follow-encourage-the-solid-design-principles
Description
Propel is based on the ActiveRecord Pattern, and Doctrine, instead make use of Data Mappers and Virtual Proxies.
When I first learned about OOP with PHP, ActiveRecord was being widely used, most due the Ruby On Rails influence. Then the developers started realizing that ActiveRecord is a limited concept, specially for larger applications.
Why?
Think by yourself, is the responsibility of a domain model object to know how to save itself? Or even convert its data to the JSON format?
The answer is no, because it violates the Single Responsibility Principle (SRP) and the domain cohesion.
Martin defines a responsibility as a reason to change, and concludes
that a class or module should have one, and only one, reason to change.
Let's say you have an Author
entity. For which reasons would you change it?
Well, if implemented using ActiveRecord, besides the need to change the object properties itself, you'll also need to change it if you implement a new type of persistence specific to that object, as we do when implementing cache strategies.
Also, let's say the Propel library changes the method of the model's base class (just a naive example). Now you have a reason to change other parts of your application that doesn't have nothing with persistence, since your entities are tight coupled with the ORM base class. In other words, your domain model objects should not depend on the specifics of each ORM Framework. You should be able to change your ORM letting your domain model objects untouched.
So active record requires your domain model to knows about the existence of a persistence layer. There are two responsibilities.
The worst thing when using Active Record, is that you "screw up" your domain model inheritance hierarchy. If you have a Human
entity extending a base class for persistence purposes, and then a Man
entity extending the Human
entity, if for some reason you have another Woman
entity extending the Human
Entity without the ability to be persisted, then you will break the contract established by the base class which states that each entity needs to know how to save itself. You'll not be able to do $woman->save()
.
Strictly saying, it also leds to violations of the Liskov Substitution Principle
Finally, the ultimate reason to stop using ActiveRecord. Looking at the following code you'll be able to see that the ORM's responsibility of mapping objects to the storage during a transaction was delegated to a domain model entity:
class Book extends BaseBook
{
public function postSave(PropelPDO $con)
{
$this->updateNbBooks($con);
}
public function postDelete(PropelPDO $con)
{
$this->updateNbBooks($con);
}
public function updateNbBooks(PropelPDO $con)
{
$author = $this->getAuthor();
$nbBooks = $author->countBooks($con);
$author->setNbBooks($nbBooks);
$author->save($con);
}
}
http://www.propelorm.org/documentation/06-transactions.html You must
update this new column every time you save or delete a Book object;
this will make write queries a little slower, but read queries much
faster. Fortunately, Propel model objects support pre- and post- hooks
for the save() and delete() methods, so this is quite easy to
implement
So, in a single model entity you have:
- Persistence
- Intercepting Filter Pattern for pre and post saving/deleting
- Validation (http://www.propelorm.org/documentation/05-validators.html)
- Cache
- And there you go...