SitePoint Sponsor |
|
User Tag List
Results 1 to 25 of 274
-
Nov 10, 2004, 12:02 #1
- Join Date
- Sep 2004
- Location
- New York, NY
- Posts
- 258
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Data Access Layer - Is there a real seperation?
This conversation started in the comments of HarryF's Blog, and I thought it would be worthwhile to move here to continue the conversation, since thats what this forum is for.
This started as a response to Tony Marston's very interesting Development Infrastructure for PHP, which I highly recommend as reading even if you walk away disagreeing with his methodology.
To bring you up to speed:
Originally Posted by JNKlein
Originally Posted by Tony Marston
Originally Posted by Cochambre
Originally Posted by Tony Marston
To respond to Tony's question about why I seperated the User and UserMapper class; if you have a User class that performs some business logic that doesn't interact with the database - suppose a hypothetical printUserName() method that just spits out the current $user->username, wouldn't you want a seperate class that mapped a user to the database, either inserting or deleting or what-have-you? Then, if you needed to add more functionality on the business logic end, you would only change the User class (not the UserMapper class) to have another method, suppose printUserEmail(). This way, you can extend or refactor your User business logic (maintaining the same interface), without changing anything about the data access.
This is how I interpret the "seperation of data access and business logic".
Maybe I'm not doing a good job of explaining my logic here - George Schlossangle says it well in his "Advanced PHP" chapter on this very subject (which, again, I recommend). Maybe someone else can clarify?
Regardless, my question to Tony is this - if you have your business logic and data access logic in the same class, can they be seperate? In reference to the business logic, Tony said "Each business entity (eg: customer, product, invoice) has its own class. This identifies the structure of the associated database table..." - I think this is the part I'm having trouble understanding, because where is the seperation of logic if the business entity knows both the business logic and must know the structure of the associated data storage mechanism.
And now, a seperate question - what is the point of a DAO (chose your favorite - ADODB or PEAR DB) that probably won't make it any easier to change what database you're using, since there is inevitably still hardcoded some query that doesn't work the same in mySQL, msSQL, PostreSQL, and Oracle, let alone just two of the above. Just because you execute($query) doesn't mean $query will actually work.
-
Nov 10, 2004, 12:35 #2
- Join Date
- Oct 2004
- Location
- downtown
- Posts
- 145
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Off Topic:
I believe that we must not punish the users by using this "development shurtcuts".
No offence though I wouldn't consider them to be shortcuts? Fair enough about the point you made about adding functionality to the core of an application where it may not be needed, though you do not really know this for certain 3 years down the line.
This is more of a design issue than a development issue in my view, but it's always possible that once an application has been in use for a while, it'll grow and evolve, requireing for example, a better database server.
You need to account for this possibility, even if it (presently) sounds remote.
Back on topic then, I agree that a degree of seperation is a basic requirement for todays applications. I'm still looking at the url in question, very interesting it is so will have more to say once I've digested it all
-
Nov 10, 2004, 12:40 #3
- Join Date
- Oct 2004
- Location
- downtown
- Posts
- 145
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I think this is the part I'm having trouble understanding, because where is the seperation of logic if the business entity knows both the business logic and must know the structure of the associated data storage mechanism.But in my thinking, would the actual database table (columns in this case) actual change as well? Just because your moving to a new(er) database server?
I think in my view the seperation for the most part is in removing the data source from the business logic.
On this same subject I'm looking into the same thing with Reflection, so I do not need to have the database table column names within a class, if I am thinking right anyways.
-
Nov 10, 2004, 13:54 #4
- Join Date
- Oct 2004
- Location
- Sutton, Surrey
- Posts
- 259
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by JNKlein
To follow the rules of encapsulation all the methods which deal with an object must be defined within a single class. It does not matter if one method talks to a database, one method sends an email, one method dials a telephone number and yet another method changes the television channel. The internals of each method are supposed to be irrelevant.
Originally Posted by JNKlein
Although this information is defined within a business object it is not used to access the persistent data store (i.e. database) until it is passed to my Data Access Object (DML class). This uses the information given to it - the table structure and some data - to construct the relevant query and then pass it to the specified database engine via the relevant API.
There is nothing in the rules of OOP that says I cannot define information in one object, then pass it to another for processing. It is where this information is actually processed which is important. My $fieldspec array actually contains information which is used in three different places:
- Some information is passed to a validation object to perform primary validation.
- Some information is passed to the XSL transformation to help build the HTML control for each field.
- Some information is passed to the DAO to communicate with the database.
If I were to define this information in three separate places surely this would break encapsulation?
Remember that my data access object contains no information about any database table whatsoever, so this information has to be passed to it from an external source. This does not make the external source part of the data access object, now does it? Similarly the XSL stylesheet, which is used to construct the XHTML output, is useless without an XML file containing the data. This data originates from the business layer, but that does not make the business layer part of the XSL stylesheet, now does it?
If you are prepared to treat the term logic as where information is processed rather than where information originates you will see that my usage of the term 'separation of logic' is entirely justified whereas yours is questionable.
Originally Posted by JNKlein
-
Nov 10, 2004, 14:46 #5
- Join Date
- Jul 2004
- Location
- Norway
- Posts
- 13
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Version0-00e
The limits of my language are the limits of my world.
(Wittgenstein)
-
Nov 10, 2004, 16:00 #6
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Using your example of a data mapper between the domain model (User class) and the data model (database)... If you push the specific mapping code into a data mapper class you only have to make changes in one place. If there is a bi-directional dependency between the domain object and the data access object you have to make changes in two places. The business objects care about business logic and data not how to get the data. The data access objects care about how to get the data not what to do with it. It isn't just a logical separation of business logic and data access logic, it is a separation of concerns.
JT
-
Nov 10, 2004, 16:56 #7
- Join Date
- Oct 2004
- Location
- downtown
- Posts
- 145
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
In my case my business object does not construct an SQL query then pass it to the DAO for processing. It passes the components of the query to the DAO, and it is up to the DAO to construct the actual query string. I have a separate DAO class for each database engine, so each class is able to construct the query according to the requirements of that particular database engine.
The business objects care about business logic and data not how to get the data. The data access objects care about how to get the data not what to do with it. It isn't just a logical separation of business logic and data access logic, it is a separation of concerns.
-
Nov 10, 2004, 17:13 #8
- Join Date
- Jul 2003
- Location
- Los Angeles
- Posts
- 199
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by JNKlein
PHP Code:
<?php
$mysqli = new mysqli('localhost', 'user', 'password', 'dbname');
if( $result = $mysqli->query('SELECT * from user LIMIT 5, 0') )
{
while( $row = $result->fetch_assoc() ){
print_r($row);
}
}
?>
PHP Code:<?php
$stmt = $con->createStatement();
$stmt->setLimit(5);
$stmt->setOffset(0);
$result = $stmt->executeQuery('SELECT * FROM user');
foreach( $result as $user)
{
// process here
}
?>
However there are certain situations where it's not so easy and it requires the programmer to do a switch on the database type and handle a special case but overall a good abstraction layer makes your application that much more portable.
You can apply the same thing to one level higher with an ORM where you define your database tables in XML and run a generator to create classes to help you access your database tables. It can save you a lot of time but can't cover every single situation. Propel for instance which I really love requires manual intervention for returning data from functions like COUNT, MAX, etc when you mix it with results from entities:
Code:SELECT 'c.cat_id, c.cat_title, COUNT(photo_id) as cnt FROM gallery_category AS c LEFT JOIN gallery_photos as p ON p.cat_id = c.cat_id GROUP BY c.cat_id, c.cat_title'
PHP Code:<?php
/*
* Fetch the categories with the count of the number of photos
* that belong to each of them.
*
* @return array
* @throws PropelException
*/
class GalleryCategoryPeer extends BaseGalleryCategoryPeer
{
function findCategories($limit, $offset)
{
$con = Propel::getConnection();
$sql = 'SELECT c.cat_id, c.cat_title, count(photo_id) as cnt
FROM
gallery_category AS c
LEFT JOIN
gallery_photos as p
ON
p.cat_id = c.cat_id
GROUP BY
c.cat_id, c.cat_title';
$stmt = $con->createStatement();
$stmt->setLimit($limit);
$stmt->setOffset($offset);
// Hand code the query to return just the cat_id, cat_title, cnt
$rs = $stmt->executeQuery($sql, ResultSet::FETCHMODE_ASSOC);
while( $rs->next() )
{
$row = $rs->getRow();
$categories[] = $row;
}
return $categories;
}
}
?>
And as for the issue of DataMappers, again it's ONE possible design pattern but not the magical one. There are many ways to skin a cat including ActiveRecords and the RDG+TDG approach that Propel takes. For some things I use hand coded DataMappers for others I use raw SQL(eww yea gross but it's fast as hell to code) and for a project I'm working on now I use Propel.
There isn't one answer to fit everybody's needs.
-
Nov 10, 2004, 17:18 #9
- Join Date
- Oct 2004
- Location
- Sutton, Surrey
- Posts
- 259
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Tony Marston
Originally Posted by Version0-00e
-
Nov 10, 2004, 18:04 #10
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Version0-00e
Code:Domain Model <-- Data Mapper --> Data Model
PHP Code:<?php
// Find the person with an ID of "1"
$mapper =& new PersonMapper($db);
$person = $mapper->findById(1);
// Do something with the person here
$person->setName("John");
// Tell the mapper to update the data model (the data model does not have to be a database...)
$mapper->update($person);
?>Last edited by seratonin; Nov 10, 2004 at 22:36.
-
Nov 10, 2004, 18:27 #11
- Join Date
- Jul 2003
- Location
- Los Angeles
- Posts
- 199
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I made a post about using a datamapper pattern here http://www.sitepoint.com/forums/showthread.php?t=208510 It's a simple example with code gutted out that I think is pretty clear.
Just keep an open mind about these things. DataMappers are one possible solution and may not appeal to everyone.
-
Nov 10, 2004, 19:01 #12
- Join Date
- Dec 2003
- Location
- oz
- Posts
- 819
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Tony Marston
PHP Code:$result_iterator = User::GetByFirstName('John');
-
Nov 10, 2004, 19:12 #13
- Join Date
- Oct 2004
- Location
- Sutton, Surrey
- Posts
- 259
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by lazy yogi
PHP Code:$object = new User;
$where = "first_name='John'";
$data = $object->getdata($where);
-
Nov 10, 2004, 19:34 #14
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by lazy_yogi
PHP Code:<?php
$mapper =& new UserMapper($db);
// returns an array of User objects
$users = $mapper->findUsersByName("John");
foreach ($users as $user) {
echo $user->getId();
}
?>
-
Nov 10, 2004, 19:50 #15
- Join Date
- Oct 2004
- Location
- downtown
- Posts
- 145
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
The Data Row Gateway if I remember is for returning only the one row of data?
In that case, isn't this a Data Row Gateway,
PHP Code:$object = new User;
$where = "first_name='John'";
$data = $object->getdata($where);
-
Nov 10, 2004, 19:51 #16
- Join Date
- Jul 2003
- Location
- Los Angeles
- Posts
- 199
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
And now to really confuse you...the Propel way.
PHP Code:$criteria = new Criteria();
$criteria->add(UserPeer::NAME, "%John%", Criteria::LIKE);
$users = UserPeer::doSelect($criteria);
foreach($users as $user)
{
echo $user->getId();
// Propel entity classes can save state so we could do this:
// $user->setLastName('Smith');
// $user->save();
}
PHP Code:$users = UserPeer::findUsersByName("John");
-
Nov 10, 2004, 21:22 #17
- Join Date
- Apr 2003
- Location
- London
- Posts
- 2,423
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Hi...
Originally Posted by Tony Marston
Flexible classes have a single role within the system, a concept called "cohesion". However you don't usually want every single behaviour of a concept in a separate class either. That would be overkill. For that reason we usually split the concept into just enough classes to do the job in the myriad ways we need.
A DataMapper splits persistence off from the domain object leaving both classes more cohesive. The price you pay is extra client code handling two objects. What you gain is divide and conquer on the complexity of the code. Smaller classes are easier to get right. You can also swap them around. You can use the application with different databases just by choosing a different mapper at run time without touching any of the domain object code. This makes it easier to test as well.
yours, MarcusMarcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things
-
Nov 10, 2004, 22:31 #18
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Version0-00e
JT
-
Nov 11, 2004, 02:36 #19
- Join Date
- Jul 2004
- Location
- Canada, Qc
- Posts
- 42
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Tony, I readed a lot of your stuff tonight (mainly the 2 database/php articles + your FAQ). I like your style, your boldness (often people that are bold in their comment and not afraid to joke-around or criticize are very good, because they have to make up for their comment and attitude).
I probably have had some comment and wondering about your way to do stuff while reading but I tried to see the big picture rather than do my often critic or try to test everything. But since you seem to like to be pragmatic, just doing a simple stuff in your sample application:
Go there
http://www.tonymarston.net/sample/person_add.php
Enter anything in a field, let's say First name. Then click ENTER.
It leads to a tree structure popup page.
Uh oh?
This can probably be explained, but I tried to be a simple user and just test your application and found this. Either I am lucky or what? ;-)
No matter, what I readtonight was interesting, just your attitude is a great thing IMHO ;-) There is gazillions of way to do and sometime there is 2 way that is good, it all depend of your criteria of good and since you said it is like art (and I agree), then it is maybe just a matter of taste? ;-)
-
Nov 11, 2004, 06:45 #20
- Join Date
- Oct 2004
- Location
- Sutton, Surrey
- Posts
- 259
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by MickoZ
(*) Note: This behaviour is different with different browsers, so it is wrong to assume that the ENTER key is the same as the SUBMIT button, especially when the form contains more than one button.Last edited by Tony Marston; Nov 11, 2004 at 08:25.
-
Nov 11, 2004, 06:54 #21
- Join Date
- Oct 2004
- Location
- Sutton, Surrey
- Posts
- 259
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by lastcraft
Encapsulation means that the class must define all the properties and methods which are common to all objects of that class. All those properties and methods must exist inside a single container or 'capsule', and must not be distributed across multiple locations.
-
Nov 11, 2004, 07:03 #22
- Join Date
- Oct 2004
- Location
- Sutton, Surrey
- Posts
- 259
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by seratonin
Note that my User class does not access the database directly - it goes through a data access object which is reponsible for generating the actual SQL query. Perhaps this serves the same functionality as your userMapper?Last edited by Tony Marston; Nov 11, 2004 at 09:49.
-
Nov 11, 2004, 09:38 #23
- Join Date
- Oct 2004
- Location
- downtown
- Posts
- 145
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Therefore I consider your opinion to be totally wrong.
Now, that is arrogance, and as one person after following many posts by lastcraft, I'd have to disagree with your statement Tony.
Lastcraft has basically explained encapsulation, and from this statement is something I can take from it as I've near as damn it read something much along the same lines before elsewhere, Thinking In Java 3rd Edition if I remember?
Take it easy, you'll end up with a bad reputation, and as someone talking from experience () it doesn't do you any good around this parts
-
Nov 11, 2004, 10:07 #24
- Join Date
- Oct 2004
- Location
- Sutton, Surrey
- Posts
- 259
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Version0-00e
If you believe that encapsulation means having properties and methods contained within more than one class then you are hopelessly wrong. If my bringing this to your attention makes me arrogant, then so be it. What does it make you?
-
Nov 11, 2004, 10:36 #25
- Join Date
- Dec 2003
- Location
- Arizona
- Posts
- 411
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Tony Marston
JT
Bookmarks