Модифицированный PDO и lastInsertId()

Это просто новая форма PDO, которую я не совсем понимаю. Я знаю, что подобные проблемы решались на этом сайте. Но меня привлекла эта новая (новая для меня) классовая система PDO. Это гладко и лаконично. Я со всем разобрался, все динамические пользовательские данные ВСТАВЛЯЮТСЯ в базу данных просто отлично. Однако я не могу понять, как включить lastInsertId(), используя этот конкретный стиль. Пользователь не вводит идентификатор сообщения, и я не могу использовать запрос GET, как я обычно получаю идентификатор сообщения.

То, что у меня есть ниже, после многих неудачных попыток, это лучшее, что я могу сделать. Очевидно, это не работает. Любые исправления в моем коде будут высоко оценены.

Вот класс базы данных:

functions.php

class DB{

    private static function connect(){
        $pdo = new PDO('mysql:host=localhost;dbname=poetionpics;charset=utf8', 'root', '');
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $pdo;
    }
    public static function query($query, $params = array()){
        $stmt = self::connect()->prepare($query);
        $stmt->execute($params);

        if(explode(' ', $query)[0] == 'SELECT'){
        $data = $stmt->fetchALL();
        return $data;
        }
    }
    public function lastInsertId(){

        $pdo = new PDO('mysql:host=localhost;dbname=poetionpics;charset=utf8', 'root', '');
    $pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

    return $pdo->lastInsertId();
        }


}

Вот мой код INSERT:

action.php

for($count = 0; $count < count($_POST['hidden_post_title']); $count++){

$post_title = (isset($_POST['hidden_post_title'][$count])) ? strip_tags($_POST['hidden_post_title'][$count]) : NULL;
$post_desc = (isset($_POST['hidden_post_desc'][$count])) ? strip_tags($_POST['hidden_post_desc'][$count]) : NULL;
$newvidurl = (isset($_POST['hidden_vid_url'][$count])) ? strip_tags($_POST['hidden_vid_url'][$count]) : NULL;
$url_1 = (isset($_POST['url1_hidden_id'][$count])) ? strip_tags($_POST['url1_hidden_id'][$count]) : NULL;
$url_2 = (isset($_POST['url2_hidden_id'][$count])) ? strip_tags($_POST['url2_hidden_id'][$count]) : NULL;


DB::query('INSERT INTO cloudbook_posts VALUES (\'\', :post_title,  :post_desc)',
array(':post_title'=>$post_title, ':post_desc'=>$post_desc));

//This is possibly problematic code or wrong location....

$postid = DB::lastInsertId('SELECT id FROM cloudbook_posts WHERE  id=:id',
array(':id'=>$_POST['id']))[0]['id'];

//End problematic code

DB::query('INSERT INTO vid_info VALUES (\'\', :newvidurl, :postid)',
array(':newvidurl'=>$newvidurl, ':postid'=>$postid));

DB::query('INSERT INTO url_1 VALUES (\'\', :url_1, :postid)',
array(':url_1'=>$url_1, ':postid'=>$post_id));
}

echo str_replace(array('hidden_post_title', 'hidden_post_desc',    'url1_hidden_id', 'url2_hidden_id', '{', '}', '"', ',', ':'), '',
htmlspecialchars(json_encode($result), ENT_NOQUOTES));

?>

Что я получаю в базе данных, это:

vid_info table
id  |     newvidurl    | postid
69  |   some user data | 0

Я хочу:

vid_info table
id |       newvidurl    | postid
69 |   some user data   | $postid (see action.php for variable value)

