In reply to the previous post from JNKlein here are my (arrogant?) views on the subject of separating business logic from data access logic:
The primary purpose of having a separate object in the data access layer (sometimes known as a Data Access Object or DAO) is that is should be possible to switch the entire application from one data source to another simply by changing this one component. Thus if I want to switch my application from MySQL to PostgreSQL (or whatever) I simply change my DAO.
In order to make this work in practice my own implementation is as follows:
(a) Each business entity (eg: customer, product, invoice) has its own class. This identifies the structure of the associated database table plus all the business rules required by that entity. Each of these is actually a subclass of a generic table class which contains sharable code that can be applied to any database table.
(b) When the business object gets instructions to update the database it does so via an insertRecord(), updateRecord(), or deleteRecord() method which contains the entire $_POST array. This is validated according to whatever rules have been defined within that particular subclass. If there are no errors it will talk to the relavant DAO in order to perform the database update.
(c) The DAO also has the insertRecord(), updateRecord() and deleteRecord() methods, but as well as the validated contents of the $_POST array it is also given a second array which contains all the table structure details. Using these two arrays it is easy to construct the relevant SQL query string before calling the relevant database API.
In this way my business object contains business rules, but no calls to database APIs, and my DAO contains calls to database APIs but no business rules. This is clear separation of logic.
Switching from one DBMS to another is simple to achieve in my infrastructure. In my generic table superclass I have a variable called $dbms_engine which is set to 'mysql' or 'postgresql' or whatever. This will then apply to all database tables unless overridden in any individual subclass. When the business object wants to talk to the data access object it first needs to instantiate an object from a class which is defined within a separate include() file. The name of this file is in the format 'dml.<engine>.class.inc' where <engine> is replaced by the contents of variable $dbms_engine. I have a separate version of this include() file for every DBMS that I use. All I need to do before accessing a new DBMS is to create a new version of the 'dml.<engine>.class.inc' file and I'm up and running.
Another advantage of this mechanism is that it would even be possible to talk to different database tables through different DBMS engines within the same transaction. Hows that for flexibility?
In case you want to see these (arrogant?) theories put into practice I have created a sample application which is described in
http://www.tonymarston.net/php-mysql...plication.html. This contains links where you can run the application online as well as download all the source code and run it on your own machine. You can then examine the source code and tell me what I am doing wrong.
BTW, in your example you mentioned have a User class and a UserMapper class. Why two? I can put everything I need into a single class, which is what encapsulation is supposed to be about.
Posted by: Tony Marston from tonymarston.net Nov 5th, 2004 @ 4:29 AM MST
Bookmarks