Reputation: 33
Since the enum feature was released in PHP8.1, I was wondering how can I fetch data from my database with PDO into an object with an ENUM property.
I have the following enum:
enum UserType{
case Master: 1;
case Admin: 2;
case Manager: 3;
}
And I have the following class:
class User{
private int $id;
private string $name;
private UserType $userType;
}
Every time I try to execute the code below I get the error Cannot assign int to property User::$userType of type UserType
Database::getInstance()->fetchObject(sql: "SELECT id, name, userType FROM user WHERE id = 1", class_name: User::class);
I want to know if there is a way to make the code above works or what is the best way to implement the new enum feature in my code.
My fetchObject code:
public function fetchObject($sql, array $args = array(), string $class_name = "stdClass"): mixed
{
$sql = self::$instance->prepare($sql);
if(empty($args)){
$sql->execute();
} else{
$sql->execute($args);
}
$object = $sql->fetchObject($class_name);
$sql->closeCursor();
return $object;
}
Upvotes: 3
Views: 891
Reputation: 33305
You can't do it with fetchObject()
. The reason is that you can't assign int
to a property of type UserType
. You can use a magic method __set()
, but you would have to not declare the property (this is highly not recommended).
You can use __set
with lazy property initialization and PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE
. If that's confusing for you, this is because it is confusing.
enum UserType:int {
case Master = 1;
case Admin = 2;
case Manager = 3;
}
class User
{
private int $id;
private string $name;
private UserType $userType;
public function __construct()
{
// unset it for lazy initialization (PDO will call __set method instead)
unset($this->userType);
}
public function __set($key, $value)
{
if ($key === 'userType') {
$this->userType = UserType::from($value);
}
}
}
// Tell PDO to call constructor first,
// then try to assign the property if it exists or call magic method __set()
$stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, User::class);
$user = $stmt->fetch();
It might be easier to just stick to doing this in the constructor. You could then fetch the row as an associative array and unpack it into the constructor.
class User
{
private UserType $userType;
public function __construct(private int $id, private string $name, int $userType)
{
$this->userType = UserType::from($userType);
}
}
Then your fetchObject()
would look like:
public function fetchObject($sql, array $args = [], string $class_name = "stdClass"): ?object
{
$stmt = self::$instance->prepare($sql);
$stmt->execute($args ?: null);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row ? new $class_name(...$row) : null;
}
You need to unpack the row because PDO doesn't have the capability to pass values to the constructor.
Upvotes: 3