person jedihomeslice    schedule 27.08.2019    source источник
comment
Какова цель [0]['id']?   -  person Dharman    schedule 28.08.2019
comment
Вы используете lastInsertId, но не вставляете, а пытаетесь выбрать.   -  person Dharman    schedule 28.08.2019
comment
вы, кажется, слишком много думаете об этом. Нет причин выбирать последний идентификатор вставки в запросе sql - php сделает это за вас. Вам нужно использовать только $postid = $db->lastInsertId(); или, как вы его использовали, $postid = PDO::lastInsertId()   -  person nomistic    schedule 28.08.2019
comment
В целом это хорошая работа, но не забудьте прочитать мою статью о о распространенных ошибках изящных и лаконичных систем PDO. И имя метода lastInsertId() абсолютно неправильное и вводящее в заблуждение. По крайней мере, назовите это просто вставкой ()   -  person Your Common Sense    schedule 28.08.2019
comment
Дхарман, назначением [0]['id] является выбор поля id из базы данных MySql в таблице cloudbook_posts. Я не показывал таблицу cloudbook_posts, т.к. в этом вопросе нет необходимости, но в последовательном порядке таблица сообщений id | пост_название | post_desc.....   -  person jedihomeslice    schedule 28.08.2019
comment
codereview.stackexchange.com/a/29394/19185   -  person Nikita U.    schedule 02.09.2019


Ответы (1)


Это действительно интересный случай.

Проблема lastInsertId() является прямым следствием неправильного дизайна (и частично скопированного кода, созданного с помощью культа карго).

Эта «новая форма PDO» просто отображает распространенные ошибки (настолько распространенные, что я даже написал статью об этом). И одна из таких проблем:

Вы должны понимать, что каждый экземпляр PDO создает отдельное соединение с сервером БД. Таким образом, вы никогда не должны открывать и закрывать новое соединение в каждой функции. Потому что это значительно замедлит ваш PHP и не позволит вам использовать некоторые функции БД, которые можно использовать только в рамках одного и того же соединения, например, транзакции или получение идентификатора вставки.

Итак, теперь вы можете сказать, что проблема возникает из-за того, что в методе lastInsertId() создается новое соединение. И решение состоит в том, чтобы всегда поддерживать одно и то же соединение.

Есть два способа решить эту проблему: более простой на данный момент, который усложнит задачу в будущем, или тот, который немного сложнее реализовать, но сделает ваш код менее связанным и более простым в обслуживании. Оба они объяснялись в моей другой статье о создании такой «гладкой и лаконичной системы PDO на основе классов» (но на основе большого опыта и вопросов, увиденных здесь, в Stack Overflow).

Самым правильным решением было бы получить этот статический материал, поскольку он трудно поддерживать код. Таким образом, лучшим решением было бы создать обычный класс, а затем создать один экземпляр, который должен передаваться по всему вашему коду. В этом случае все вызовы $this->dbh будут иметь смысл и указывать на один и тот же экземпляр PDO. В этом случае вам придется изменить обозначение DB:query() на $db->query().

Однако код в моей статье использует другой подход к расширению PDO. Ваш подход лучше, так что давайте перепишем ваш код в обычный класс

class DB{

    public function __construct()
    {
        $host = '127.0.0.1';
        $db   = 'poetionpics';
        $user = 'root';
        $pass = '';
        $charset = 'utf8mb4';

        $options = [
            \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
            \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
            \PDO::ATTR_EMULATE_PREPARES   => false,
        ];
        $dsn = "mysql:host=$host;dbname=$db;charset=$charset";
        try {
             $pdo = new \PDO($dsn, $user, $pass, $options);
        } catch (\PDOException $e) {
             throw new \PDOException($e->getMessage(), (int)$e->getCode());
        }
    }
    public function query($query, $params = array())
    {
        $stmt = $this->pdo->prepare($query);
        $stmt->execute($params);
        return $stmt;
    }
    public function lastInsertId()
    {
        return $this->pdo->lastInsertId();
    }
}

Но помните, поскольку вы не расширяете PDO, вы должны реплицировать все методы PDO в своем классе.

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

После получения надлежащего класса БД мы можем переписать ваши запросы

$db = new DB();

$sql = 'INSERT INTO cloudbook_posts VALUES (null, :post_title,  :post_desc)';
$db->query($sql, ['post_title'=>$post_title, 'post_desc'=>$post_desc]);

$postid = $db->lastInsertId();

$sql = 'INSERT INTO vid_info VALUES (null, :newvidurl, :postid)';
$db->query($sql, ['newvidurl'=>$newvidurl, 'postid'=>$postid]);
person Your Common Sense    schedule 28.08.2019
comment
О, спасибо, YCS! Не осознавая, что вы прислали мне этот ответ, я задал вопрос на странице вашей статьи после ее просмотра. Пожалуйста, не обращайте внимания на вопрос. Это было идеально!! Это первый вопрос, который я когда-либо задавал, так что я все еще привыкаю ко всему. Сейчас я одобрю твой ответ, мой друг. - person jedihomeslice; 02.09.2019