让我先介绍一下。我正在学习PHP中的OOP。我也研究过设计模式,但还没有完全掌握不同类型的概念。我正处于这样的阶段,每隔几个月我就会意识到我没有按照正确的方式做事,不得不改变我的风格。 这太令人沮丧了。因此,我想找出一劳永逸的正确方法。我试图在Stackoverflow上完整阅读以下主题:
ORM
数据映射器
辛格尔顿
全球是邪恶的
一切相关
但是我仍然不清楚一些事情。我在这里以清晰简洁的方式发布我的代码,并希望人们可以指出两者好的做法和坏的做法。我会在最后列出我的所有问题。
请不要重复关闭,我老老实实地搜索了关于该主题的几乎所有问题,但我仍然想知道一些我无法澄清的事情。对不起,它已经很久了,但我已经尝试组织它,所以它应该读得很好!
我将首先发布我的数据库类的基本知识。
的 database.php中 的
<?php
class DatabaseMySQL{
private static $dbh;
public function __construct(){
$this->open_connection();
}
public function open_connection(){
if(!self::$dbh){
return (self::$dbh = new PDO(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME, DB_USER,DB_PASSWORD)) ? true : false;
}
return true;
}
public function query($sql, $params=array()){
$this->last_query = $sql;
$stmt = self::$dbh->prepare($sql);
$result = $stmt->execute($params);
return $result ? $stmt : $stmt->errorInfo();
}
public function fetch_all($results, $class_name=''){
return $results->fetchAll(PDO::FETCH_CLASS, $class_name);
}
}
?>
这是我的数据库类文件。这个类允许我创建我喜欢这个类的任意数量的实例,它将重用已经实例化的PDO对象作为类的静态属性存储。它还使用PDO从结果集中获取数据,以将数据作为指定类的对象。
我的下一个文件是我所有其他类继承的类。我称之为MainModel。我不知道这是否符合惯例。
的 MainModel.php 的
<?php
abstract class MainModel{
protected static $table;
public function __construct($array=array()){
$this->assign_known_properties($array);
}
public function assign_known_properties($array){
foreach($array as $key=>$value){
$this->$key = $value;
}
}
public static function find_by_id($id){
$db = new DatabaseMySQL();
self::intialise_table_name();
$id = (int) $id;
$sql = "SELECT * FROM ".static::$table." ";
$sql .= "WHERE id = {$id} ";
$result = self::find_by_sql($sql);
return array_shift($result);
}
public static function find_all(){
$db = new DatabaseMySQL();
self::intialise_table_name();
$sql = "SELECT * FROM ".self::$table." ";
return self::find_by_sql($sql);
}
public static function fetch_as_objects($results){
$db = new DatabaseMySQL();
$called_class = get_called_class();
$results = $db->fetch_all($results, $called_class);
return $results;
}
public static function find_by_sql($sql){
$db = new DatabaseMySQL();
$results = $db->query($sql);
return $results ? self::fetch_as_objects($results) : false;
}
public static function intialise_table_name(){
$called_class = get_called_class();
static::$table = strtolower($called_class).'s';
}
public function get_table_fields(){
self::intialise_table_name();
$sql = "SHOW FIELDS FROM ".static::$table." ";
return self::find_by_sql($sql);
}
public function set_table_details(){
$fields = $this->get_table_fields();
$total = count($fields);
$array = array();
foreach($fields as $object){
$array [] = $object->Field;
}
$this->table_details = array('objects'=>$fields,'array'=>$array,'total'=>$total);
$this->set_placeholders_for_new_record();
$this->set_properties_as_array();
$this->set_properties_as_array(true);
}
public function set_properties_as_array($assoc=false){
$array = array();
if (!$assoc){
foreach($this->table_details['array'] as $field){
if(isset($this->$field)){
$array [] = $this->$field;
}else{
$array [] = NULL;
}
}
$this->table_details['values'] = $array;
}else{
foreach($this->table_details['array'] as $field){
if(isset($this->$field)){
$array[$field] = $this->$field;
}else{
$array [$field] = NULL;
}
}
$this->table_details['assoc_values'] = $array;
}
}
public function set_placeholders_for_new_record(){
$string = '';
for($i=0; $i<$this->table_details['total']; $i++){
$string .= '? ';
if(($i+1) != $this->table_details['total'] ){
$string .= ", ";
}
}
$this->table_details['placeholders'] = $string;
}
public function create(){
$db = new DatabaseMySQL();
$this->set_table_details();
$sql = "INSERT INTO ".static::$table." ";
$sql .= " VALUES({$this->table_details['placeholders']}) ";
$result = $db->query($sql, $this->table_details['values']);
// If array is returned then there was an error.
return is_array($result) ? $result : $db->insert_id();
}
public function update(){
$db = new DatabaseMySQL();
$this->set_table_details();
$sql = "UPDATE ".static::$table." ";
$sql .= " SET ";
$count = 1;
foreach($this->table_details['array'] as $field){
$sql .= "{$field} = :{$field} ";
if($count < $this->table_details['total']){
$sql .= ", ";
}
$count++;
}
$sql .= " WHERE id = {$this->id} ";
$sql .= " LIMIT 1 ";
$result = $db->query($sql, $this->table_details['assoc_values']);
return $result;
}
public function save(){
return isset($this->id) ? $this->update() : $this->create();
}
}
?>
总结此文件。我使用像find_by_id($int)
这样的静态方法来动态生成被调用类的对象。我使用Late Static Bindings来访问被调用类的名称,并使用$stmt->fetchAll(PDO::FETCH_CLASS, $class_name)
实例化这些对象,数据库中的数据自动转换为对象。
在每个静态方法中,我实例化DatabaseMySQL类的实例。在每个静态方法中,我通过获取类的名称并向其附加$table
来设置正确的静态s
名称,以便在动态SQL查询中使用。因此,如果我的班级为User
,则会使表名为users
。
在我的构造函数中,我放置了一个可选数组,可用于在创建对象时将某些变量作为对象的属性插入。这样一切都是动态完成的,这让我进入了项目的最后阶段。我的继承类。
的 user.php的 的
class User extends MainModel{
}
的 Question.php 的
class Question extends MainModel{
}
我现在所做的很简单。我可以说:
$user = new User(array('username'=>'John Doe'));
echo $user->username; // prints *John Doe*
$user->save(); // saves (or updates) the user into the database.
我可以通过静态调用User来检索用户。
$user = User::find_by_id(1);
echo $user->username; // prints users name
现在问我的问题:
1)你会用什么名字来称呼这个设计模式(如果它甚至是一个).Data Mapper?依赖注入?域模型(无论是什么)?数据访问层?
2)现在看来这个实施是否结构合理?
3)如果它是好的,我的代码中是否存在我应该注意的命名约定?
4)如果它被认为是好的,你会介意指出你特别喜欢什么,所以我会知道哪一部分肯定会保留?
5)如果您认为不好,您是否愿意详细解释为何如此?
6)我的create
,update
,delete
这些对象的所有方法都与我的find_by_id
属于同一个班级,{{1它们只是静态调用并实际返回对象。如果他们应该分为两个不同的班级,我该怎么办呢?
7)为什么其他人都使用find_all
函数和像mappers这样的花哨字样而我还没有需要它们一次?
答案 0 :(得分:1)
您提出的解决方案称为“Active Record”;为了获得利弊,您可以阅读Martin Fowler的书籍“企业架构模式”,以及您可以在互联网上找到的许多对话。
Fwiw,我个人的观点是,这是构建数据库驱动的应用程序的完美方式,其中主要业务逻辑是读写数据库;在构建更复杂的业务逻辑时,它往往会变得有点麻烦。
此外,Stack Overflow不是为了“请讨论这个主题”的风格问题 - 它是为了得到客观真实答案的答案。所以,你的问题1适合,但其他问题并不适合SO ......