这是一个有趣的人做
- 一个需要注意的是,这将采取所有的类
public
性质,并尝试使用它们作为查询的一部分。如果你真的需要,你可以过滤它们。
我们将使用所谓的反射。
这是一把双刃剑。
一方面,我们可以把它放在一个基类中,并用它的所有对象扩展它,它对它们来说工作得很好。如果在以后的日子里你添加了一个字段到表中,同样的处理你不需要改变任何东西,它只是起作用。
另一方面,它需要一点时间来处理这个,而不是说它写成一个字符串。其中一些可以通过缓存protected static properties
内部的查询并缓存属性本身来解决(但不是值)。
我将从抽象类开始。这是一个非常灵活的方法来解决这个问题,它可以自我重用 - 注意:我只测试了插入方法,所以如果其他部分有任何错误,请原谅我。
abstract class BaseObject
{
public $id;
protected static $_DB;
public function getDB(\PDO $db){
if(!self::$_DB){
//@todo: connect to Database
}
return self::$_DB;
}
public function getId(){ return $this->id; }
public function setId($id){ $this->id = $id }
//returns the records id
public function save(){
//if there is no ID then we know it didn't come from the DB
if($this->id)
return $this->update();
else
return $this->insert();
}
// query format = 'INSERT INTO table (id, ...)VALUES(:id, ...)'
public function insert(){
$this->validate();
$db = $this->getDB(); //localize
$R = new \ReflectionObject($this);
$props = (array)$R->getProperties()[0];
$names = array_keys($props);
$sql = 'INSERT INTO '.$this->getTable().' ('.implode(',', $names).')VALUES(:'.implode(', :', $names).')';
$params = array_combine(
array_map(function($item){
return ':'.$item;
}, $names),
$props
);
$stmt = $db ->prepare($sql);
$stmt->execute($params);
$this->id = $db->lastInsertId(); //don't forget to update the id
return $this->id;
}
// query format = 'UPDATE table SET prop=:prop, ... WHERE id=:id'
public function Update(){
$this->validate();
$db = $this->getDB(); //localize
$R = new \ReflectionObject($this);
$props = (array)$R->getProperties()[0];
$names = array_keys($props);
$sql = 'UPATE '.$this->getTable().' SET ';
$set = [];
$params = [];
foreach($props as $name=>$value){
$params[':'.$name] = $value;
if($name == 'id') continue;
$set[] = "$name = :$name";
}
$sql .= implode(', ', $set).' WHERE id=:id'
$stmt = $db->prepare($sql);
$stmt->execute($params);
return $this->_id;
}
abstract public function getTable();
abstract public function vallidate();
}
然后在你的具体类,你只需要实现的抽象方法,并添加其他属性具体到他们
class Dude extends BaseObject
{
public $name;
public function getName(){ return $this->name ; }
public function setName($name){ $this->name = $name }
public function getTable(){
return 'dudes';
}
public function validate(){
if(empty($this->name)) throw new \Exception("Name cannot be empty");
//...etc.
}
}
有一件事我偶然想到的,你应该知道的,当加载一个类 - 通过PDO结果时,类的构造函数不被调用。我一直以这种方式加载一个类,并且可能有办法强制它调用构造函数,或者我可能只是回忆不正确。但这是值得一提的。
我提到的原因是抽象类需要一个PDO实例,所以我添加了getDB()
方法。这只是一个简单的例子,说明如何缓存所有类的数据库连接(我懒得做实际的连接部分,对不起)
我个人使用的是我所需要的所谓的Singleton
我只想在这里打电话self::$_DB = DB::getInstance();
,但这是另一天的故事。
我也建议增加一个delete
方法,这样你就可以得到整个CRUD
的经验,你所有的程序员都会一直在讨论这个问题。
我能想到的另一个主要改进是你可以将这些东西存储在静态属性中,基本上在第一次运行后缓存它。这可以节省一些重复处理(内省)多次的课程。
虽然它有点棘手,你会想进一步打破事情。我可以给你的一个提示是确保在基类中使用static::$Var
,而不是self::$Var
,所以你使用什么叫做Late Static Binding
。这基本上是棘手的部分,因为你可以有完全不同的东西的后代类。我不确定这是否是正确的术语,但它是一种范围解决问题。
但我会留下最后这些东西给你。这个例子应该指出你正确的道路(或者至少我认识的一条道路),并且告诉你一些可能的东西。
最后一两件事,这是完全正常的访问属性或使用类似这样的字符串调用方法(假设它们的存在,当然)
foreach($props as $name=>$value){
//for this we will say $name = 'id'
$method = "get".ucFirst($name); // 'getId'
$a = $this->$method(); // calls $this->getId()
$a = $this->$name; //access property $this->id;
}
我只是想我会把那个在那里给你另一种方式访问您可能会觉得有用的数据。
是的,你可以在'execute()'方法中传递一个数组,而不是使用'bindParam()'。 –