Skip to content
Advertisement

Assign object attributes from the result of prepared statement

I’m wanting to create a new instance of my Class and assign it’s attributes the values that are returned. The reason for this is I’m creating a series of methods inheriting from the calling class, as opposed to using static methods which I already had working.

Example of what I’m using currently:

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) {
            
    }
}

What I tried was writing an instantiation method that creates a new instance of my class, and then assign each attribute of the object the value it returns from an array from a tutorial I did. However, the tutorial was fairly outdated and didn’t use any new syntax or binding, so I was trying to rework this.

Example from the tutorial below:

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);
}

What I was trying to do from the tutorial, was to return my result as an array using a while, and then assigning a variable by passing the built array into the static::instantation() method, but it doesn’t seem to ever be working correctly, as any public functions I create in my calling class (Admin for example) aren’t called after as they don’t exist due to the Class not being instantiated.

Advertisement

Answer

mysqli_result::fetch_object() accepts the class name as the first argument. You can pass the class name as an argument to that method and get the instance of the model. I am not sure why you have that much code but consider my example which I wrote based on your own code:

<?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);

The output from that example is:

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

As you can see, the code created an instance of the class using late-static binding and it also assigned the value to a private property, which you can’t do otherwise.

P.S. My example is a little bit tidier. I added parameter typing and removed a lot of unnecessary code. In particular, I remove empty try-catch which is a terrible practice.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement