SitePoint Sponsor |
|
User Tag List
Results 1 to 25 of 26
Thread: Implementing a data mapper
-
Jul 26, 2005, 20:18 #1
Implementing a data mapper
I've been thinking lately that the ActiveRecord approach to ORM I've been using isn't flexible enough, and I've been looking into other patterns. I'm aware of the ones discussed in PoEAA, but not having read the book, I'm not 100% on what the best implementation would be. Here are some ideas I have, though:
The first is to have the mapper be contained by the model, passed as a parameter in the constructor, like this:
PHP Code:<?php
class Model {
var $mapper;
function Model($mapper) {
$this->mapper = $mapper;
}
function save() {
$this->mapper->save($this);
}
}
class Mapper {
function save($model) {
}
}
$model = new Model(new Mapper());
$model->save();
?>
However, the code could be even further decoupled like this:
PHP Code:<?php
class Model {
}
class Mapper {
function save($model) {
}
}
$model = new Model();
$mapper = new Mapper();
$mapper->save($model);
?>
Are there any other approaches that are worth looking into? How is the Data Mapper pattern typcially implemented? And how about the Row and the Table Data Gateway patterns?
Thanks...
-
Jul 27, 2005, 01:17 #2
- Join Date
- Jul 2005
- Location
- United Kingdom
- Posts
- 86
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Just wondering, I know that in general loose coupling is preferred but why exactly should the Model and Data Mapper be decoupled? In any decent separation the Data Mapper will be used only by the Model layer and personally, I'd say it's part of the Model layer in that it should be separated from the rest of the application.
In my opinion, something along these lines would be better:
PHP Code:<?php
class Mapper {
}
class Model {
private $data;
function save () {
$mapper = new Mapper();
// Code to save model...
}
}
?>
Try picturing it in UML, it's oh-so-useful
-
Jul 27, 2005, 02:32 #3
- Join Date
- Dec 2004
- Location
- ljubljana, slovenia
- Posts
- 684
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
How about when you want to use a different mapper? Creating the mapper sometimes depends on some decision-making and maybe configuration, so the models shouldn't bear the responsibility of creating them.
Besides, if you use a database abstraction layer, even the mapper has to be decoupled with it, so what you get is:
PHP Code:$db = new DB();
$mapper = new Mapper($db);
$model = new Model($mapper);
$model->save();
-
Jul 27, 2005, 02:41 #4
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Chapter #3 from PeEAA, which is about ORM, was out as a sample chapter at some time, but I seem to have lost the link. Google should help you there.
-
Jul 27, 2005, 02:41 #5
- Join Date
- Sep 2003
- Location
- Bratislava, Slovakia
- Posts
- 184
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by 33degrees
Annotations support for PHP5
TC/OPT™ Group Leader
-
Jul 27, 2005, 02:46 #6
- Join Date
- Jul 2005
- Location
- United Kingdom
- Posts
- 86
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by dbevfat
PHP Code:<?php
abstract class Mapper {
}
class MySQLMapper extends Mapper {
}
class PgSQLMapper extends Mapper {
}
class DatabaseFactory {
public static function create($type) {
switch (strtolower($type)) {
case 'mysql': return new MySQLMapper(); break
case 'pgsql': return new PgSQLMapper(); break
// etc.
default: trigger_error('Bad Mapper selected', E_USER_ERROR);
}
}
class Model {
private $data;
private $db;
public function __construct() {
$this->db = DatabaseFactory::create('mysql');
}
public function save() {
}
}
?>
The Database Access Layer is part of the Model in any 3-tiered application (which I'm assuming is the context of this discussion). Therefore, it shouldn't be globally accessible. What the hell is a view or controller going to do with a database!?
-
Jul 27, 2005, 02:53 #7
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Lazesharp
Originally Posted by johno
-
Jul 27, 2005, 03:19 #8
- Join Date
- Jul 2005
- Location
- United Kingdom
- Posts
- 86
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by kyberfabrikken
Everyone is entitled to their own opinions and interpretations I suppose.
-
Jul 27, 2005, 04:13 #9
- Join Date
- Dec 2004
- Location
- ljubljana, slovenia
- Posts
- 684
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Lazesharp
1) you're retrieving/constructing a database from inside of the model, which couples the model with the DatabaseFactory class. This is not flexible enough, if you want to reuse the model.
2) the model is aware of the completely irrelevant piece of data (irrelevant to the model, that is): the database type string. To provide some portability, you'd have to pass that string to the Model constructor, so _it_ could pass it down to the Mapper, so the proper DB gets constructed, right? Instead, you could just pass an existent mapper to the model (a model factory does that), which results in two good side-effects:
- switching to another db only happens at the construction time of the models (from within the factory), not from the models themselves. It's not model's responsibility to be aware of the database in use.
- models could reuse the mapper instance, instead of each constructing their own. While we could argue about which is better, it's definitely more resource-friendly to reuse an instance. After all, in a large application there could be thousands of models in use at some time.
Here's what I mean:
PHP Code:abstract class Mapper {}
class MysqlMapper extends Mapper {}
class PgsqlMapper extends Mapper {}
class Model
{
function __construct(Mapper $mapper) {}
}
class AppModelFactory
{
function Create($ModelName)
{
// retrieve the DB type string from the application registry
// create the proper mapper
// create the model, based on $ModelName
// return the model
}
}
// Usage:
$model = AppModelFactory::Create('User');
$model->save();
The database/mapper/model switching is concentrated in these few lines of code for the whole project. The logic of selecting the db lies here.
Also, the end-users' model creation code is lightweight and easy.
Regards
-
Jul 27, 2005, 04:29 #10
- Join Date
- Jul 2005
- Location
- United Kingdom
- Posts
- 86
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
To be honest, my example was pretty bad, in fact, i missed most of the point I was trying to convey. Let me try again.
Lets say we have an abstraction layer:
PHP Code:abstract class Database {}
class MySQLDatabase extends Database {}
class SQLiteDatabase extends Database {}
class DatabaseFactory {
public static function create($type) {
// Code to return an instance of a Database class.
}
}
PHP Code:abstract class Model {}
class ExampleModel extends Model {
private $data;
public function getData() { return $this->data; }
public function setData($data) { $this->data = $data; }
}
PHP Code:abstract class Mapper {}
class ExampleMapper extends Mapper {
private $model;
private $db;
public function __construct($model) {
$this->model = $model;
// Creates an instance of the DB abstraction. Therefore, Mapper composes Database
$this->db = new DatabaseFactory::create('sqlite'); // The Database "type" could here be taken from a registry, passed through a parameter etc.
}
public function insert() {
// Utilise $this->db and $this->model to map $this->model->getData() to a table or tables
}
}
PHP Code:// Controller aggregates Model (nothing new here)
$model = new ExampleModel();
$model->setData('foo');
// Controller also aggregates Mapper (this, therefore, can be considered part of the Data Model layer.
$mapper = new ExampleMapper($model);
$mapper->insert();
-
Jul 27, 2005, 04:31 #11
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by dbevfat
PHP Code:$user = new User();
$mapper->save($user);
PHP Code:$user = $mapperA->get('User', 1);
$mapperB->save($user);
Originally Posted by Lazesharp
-
Jul 27, 2005, 04:34 #12
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Lazesharp
http://argouml.tigris.org/
-
Jul 27, 2005, 04:35 #13
- Join Date
- Jul 2005
- Location
- United Kingdom
- Posts
- 86
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by kyberfabrikken
Heh, don't worry, I got a few things wrong two (my examples for one thing). But instead of drawing attension to that, I've instead opted to draw attention to your admission of incorrectness in the hope that no one will notice mine :P
-
Jul 27, 2005, 04:47 #14
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Lazesharp
I noticed in the following code, you'd be creating an instance of ExampleMapper per each instance of ExampleModel. Is this on purpose or just for sake of simplifying the example ?
PHP Code:// Controller aggregates Model (nothing new here)
$model = new ExampleModel();
$model->setData('foo');
// Controller also aggregates Mapper (this, therefore, can be considered part of the Data Model layer.
$mapper = new ExampleMapper($model);
$mapper->insert();
PHP Code:$model = new ExampleModel();
$model->setData('foo');
$mapper = $registry->getMapper($model);
$mapper->insert($model);
-
Jul 27, 2005, 04:56 #15
- Join Date
- Jul 2005
- Location
- United Kingdom
- Posts
- 86
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by kyberfabrikken
Of course, the method signature for ExampleMapper->insert would change to:
PHP Code:public function insert(ExampleModel $model)
PHP Code:public function insert(Model $model)
-
Jul 27, 2005, 04:56 #16
-
Jul 27, 2005, 07:14 #17
- Join Date
- Jul 2005
- Location
- United Kingdom
- Posts
- 86
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by Lazesharp
See attachment for UML of my earlier code example.
-
Jul 27, 2005, 09:57 #18
Wow, wasn't expecting this many replies already, thanks for that.
To answer a few questions:
Originally Posted by johno
Originally Posted by kyberfabrikken
Originally Posted by kyberfabrikken
-
Jul 27, 2005, 12:59 #19
- Join Date
- Jul 2005
- Posts
- 24
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
just a question...
how would you structure your domain classes such that the mapper would be able to figure out how to perform the CRUD operations? would there be a standard method that would basically list all fields?
in addition, how do you plan on going about the different finder methods if you have only data mapper.
thanks.
-
Jul 27, 2005, 13:17 #20
Originally Posted by gaialucien
Originally Posted by gaialucien
-
Aug 26, 2005, 08:00 #21
- Join Date
- Mar 2005
- Posts
- 10
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I'm a little knew to the database mapper pattern so I have a quesiton regarding retrieving existing classes from the database. From what I can see would you do this by:
PHP Code:$user = $mapperA->get('User', 1);
$mapperB->save($user);
PHP Code:$AccountMapper= new AccountMapper($db);
$account= $AccountMapper->getByUserNameAndPassword($user, $pass);
$account->changeParameterX('sdfsdf');
$account->changeParameterY('sdfsdf');
$AccountMapper->save($account);
Thanks
-
Mar 18, 2009, 06:13 #22
- Join Date
- Jul 2008
- Location
- London, UK
- Posts
- 26
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
hi,
I know this thread is really old but I was just wondering if anyone is now aware of anything out there that implements the data mapper pattern properly. there is phpDataMapper (phpdatamapper.com) but its in very early stages and at the moment it only supports MySQL, I need Postgres support.
If anyone is aware of anything I'd be grateful for the info.
Thanks
Ziad
-
Mar 18, 2009, 11:53 #23
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Several of the existing frameworks have datamappers. Take a look at zend framework, ez components or symphony framework
-
Mar 18, 2009, 14:52 #24
- Join Date
- Jun 2008
- Posts
- 16
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
-
Mar 18, 2009, 16:41 #25
- Join Date
- Jul 2008
- Location
- London, UK
- Posts
- 26
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I was just about to ask about ZF's data mapper! It didn't seem like the Table gateway was doing a full data mapper job but it seems pretty close though.
Is my understanding correct to say that ZF doesn't do a data mapper because Zend_Db_Table doesn't take domain entity objects as its argument for doing inserts/update/delete. Also it returns Zend_Db_Table_Row and Zend_Db_Table_Rowset for selects instead of entity objects?
Thanks
Bookmarks