所以我使用PDO :: FETCH_CLASS创建一个新对象,使用数据库中同名列中的值填充属性:
$db = Connection::get();
$sql = $db->prepare("SELECT * FROM table WHERE id = :id");
$sql->bindParam(":id", "1");
$sql->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'table');
$sql->execute();
$newobj = $sql->fetch();
将对象的属性插入到表中的相应列中是否存在相反的情况,以节省键入一大堆bindParam()和长SQL查询?
非常感谢
答案 0 :(得分:1)
这是一个有趣的事情
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();
的内容,但这是另一天的故事。
我还建议添加一个delete
方法,这样您就可以获得所有程序员恶魔一直在谈论的整体CRUD
体验。
我能想到的另一个主要改进是你可以在静态属性中存储一些这些东西,基本上是在它第一次运行后缓存它。这样可以多次节省一些类的重新处理(内省)。
虽然它有点棘手但你想要进一步分解。我可以给你的一个提示是确保在基类中使用static::$Var
而不是self::$Var
,所以你使用了名为Late Static Binding
的whats。这基本上是棘手的部分,因为你可以拥有完全不同的后代类。我不确定这是否是正确的术语,但它是一种范围解决问题。
但我会把这些最后的事情留给你。这个例子应该指向正确的路径(或者至少是我知道的路径),并给出一些可能的想法。
最后一件事是使用这样的字符串访问属性或调用方法是完全正确的(假设它们当然存在)
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;
}
我只是认为我会把它放在那里给你另一种方法来访问你可能会觉得有用的数据。
答案 1 :(得分:0)
我想你想做这样的事情。参考(The only proper) PDO tutorial
$arr = [1,2,3];
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?";
$stm = $db->prepare($sql);
$params = array_merge([$foo], $arr, [$bar, $baz]);
$stm->execute($params);
$data = $stm->fetchAll();
这可能会让您更好地了解这一点,如果我没有正确理解您的问题,请告诉我。