唐山网站建设

设为主页 加入收藏 繁體中文

《PHP设计模式先容》第105章 表数据网关模式

核心提示:前1章中使用动态记录模式对数据库表进行建立,获得,更新(通过扩大实现删除)每1行的操纵。动态记录模式是1种简单的抽象数据库连接的方式,但是这类简洁性也正是它的弱点。

前1章中使用动态记录模式对数据库表进行建立,获得,更新(通过扩大实现删除)每1行的操纵。动态记录模式是1种简单的抽象数据库连接的方式,但是这类简洁性也正是它的弱点。动态记录类只处理单1的行,使得它在需要显现大量信息的WEB利用中显得效率很低,如旅游预约,在线购物等。在这1类利用――几近是主流的WEB利用中,数据集的使用就是更普遍更流行的。

题目

怎样才能简单的操纵数据库表与表中的所有记录?

解决方案

表数据网关模式集成了动态记录模式。实际上,这个新模式的大多数代码都借签于14章动态记录模式的代码(它重用了1样的DB类与BOOKMARK TABEL DDL常量,并且也用ADOdb作为操纵数据的代码库)。但是,表数据网关模式集中于整表――记录集而不是单个的记录。

样本代码

让我们从建立操纵开始,该操纵完成向表中增加新记录。测试用例函数TableDataGatewayTestCase::testAdd() 完成向书签数据表中增加两条URL数据记录的步骤要求。它很大程度上参照了14章ActiveRecordTestCase::testAdd()方法,但它也其明显不同的地方,在于引进了1个新的BookmarkGateway这个表数据网关类。

class  TableDataGatewayTestCase  extends  UnitTestCase  {
function  testAdd()  {
$gateway  =  new  BookmarkGateway($conn  =  DB::conn());
$gateway->add(
‘http://***test.org/’,
‘SimpleTest’,
‘The  SimpleTest  homepage’,
‘testing’);
$gateway->add(
‘http://blog.casey-sweat.us/’,
‘My  Blog’,
‘Where  I  write  about  stuff’,
‘php’);
$rs  =  $this->conn->execute(‘select  *  from  bookmark’);
$this->assertEqual(2,$rs->recordCount());
$this->assertEqual(2,$conn->Insert_ID());
}
}

类似于动态记录,表数据网关测试用例示例了1个模板类,并增加1些记录到数据库。但是表数据网关模的工作对象是整张表,你只需建立1个该模式对象,并重用该对象对便可以向数据表中增加更多的新记录。

这儿是BookmarkGateway1个可行的实现。

class  BookmarkGateway  {
protected  $conn;
public  function  __construct($conn)  {
$this->conn  =  $conn;
}
const  INSERT_SQL  =  “
insert  into  bookmark  (url,  name,  description, tag,  created,  updated)
values  (?,  ?,  ?,  ?,  now(),  now())
“;
public  function  add($url,  $name,  $description,  $group)  {
$rs  =  $this->conn->execute(
self::INSERT_SQL
,array($url,  $name,  $description,  $group));
if  (!$rs)  {
trigger_error(‘DB  Error:  ‘.$this->conn->errorMsg());
}
}
}

以上代码看上往很熟习,动态记录模式与表数据网关模式的基本框架是相仿的:INSERT SQL 语句,函数参数表,对数据库毛病的处理等都与动态记录模式的add()方法1次处理1条记录相类似。

建立了实现CRUD操纵的代码后,现在来讨论如何获得数据。

测试用例结构

由于表数据网关的目的是处理具有多条记录的数据库表,你很有可能需要1个方便有效的方法来初始化表,使得在运行每1个实验时数据表都处于1个已知的状态。快速的解决方案是为每个实验建立1个基类,包括两个有用的方法:setup()与addSeveralBookmark,用来为每个实验重建已打乱的表和载进1些数据。

以下就是名为BaseTestCase的类

class  BaseTestCase  extends  UnitTestCase  {
protected  $conn;
function  __construct($name=’’)  {
$this->UnitTestCase($name);
$this->conn  =  DB::conn();
}
function  setup()  {
$this->conn->execute(‘drop  table  bookmark’);
$this->conn->execute(BOOKMARK_TABLE_DDL);
}
function  addSeveralBookmarks($gateway)  {
//  add(url,  name,  desc,  tag)
$gateway->add(‘http://blog.casey-sweat.us/’
,’Jason\’s  Blog’
,’PHP  related  thoughts’
,’php’);
$gateway->add(‘http://www.php.net/’
,’PHP  homepage’
,’The  main  page  for  PHP’
,’php’);
$gateway->add(‘http://slashdot.org/’
,’/.’
,’News  for  Nerds’
,’new’);
$gateway->add(‘http://google.com/’
,’Google’
,’Google  Search  Engine’
,’web’);
$gateway->add(‘http://www.phparch.com/’
,’php|architect’
,’The  home  page  of  php|architect,
an  outstanding  monthly  PHP  publication’
,’php’);
}
}

