将持久性添加到MVC模型的最佳实践是什么?

时间:2009-10-25 16:44:30

标签: php model-view-controller frameworks

我正在用PHP实现一个超轻的MVC框架。似乎普遍认为从数据库,文件等加载数据应该独立于模型,我同意。我不确定的是将这个“数据层”链接到MVC的最佳方法。


数据存储与模型

交互
//controller
public function update()
{

 $model = $this->loadModel('foo');
 $data = $this->loadDataStore('foo', $model);

 $data->loadBar(9); //loads data and populates Model
 $model->setBar('bar');
 $data->save(); //reads data from Model and saves

}

控制器在模型和数据存储区之间进行调解

看起来有点冗长,需要模型知道数据存储区存在。

//controller
public function update()
{

 $model = $this->loadModel('foo');
 $data = $this->loadDataStore('foo');

 $model->setDataStore($data);

 $model->getDataStore->loadBar(9); //loads data and populates Model
 $model->setBar('bar');
 $model->getDataStore->save(); //reads data from Model and saves

}

数据存储扩展了Model

如果我们要保存将数据库数据存储扩展到flatfile数据存储区的模型,会发生什么?

//controller
public function update()
{

 $model = $this->loadHybrid('foo'); //get_class == Datastore_Database

 $model->loadBar(9); //loads data and populates
 $model->setBar('bar');
 $model->save(); //saves

}

模型扩展数据存储区

这允许模型可移植性,但像这样扩展似乎是错误的。此外,数据存储区无法使用任何Model的方法。

//controller extends model
public function update()
{

 $model = $this->loadHybrid('foo');  //get_class == Model

 $model->loadBar(9); //loads data and populates
 $model->setBar('bar');
 $model->save(); //saves

}

编辑:模型与DAO

进行通信
//model
public function __construct($dao)
{
    $this->dao = $dao;
}

//model
public function setBar($bar)
{
    //a bunch of business logic goes here
    $this->dao->setBar($bar);
}

//controller
public function update()
{
    $model = $this->loadModel('foo');
    $model->setBar('baz');
    $model->save();
}

对“最佳”选项 - 或替代选项 - 的任何输入都表示赞赏。

5 个答案:

答案 0 :(得分:7)

我不认为它属于控制器 - 这实际上是视图的一部分,它可以来来去去。业务逻辑和持久性不应该依赖于视图或控制器。

单独的服务层使您有机会将该逻辑作为分布式组件公开给非基于浏览器的客户端(例如,移动视图,批处理等)。

我不希望模型以面向对象的方式扩展数据存储,因为继承是一种IS-A关系。如您所知,该模型不是关系数据库;这只是众多持久信息中的一种选择。

我认为这是更多的构成。持久层可以是一个单独的DAO,它可以持久化模型对象,而不需要它们知道它们的寿命更长。或者你有mixin行为,其中模型公布它有CRUD操作的事实,但它只是将请求传递给它们给出的实现。

通常的Spring习语会有一个与控制器分开的服务层。它了解用例和工作单元。它使用模型和持久性对象来实现用例的目标。

MVC意味着三个演员。我想说在典型的应用程序中有更多的层 - 需要添加持久性和服务。

更新:

如果模型对象没有从持久性包中导入任何内容,那么它就不知道它。这是一个简单的例子,使用一个简单的模型类Person及其DAO接口,每个接口都在自己的包中:

package persistence.model;

public class Person
{
    private String name;

    public Person(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
}

请注意,DAO接口导入模型包,但不是相反:

package persistence.persistence;

import persistence.model.Person;

import java.util.List;

public interface PersonDao
{
    Person find(Long id);
    List<Person> find();

    void saveOrUpdate(Person p);
}

答案 1 :(得分:1)

RedBeanPHP使用名为Fuse的系统来解决此问题。 (有关wiki的详细信息:http://redbeanphp.com/community/wiki/index.php/Fuse

//Controller
$foo = R::load("foo", 9);
$foo->bar = "bar";
R::store( $foo );

这里你的模型实际上只不过是一个bean。但是,系统会根据需要将bean与完整的模型融合在一起。

class Model_Foo extends RedBean_SimpleModel {
  public function update() {
    if ($this->bar!="bar") do something... 
  }
}

框架将Model_Foo绑定到动态类型为“foo”的bean,并在保存之前调用update()方法。您可以在此处存储业务规则。除了update()之外,您还可以使用open()和delete()。

答案 2 :(得分:0)

我的投票是沿着Model扩展数据存储的路线走下去。这似乎是最自然的,因为实际上您的模型基础数据存储的扩展。但是,您可能希望使用服务设计模式将模型对象的创建与结果对象的消耗分离。

class Model {
   protected $_tablename == __CLASSNAME__; // or some derivative of the classname
   protected $_datastore;
   protected $_typeOfDataStore;

   function __construct( $type == 'mysql' ) { 
      $this->_typeOfDataStore = $type;
      $this->bindToDataStore();
   }

   function bindToDataStore( $ds ) { 
      $this->_datastore = DataStoreFactory::buildDataStore( $this->_typeOfDataStore );
   }

}

class DataStoreFactory {

   function buildDataStore( $type ) {
     switch ( $type ) {
       case 'flatfile' : return new FlatFileDataStore();
       case 'mysql': return new MySQLDataStore();
     }
   }
}

class DataStoreBase {
   function connect() { }
   function disconnect() { }
   function getData() { }
   // ...
} 

class FlatFileDataStore extends DataStoreBase { }
class MySQLDataStore extends DataStoreBase { 
   function runQuery() { }
}

这是建筑部分。当您需要创建/实现新模型时,您可以简单地扩展模型:

class Users extends Model {
   protected $_tablename = 'users';
   protected $_typeOfDataStore = 'flatfile';

   function getAllUsers() {
      return $this->runQuery( "SELECT * FROM " . $this->_tablename );
   }

}

在你的控制器中:

$usersModel = new Users();
$usersModel->getAllUsers();

您指出数据存储区无法使用任何模型方法的异议是对系统的一个很好的约束。该模型是数据存储区的使用者,因此数据存储区不应该能够直接调用模型对象的函数。该模型应保持控制并在需要时使用数据存储区服务。

答案 3 :(得分:0)

答案 4 :(得分:0)

我最终用数据存储类来装饰我的模型:

//datastore class
public function __call($name, $arguments)
{

    if (!method_exists($this->model, $name))
        throw new Exception('does not exist!');

    return call_user_func_array(array($this->model, $name), $arguments);

}

//controller - decorating
$model = new Model;
$datastore = new Datastore($model);
$datastore->actions();