Расширенный класс, вытягивающий NULL - ООП

У меня есть следующий класс для всех моих пользовательских методов:

class User {
  protected $_db,
            $_data;

  public function __construct($user = null, $findby = 'id') {
    $this->_db = DB::getInstance();

    if (!$user) {
      ........
    } else {
      ........
    }
  }

 .......

  public function login($username = null, $password = null) {
    $user = $this->find($username, 'username');
    if ($user) {
      $lockdown = new Lockdown; 
    }
  }

  public function find($param = null, $method = null) {
    if ($param && $method) {
      $data = $this->_db->query("SELECT * FROM users ...");
      if ($data->count()) {
        $this->_data = $data->result();
        return true;
      }
    }
    return false;
  }

  public function data() {
    return $this->_data;
  } 
}

Выше приведена полностью урезанная версия моего пользовательского класса. У меня также есть другой класс (блокировка), который расширяет пользователя:

class Lockdown extends User {
  public $getAttempts;

  public function __construct() {
    var_dump($this->data());
    die();
  }
}

Однако, когда я вызываю класс блокировки внутри класса входа в систему, даже если объект данных должен содержать всю информацию о пользователе, var_dump() просто возвращает NULL.

По моим расчетам, когда вызывается класс входа в систему, метод поиска должен установить $_data = USER INFO, что, следовательно, должно позволить новому методу Lockdown, вызванному сразу после ($this->find()), выполнить иметь доступ к одному и тому же методу данных.

Я все еще изучаю ООП-программирование, поэтому не знаю, есть ли что-то, что мне не хватает, но я не могу понять причину, по которой класс Lockdown возвращает NULL в методе данных, когда он должен его наследовать.


person Matthew M    schedule 03.11.2017    source источник
comment
Привет, @mic, спасибо за ответ. Я добавил класс User::__construct() в исходный вопрос и не вижу, как его вызов поможет заполнить переменную _data. Могу я спросить, почему переменная _data не заполняется, поскольку код, кажется, предполагает, что метод данных должен быть выполнен перед вызовом Lockdown?   -  person Matthew M    schedule 03.11.2017
comment
В __construct() вашего Lockdown вам нужно добавить parent::__construct() перед вызовом var_dump   -  person Ole Haugset    schedule 03.11.2017
comment
parent::__construct() работал до тех пор, пока я вводил имя пользователя в метод __construct(). Все работает теперь спасибо. Однако еще одна быстрая вещь: почему это parent::__construct(), когда ни один из моих методов не является статическим?   -  person Matthew M    schedule 03.11.2017
comment
@MatthewM, вам нужно принять к сведению ответ Терешко.   -  person JayIsTooCommon    schedule 03.11.2017
comment
Передача $this в качестве параметра конструктора? Вы сумасшедший или просто заблуждаетесь?   -  person tereško    schedule 03.11.2017


Ответы (1)


Вы не должны помещать какую-либо вычислительную логику внутрь конструктора. Это затрудняет тестирование. Вы также не можете вернуться из конструктора.

Ваша структура - полная катастрофа. Как из-за вашего злоупотребления наследованием, так и глобальным состоянием.

Для класса нет смысла создавать новый экземпляр своего собственного дочернего класса для извлечения данных. Вероятно, это результат того, что вы пытаетесь совместить в классе User две разные функции: постоянство и бизнес-логику. Это представляет собой нарушение принципа единой ответственности, что затем проявляется в виде запутанного графа вызовов. .

Также вся конструкция class Lockdown extends User не имеет смысла. Ключевое слово extends в ООП можно перевести как «частный случай» (согласно LSP) . Класс для отслеживания попыток входа пользователя в систему не является частным случаем «пользователя».

Для этого у вас должно быть как минимум 3 отдельных класса: один для обработки "поведения пользователя" и другой для сохранения/восстановления "состояния пользователя" (подход называется "сопоставитель данных"). Третий будет для управления неудачными попытками.

Я также настоятельно рекомендую посмотреть эту лекцию.

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

Что касается кода, то на высоком уровне он, вероятно, должен выглядеть примерно так:

$user = new User;
$mapper = new UserMapper($db);

$user->setName($username)
if ($mapper->fetch($user)) {
   if ($user->matchPassword($password)) {
       // you have logged in
       // add some flag in session about it
       header('Location: /greetings');
       exit;
   }
   // check the failed attempts
} else {
  // no matching username 
}
person tereško    schedule 03.11.2017