这是一件很有趣的事
- 需要注意的是,这将获取类中的所有
public 属性并尝试将它们用作查询的一部分。如果您确实需要,可以对它们进行更多过滤。
我们将使用所谓的反射。
这是一把双刃剑。
一方面,我们可以把它放在一个基类中,并用你所有的对象扩展它,它对他们来说就很好了。如果以后您在表格中添加一个字段,同样的交易您不必更改任何内容,它就可以工作。
另一方面,处理它需要一点时间,而不是把它写成一个字符串。其中一些问题可以通过在protected static properties 中缓存查询和缓存属性本身(但不是值)来解决
我将从创建一个抽象类开始。这是解决此问题的一种非常灵活的方法,并且可以重复使用 - 注意:我只测试了 Insert 方法,如果其他部分有任何错误,请见谅。
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();there 的东西,但这是另一天的故事。
我还建议添加一个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;
}
我只是想我会把它放在那里给你另一种方式来访问你可能会发现有用的数据。