现在,每1个测试用例都源自BaseTestCase并继续它的构造器,1个setup()方法与1个addSeveralBookmarks()方法来预装1些数据。

1 2 3 4 下1页

核心提示:前1章中使用动态记录模式对数据库表进行建立,获得,更新(通过扩大实现删除)每1行的操纵。动态记录模式是1种简单的抽象数据库连接的方式,但是这类简洁性也正是它的弱点。

以数组情势返回记录集

不管甚么时候实现1个表数据网关类,你都要选择1种数据结构来显现从相干方法中获得的数据集。PHP中常常使用于表示此数据结构的术语是返回行的哈稀向量(以0为起始的数组),就是1个关联数组,由字段=>值成对组成。

在BookmarkGateway通过ADOconnection取得这样1个数据结构是很简单的,由于ADOResultSet::getArray()方法几近采取的是相同的术语来表示(即返回1个关联数组)。

例如,这里findAll()可方法返回全部表数据网关类中表的内容。

class  BookmarkGateway  {
//  ...
public  function  findAll()  {
$rs  =  $this->conn->execute(‘select  *  from  bookmark’);
if  ($rs)  {
return  $rs->getArray();
}  else  {
trigger_error(‘DB  Error:  ‘.$this->conn->errorMsg());
}
}
}

简单与否,都需要测试1下:

class  TableDataGatewayTestCase  extends  BaseTestCase  {
//  ...
function  testFindAll()  {
$gateway  =  new  BookmarkGateway(DB::conn());
$this->addSeveralBookmarks($gateway);
$result  =  $gateway->findAll();
$this->assertIsA($result,  ‘Array’);
$this->assertEqual(5,  count($result));
}
}

假设你想做得更多,你可以检查1下返回的每1行。

class  TableDataGatewayTestCase  extends  BaseTestCase  {
//  ...
function  testFindAll()  {
$gateway  =  new  BookmarkGateway(DB::conn());
$this->addSeveralBookmarks($gateway);
$result  =  $gateway->findAll();
$this->assertIsA($result,  ‘Array’);
$this->assertEqual(5,  count($result));
$this->assertIsA($result[0],  ‘Array’);
$this->assertEqual(7,  count($result[1]));
$expected_keys  =  array(
‘id’
,’url’
,’name’
,’description’
,’tag’
,’created’
,’updated’);
$this->assertEqual(
$expected_keys
,array_keys($result[3]));
}
}

(索引0,1,和3 都是随机选的,可以是返回的5行中的任意1行),由于在返回的集合中的值是你初始化时存储的值(假定是这样),你也能建立1个实验来直接对数据值进行比较。

class  TableDataGatewayTestCase  extends  BaseTestCase  {
//  ...
function  testFindAll()  {
$gateway  =  new  BookmarkGateway(DB::conn());
$this->addSeveralBookmarks($gateway);
$result  =  $gateway->findAll();
//  ...
$this->assertEqual(‘PHP  homepage’,  $result[1][‘name’]);
$this->assertEqual(‘http://google.com/’,  $result[3][‘url’]);
}
}

返回可迭代的对象集合

数组是PHP内置的数据类型,并且PHP提供了大量的数组函数,使得在你的程序中使用数组非常方便。但是,你有可能希看以对象而不是数组的情势返回结果集。实际上,返回数据载体对象(数据的基本容器,也包括业务逻辑)的集合是很平常的,ADOResultSET()方法恰好证实了这1点。让我们建立1个查找方法,可根据“tag”字段的值来查询数据,由于这个例子基于PHP5,让我们也要求返回的结果集可迭代(参见第8章,迭代模式),用PHP的foreach结构便可以使用该结果集了。

上1页 1 2 3 4 下1页

核心提示:前1章中使用动态记录模式对数据库表进行建立,获得,更新(通过扩大实现删除)每1行的操纵。动态记录模式是1种简单的抽象数据库连接的方式,但是这类简洁性也正是它的弱点。

ADOdb默许返回的是行记录的散列数组,我故意让这个例子稍微复杂1点,来强迫其返回1个数据转输对象,这样代码就故意思多了。并且你将看到这个示例利用了前面学习过的1些设计模式)。

