$ pdo возвращает NULL из метода после реализации маршрутизатора - PullRequest
0 голосов
/ 18 марта 2020

Я часами пытаюсь решить эту проблему. Мой веб-сайт работал хорошо, но мне нужно было изменить некоторые ужасные URL-адреса.

Я пытался реализовать роутер (AltoRouter), и все работало очень хорошо после некоторых изменений, пока я не получил эту ошибку:

Неустранимая ошибка: Uncaught Ошибка: вызов функции-члена prepare () для null в следующей строке: X

У меня есть основа c MVC, модель выглядит следующим образом:

В основном $ pdo в этом методе равен нулю: (И каждый метод в этом классе также хорошо)

class UsersModel {
    public function confirmedUser($username) {
        global $pdo;

        $sql = "SELECT * FROM users WHERE username = ? AND confirmed_at IS NOT NULL";
        $request = $pdo->prepare($sql); // Fatal error here
        $request->execute([$username]);
        return $request->fetch();
    }
}

Контроллер выглядит так:

<?php
require_once "../php/connect.php";
require_once "../models/UsersModel.class.php";

$usersModel = new UsersModel();

if(array_key_exists('register', $_POST) && !empty($_POST)) {
    $username = $_POST['username'];
    $email = $_POST['email'];
    $password = $_POST['password'];
    $passwordConfirm = $_POST['passwordConfirm'];

    $usersModel->checkName($username);
    $usersModel->checkEmail($email);
    $usersModel->checkPassword($password, $passwordConfirm);
}

И мой файл подключения :

<?php

const DBNAME = "imparfait";
const DBUSER = "root";
const DBPASS = "";

$pdo = new PDO('mysql:host=localhost;dbname='.DBNAME.';charset=utf8', DBUSER, DBPASS);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Маршрутизатор выглядит следующим образом: (немного грязно)

$router = new AltoRouter();

// map routes
$router->map('GET', '/', 'home', 'Home');
$router->map('GET', '/contact', 'contact', 'Contact');
$router->map('GET', '/about', 'about', 'About');
$router->map('GET', '/[*:slug]/[i:id]', 'article', 'Article');

$router->map('GET', '/login', function() {
    require './controller/users/login.php';
}, 'Login');
$router->map( 'POST', '/login', function() {
    require './controller/users/login.php';
});

$router->map('GET', '/forgot-password', function() {
    require './controller/users/forgotPassword.php';
}, 'Forgot');

$router->map( 'POST', '/forgot-password', function() {
    require './controller/users/forgotPassword.php';
});

$router->map('GET', '/logout', 'logout', 'Logout');

$router->map('GET', '/register', function() {
    require './controller/users/register.php';
}, 'Register');
$router->map( 'POST', '/register', function() {
    require './controller/users/register.php';
});

$router->map('GET', '/account', function() {
    require './controller/users/account.php';
}, 'Account');

$match = $router->match();

// call closure or throw 404 status
if(is_array($match)) {
    require './views/inc/header.phtml';

    if (is_callable( $match['target'])) {
        call_user_func_array( $match['target'], $match['params'] );
    } else {
        $params = $match['params'];
        require "./controller/{$match['target']}.php";
    }

    require './views/inc/footer.phtml';
} else {
    // no route was matched
    header( $_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
    require './views/404.php';
}

Я осматривал переполнение стека, но каждое сообщение относится к объекту базы данных.

I Не понимаю, что может повлиять на значение $ pdo после реализации маршрутизатора. Нужно ли создавать класс для решения этой проблемы?

Любая помощь приветствуется.

1 Ответ

1 голос
/ 19 марта 2020

@ AbraCadaver уже объяснил вам причину, по которой вы столкнулись с указанной проблемой. Таким образом, в этом отношении все кредиты go ему.

Обратите внимание, что при правильной реализации приложения MVC соединение с базой данных используется только компонентами уровня модели .

Итак, имея в виду ваш текущий дизайн, правильное решение - передать экземпляр $pdo каждому объекту модель , созданному в области действия * 1010 соответствующего маршрута. * target .

Итак, UsersModel должен получить $pdo в качестве зависимости:

class UsersModel {

    /**
     * Database connection.
     * 
     * @var \PDO
     */
    private $connection;

    /**
     * @param PDO $connection Database connection.
     */
    public function __construct(\PDO $connection) {
        $this->connection = $connection;
    }

    public function confirmedUser($username) {
        // ...Please, no global variables anymore!...

        $sql = 'SELECT * 
                FROM users 
                WHERE 
                    username = :username AND 
                    confirmed_at IS NOT NULL';

        $statement = $this->connection->prepare($sql);

        $statement->execute([
            ':username' => $username,
        ]);

        $data = $statement->fetch(PDO::FETCH_ASSOC);

        /*
         * As I recall, PDOStatement::fetch returns FALSE when no records are found - instead of 
         * an empty array, like PDOStatement::fetch returns. But, in terms of PDO, a returned 
         * FALSE means "failure" and triggers an exception.
         * So I handle this "special" situation here.
         */
        return ($data === false) ? [] : $data;
    }
}

Контроллер (например, path-to/controller/users/register.php) должен затем передать $pdo объект для только что созданного UsersModel экземпляра (см. внедрение зависимостей ):

<?php
require_once '../php/connect.php';
require_once '../models/UsersModel.class.php';

$usersModel = new UsersModel($pdo);

if(/*...*/) {
    //...

    $usersModel->confirmedUser($username);
}

Предложение: всегда используйте require вместо require_once при включении файла, в котором соединение с базой данных создано:

require "../php/connect.php";
...