這是一個有趣的人做
- 一個需要注意的是,這將採取所有的類
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()'。 –