以下测试用例是上述需求的简化表述。

class  TableDataGatewayTestCase  extends  BaseTestCase  {
//  ...
function  testFindByTag()  {
$gateway  =  new  BookmarkGateway(DB::conn());
$this->addSeveralBookmarks($gateway);
$result  =  $gateway->findByTag(‘php’);
$this->assertIsA($result,  ‘AdoResultSetIteratorDecorator’);
$count=0;
foreach($result  as  $bookmark)  {
++$count;
$this->assertIsA($bookmark,  ‘ADOFetchObj’);
}
$this->assertEqual(3,  $count);
}
}

findByTag()方法的实现以下

class  BookmarkGateway{
//  ...
public  function  findByTag($tag)  {
$rs  =  $this->conn->execute(
‘select  *  from  bookmark where  tag  like  ?’
,array($tag.’%’));
return  new  AdoResultSetIteratorDecorator($rs);
}
}

很典型的,findByTag()首先调用execute()方法天生1个数据集。ADOdb的execute()方法带进两个参数,待履行的SQL语句和1个可选的梆定参数变量的数组。由于findByTag()需要用带通配符的LIKE操纵,并且ADOdb会自动的给查询字串加引号,所以必须要给作为参数的数组在其内部就加上通配符。Execute()产生1个记录集后,AdoResultSetIteratorDecorator()将对其进行封包。AdoResultSetIteratorDecorator()的主要目的在于把结果集“转换”为可迭代的对象集合,也因此而得名。

ADOdb通过包括adodb-iterator.inc.php提供对迭代的支持。其中定义了1个ADODB_Iterator的类,实在质是将ADOResultSet修饰成为PHP5的1个迭代接口标准库。这使得你可以快速的构成1个可以遍历的结果集了。但是,迭代器的默许行动还是返回1个聚合数组。正如你将在下述实验中看到的那样。

class  AdoResultSetIteratorDecoratorTestCase  extends  BaseTestCase  {
function  testADOdbDecorator()  {
$gateway  =  new  BookmarkGateway($this->conn);
$this->addSeveralBookmarks($gateway);
$rs  =  $this->conn->execute(‘select  *  from  bookmark’);
foreach($rs  as  $row)  {
$this->assertIsA($row,  ‘array’);
$this->assertIsA($rs->fetchObj(),  ‘ADOFetchObj’);
}
}
}

这儿,通过ADOdb迭代器,表数据便可以够被建立,存储,迭代获得数据。

突出显示的代码行实际是无效,要留意避免。你的确能为每1行天生1个对象,假设这样,你就不能不在你的利用中到处重复这个笨拙的代码来实现对全部集合的迭代。

1个更好的解决方案――能更直接的满足对象集合迭代要求的是:修饰ADOdb迭代器。

测试外部库

写1个小测试用例来帮助你探测第3方库,更好的了解它们的特点。1系列的测试用例也能使你更好的掌控住对外部库的依托性(独立性),或是你的代码是如何特定的使用这些库,这样当库因升级而改变时能更快的找到并解决题目。

假设你担心对这些外部库的依托性,则引进适配器(见第103章--适配器模式)使你的代码从这类依托关系中独立出来。

让我们写1个测试用例来演示迭代器是如何工作的。

class  AdoResultSetIteratorDecoratorTestCase  extends  BaseTestCase  {
//  ...
function  testRsDecorator()  {
$gateway  =  new  BookmarkGateway($this->conn);
$this->addSeveralBookmarks($gateway);
$rs  =  $this->conn->execute(‘select  *  from  bookmark’);
$count=0;
foreach(new  AdoResultSetIteratorDecorator($rs)  as  $bookmark)  {
++$count;
$this->assertIsA($bookmark,  ‘ADOFetchObj’);
$this->assertTrue($bookmark->id  >  0);
$this->assertTrue(strlen($bookmark->url)  >  10);
}
$this->assertEqual(5,$count);
}
}

以下代码说明了怎样改进(修饰)ADODB_Iterator来满足上述的需求。

require_once  ‘adodb/adodb-iterator.inc.php’;
class  AdoResultSetIteratorDecorator  implements  Iterator  {
protected  $rs;
public  function  __construct($rs)  {
$this->rs  =  new  ADODB_Iterator($rs);
}
public  function  current()  {
return  $this->rs->fetchObj();
}
public  function  next()  {
return  $this->rs->next();
}
public  function  key()  {
return  $this->rs->key();
}
public  function  valid()  {
return  $this->rs->valid();
}
public  function  rewind()  {
return  $this->rs->rewind();
}
}

