根据预准备语句的结果分配对象属性

Assign object attributes from the result of prepared statement

提问人:Nathan 提问时间:3/9/2021 最后编辑:DharmanNathan 更新时间:3/10/2021 访问量:54

问:

我想创建一个 Class 的新实例,并为其属性分配返回的值。这样做的原因是我正在创建一系列从调用类继承的方法,而不是使用我已经使用的静态方法。

我目前使用的示例:

public static function findById($id) {      
    $id = self::escapeParam($id);       
    $idVal = is_int($id) ? "i" : "s";
        
    $sql = "SELECT * FROM ".static::$db_table." WHERE id = ? LIMIT 1";
        
    return static::findByQuery($sql,$idVal,$id);
}

public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
    try {       
        $callingClass = get_called_class();

        $object = new $callingClass;

        $statement = Database::$connection->prepare($sql);
        if(!empty($bindChar)) :
            $statement->bind_param($bindChar, $bindVal);
        endif; 
        if($statement->execute()) :
            $result = $statement->get_result();
            $object = $result->fetch_object();
        endif;
        $statement->close();
            
        if(!empty($object)) :
            return $object;
        endif;  
            
    } catch(Exception $e) {
            
    }
}

我尝试的是编写一个实例化方法,该方法创建我的类的新实例,然后为对象的每个属性分配它从我所做的教程中的数组返回的值。但是,该教程已经过时,并且没有使用任何新的语法或绑定,因此我试图对其进行返工。

以下教程中的示例:

public static function find_by_id($id) {
    global $database;
    $the_result_array = static::find_by_query("SELECT * FROM " . static::$db_table . " WHERE id = $id LIMIT 1");

    return !empty($the_result_array) ? array_shift($the_result_array) : false;
}

public static function find_by_query($sql) {
    global $database;
    $result_set = $database->query($sql);
    $the_object_array = array();
    while($row = mysqli_fetch_array($result_set)) {
        $the_object_array[] = static::instantation($row);
    }
    return $the_object_array;
}

public static function instantation($the_record){

   $calling_class = get_called_class();

   $the_object = new $calling_class;

   foreach ($the_record as $the_attribute => $value) {
      if($the_object->has_the_attribute($the_attribute)) {
         $the_object->$the_attribute = $value;
      }
   }

   return $the_object;
} 

private function has_the_attribute($the_attribute) {
   return property_exists($this, $the_attribute);
}

我在本教程中试图做的是,使用 一段时间将我的结果作为数组返回,然后通过将构建的数组传递到方法中来分配一个变量,但它似乎永远无法正常工作,因为我在我的调用类(例如 Admin)中创建的任何公共函数都不会被调用,因为它们不存在由于类未实例化而存在。static::instantation()

php oop mysqli

评论


答:

0赞 Nathan 3/10/2021 #1

我现在已经有了这个工作,尽管我觉得这可能不是最好的方法。

我主要是前端,所以如果有改进或最佳实践,请发表评论。

    public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
        try {
            
            $statement = Database::$connection->prepare($sql);
            if(!empty($bindChar)) :
                $statement->bind_param("$bindChar", $bindVal);
            endif; 
            if($statement->execute()) :
                $result = $statement->get_result();
                $output = $result->fetch_object();
            endif;
            $statement->close();
            
            if(!empty($output)) :
                $class = get_called_class();            
                $object = new $class;
            
                foreach(get_object_vars($output) as $key => $value) :
                    $object->$key = $value;
                endforeach;             
            endif;
                        
            if(!empty($object)) :
                return $object;
            endif;  
            
        } catch(Exception $e) {
            
        }
    }

我最初的想法是声明一个对象,然后我认为 PHP fetch_object调用只会在启动类后为我的对象分配它的属性,但事实并非如此。

因此,我所做的是,如果语句成功并创建了结果对象,然后我使用 get_object_vars() 命令获取对象属性和值,然后将它们作为键值对循环,为每个属性分配其返回值。

我可以确认这有效,因为我现在可以从我的删除脚本中运行 $admin->remove(),而不是我之前必须做的是 Admin::remove($id);

评论

0赞 Dharman 3/10/2021
你不能做吗?$output = $result->fetch_object(get_called_class());
0赞 Nathan 3/10/2021
谢谢 - 我可以确认这有效并且简单得多。
0赞 Dharman 3/10/2021
我可以把它作为一个答案
0赞 Nathan 3/10/2021
是的,请做。我的错误是将called_class作为$object启动,然后执行 $object = $result->fetch_object(),而不是仅将calling_class作为返回的对象传入。
1赞 Your Common Sense 3/10/2021
我建议通过不传递字符串而是数组并像这样调用 bind param 来允许绑定多个值来扩展此类的功能$statement->bind_param($bindChar, ...$bindVal)
0赞 Dharman 3/10/2021 #2

mysqli_result::fetch_object()接受类名作为第一个参数。可以将类名作为参数传递给该方法,并获取模型的实例。我不确定你为什么有那么多代码,但考虑一下我根据你自己的代码编写的示例:

<?php

class Model
{
    public static function findByQuery(string $sql, ?string $bindChar = null, ?string $bindVal = null): ?static
    {
        $statement = Database::$connection->prepare($sql);
        if ($bindChar) :
                $statement->bind_param($bindChar, $bindVal);
        endif;
        $statement->execute();
        $result = $statement->get_result();
        return $result->fetch_object(static::class);
    }
}

class User extends Model
{
    private $id;
}

class Database
{
    public static mysqli $connection;
}

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Database::$connection = new mysqli('localhost', 'user', 'password', 'test');

$user = User::findByQuery('SELECT ? as id', 's', 'Dharman');
var_dump($user);

该示例的输出为:

object(User)#4 (1) {
  ["id":"User":private]=>
  string(7) "Dharman"
}

正如你所看到的,该代码使用后期静态绑定创建了一个类的实例,它还将值分配给私有属性,否则你无法这样做。

P.S. 我的例子更整洁一些。我添加了参数输入并删除了很多不必要的代码。特别是,我删除了空的 try-catch,这是一种可怕的做法。

评论

0赞 Nathan 3/10/2021
一旦我走到这一步,捕获就会启动一个通知方法。您还会根据类似的东西删除并执行此操作吗?if($admin->remove()) : // 成功 // else : Notification::message('message here');endif;