PHP PDO для создания объекта класса с помощью fetchObject

#php #oop #pdo

#php #ооп #pdo

Вопрос:

Я пытаюсь создать объект «User» внутри моего класса «User» на основе свойств, которые я сохранил в своей базе данных, для подключения к которой я использую PDO.

Я поискал несколько страниц в Google и обнаружил, что большинство людей используют для этого метод fetchObject. Прямо сейчас я пытаюсь использовать это для создания объекта этого текущего класса, но я продолжаю получать ошибку:

Неустранимая ошибка: неперехваченный ArgumentCountError: Слишком мало аргументов для функции User::__construct(), передано 0 и ожидается ровно 11 …

Все продолжают использовать это в Интернете, но, похоже, я не могу найти их конструкторы, потому что у них может не быть никаких атрибутов для получения.

Мой текущий код таков:

 public static function createUserBySQL($id)
    {
        $stmt = BD::getConnection()->prepare("SELECT * FROM user WHERE username = ?");
        $stmt->execute([$id]);
        $user = $stmt->fetchObject(__CLASS__);
        var_dump($user);
        return $user;
    }
  

Это может быть легко решить, но я впервые выполняю ООП на PHP с помощью PDO, поэтому я еще не знаю всех советов и хитростей, которые можно так легко обойти 😉

Спасибо,
mikeysantana

БЫСТРОЕ РЕДАКТИРОВАНИЕ: Все атрибуты из экземпляра «User» являются частными, и я хотел сохранить это таким образом, чтобы поддерживать логику кода.

Комментарии:

1. fetchObject() не работает при передаче значений столбцов конструктору. Он создает объект, инициализирует все свойства из столбцов, а затем вызывает конструктор без аргументов.

2. Это почти единственное, что описано в руководстве под этой записью: php.net/manual/en/pdostatement . fetchobject.php

3. fetchObject делает какие-то волшебные вещи. Он непосредственно устанавливает атрибуты объекта. Конструктор не должен ожидать никаких параметров.

Ответ №1:

Вам нужно сделать параметры вашего конструктора необязательными. В документации говорится:

Когда объект извлекается, его свойства присваиваются из соответствующих значений столбца, а затем вызывается его конструктор.

При вызове конструктора аргументы не предоставляются, вот почему вы получаете эту ошибку.

Конструктору необходимо проверить, были ли аргументы предоставлены явно, и заполнять свойства только тогда, когда они есть. Есть несколько примеров того, как это сделать, в разделе комментариев приведенной выше документации.

Ответ №2:

После нескольких попыток мне действительно удалось заставить это работать без установки значений по умолчанию в конструкторах или какого-либо обходного пути. Ключ находится в PDO::FETCH_PROPS_LATE * и массиве аргументов в конце.

Решение

 $ctor_args = array(1, 'a placeholder')// pass an array of placeholder values.

$result = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Post", $ctor_args); 
  

Рабочий пример

 class Post
{
    private $id;
    private $content;

    function __construct($id, $content){
        $this->id = $id;
        $this->content = $content;
    }

    public function getId(){
        return $this->id;
    }
}
  
 $result = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Post", [0,0]); 

print_r($result[0]->getId());
// 1

print_r($result);

// Array
// (
//     [0] => Post Object
//         (
//             [id:Post:private] => 1
//             [content:Post:private] => value from the db
//         )

//     [1] => Post Object
//         (
//             [id:Post:private] => 2
//             [content:Post:private] => it is working!
//         )
// )
  

Предупреждение:

*При PDO::FETCH_PROPS_LATE использовании с PDO::FETCH_CLASS конструктор класса вызывается перед присвоением свойств из соответствующих значений столбца.

Поэтому имейте в виду, что конструктор будет вызываться со значениями-заполнителями, а не с данными из базы данных, поэтому это решение (абсолютно) не рекомендуется, если вы манипулируете входными значениями сразу внутри конструктора. Но отлично работает для простой инициализации свойств класса.

Проблема с этим

 class Count{
        private $number5;

        function __construct($number)
            {
                $this->number5 = $number;

                print_r(10 / $this->number5); // should be 10/5, so print 2
            }
        }        
  
 $result = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Count", [0]); 

// Warning: Division by zero in C:xampphtdocsappModelPost.php on line 15 INF
// Warning: Division by zero in C:xampphtdocsappModelPost.php on line 15 INF
  

Да, он вызывает конструктор дважды.

Комментарии:

1. Чувак, если это то, о чем я думаю, то это прорыв. Надеюсь, вы не ошибаетесь. Собираемся проверить

2. а, понятно. вы только что указали поддельные параметры конструктора. умный, но не совсем то, что я думал.

3. Что ж, это работает, и это проще, чем использовать функции в качестве оболочек или добавлять логические проверки нулей.

4. итак, вы просто могли бы сделать это встроенным массивом, "Post", [0,0]); не знаю, зачем беспокоиться о отдельной переменной

5. Я вижу, если у вас есть какая-то логика внутри конструктора, он будет выполняться с поддельными значениями.

Ответ №3:

Спасибо всем за вашу помощь.

Чтобы помочь будущим читателям этого вопроса, вот мое решение:

Я сохранил метод «createUserBySQL», описанный выше, таким, какой он есть, я только изменил свой конструктор.

Конструктор теперь имеет все свои атрибуты в подписи необязательно, вот так:

 function __construct($name = null, $surname = null ...)
  

Внутри конструктора я выполнил проверку, чтобы увидеть, все ли аргументы были нулевыми. Поскольку у меня их было 11, выполнение инструкции if для всех было бы довольно раздражающим, поэтому я использовал метод func_get_args(), который возвращает массив со всеми аргументами, переданными конструктору.

Окончательный конструктор будет выглядеть примерно так:

 function __construct($firstname = null, $surname = null, ...)
    {
        if (func_get_args() != null){
            $this->firstname = $firstname;
            $this->surname = $surname;
            ...
        }
    }