上述代码中,大多数迭代器接口方法已作为代理来处理结果集了。但是current()方法被重载用于返回fetchObj()方法的结果。

上1页 1 2 3 4 下1页

核心提示:前1章中使用动态记录模式对数据库表进行建立,获得,更新(通过扩大实现删除)每1行的操纵。动态记录模式是1种简单的抽象数据库连接的方式,但是这类简洁性也正是它的弱点。

回顾表数据网关,你应当理解findByTage()的工作原理了。

class  BookmarkGateway  {
//  ...
public  function  findByTag($tag)  {
$rs  =  $this->conn->execute(
‘select  *  from  bookmark where  tag  like  ?’
,array($tag.’%’));
return  new  AdoResultSetIteratorDecorator($rs);
}
}

更新记录

下面,让我们来解决CRUD中的“更新”。从概念上讲,你应当让表装满数据,找到1个数据对象,改变后保存它,并且再次找到该数据并校检更改是否是存储。

返回到TableDataGatewayTestCase,这儿有查找记录的代码

class  TableDataGatewayTestCase  extends  BaseTestCase  {
//  ...
function  testUpdate()  {
$gateway  =  new  BookmarkGateway(DB::conn());
$this->addSeveralBookmarks($gateway);
$result  =  $gateway->findByTag(‘php’);
$bookmark  =  $result->current();
$this->assertIsA($bookmark,  ‘ADOFetchObj’);
$this->assertEqual(
‘http://blog.casey-sweat.us/’
,$bookmark->url);
$this->assertEqual(
‘PHP  related  thoughts’
,$bookmark->description);
}
}

并且将代码改成以下所示:

class  TableDataGatewayTestCase  extends  BaseTestCase  {
//  ...
function  testUpdate()  {
$gateway  =  new  BookmarkGateway(DB::conn());
$this->addSeveralBookmarks($gateway);
$result  =  $gateway->findByTag(‘php’);
$bookmark  =  $result->current();
$this->assertIsA($bookmark,  ‘ADOFetchObj’);
$this->assertEqual(
‘http://blog.casey-sweat.us/’
,$bookmark->url);
$this->assertEqual(
‘PHP  related  thoughts’
,$bookmark->description);
$new_desc  =  ‘A  change  to  see  it  is  updated!’;
$bookmark->description  =  $new_desc;
$gateway->update($bookmark);
}
}

改变后,重新查找该条记录并验证更新

class  TableDataGatewayTestCase  extends  BaseTestCase  {
//  ...
function  testUpdate()  {
The Table Data Gateway Pattern   257
$gateway  =  new  BookmarkGateway(DB::conn());
$this->addSeveralBookmarks($gateway);
$result  =  $gateway->findByTag(‘php’);
$bookmark  =  $result->current();
$this->assertIsA($bookmark,  ‘ADOFetchObj’);
$this->assertEqual(
‘http://blog.casey-sweat.us/’
,$bookmark->url);
$this->assertEqual(
‘PHP  related  thoughts’
,$bookmark->description);
$new_desc  =  ‘A  change  to  see  it  is  updated!’;
$bookmark->description  =  $new_desc;
$gateway->update($bookmark);
$result  =  $gateway->findByTag(‘php’);
$bookmark  =  $result->current();
$this->assertEqual(
‘http://blog.casey-sweat.us/’
,$bookmark->url);
$this->assertEqual(
$new_desc
,$bookmark->description);
}
}

有了这样1个实验用例在手,现是在增加update()方法到BookmarkGateway类的时候了。

class  BookmarkGateway{
//  ...
const  UPDATE_SQL  =  ‘update  bookmark  set url  =  ?
,name  =  ?
,description  =  ?
,tag  =  ?
,updated  =  now()
where  id  =  ?’;
public  function  update($bookmark)  {
$this->conn->execute(
self::UPDATE_SQL
,array(
$bookmark->url
,$bookmark->name
,$bookmark->description
,$bookmark->tag
,$bookmark->id
));
}

BookmarkGateway知道如何往履行SQL来更新数据,并能正确的将数据传输对象的属性的值映照到SQL语句相应的参数位置。

讨论

用表数据网关在对表进行操纵,是与WEB利用中任务的履行更密切相干的。但是,表数据网关依然与数据库表具体结构关系过于紧密(耦合)。将代码从表具体结构的依托中独立出来将是下1章数据映照模式的主题。

上1页 1 2 3 4 http://www.fw8.net/


TAG:数据,网关,代码,模式,数组
评论加载中...
内容:
评论者: 验证码: