PHP Programowanie obiektowe – początki#2 Praca z bazą danych

W poprzednim wpisie z serii o programowaniu obiektowym w PHP zaczęliśmy pracę nad modułem resetowania hasła. Dla zachowania zwięzłości materiału poprzestałem jedynie na walidacji danych wprowadzonych przez użytkownika. Pora więc wykonać kolejny krok czyli zapisać zmiany w bazie danych.

Tak jak w poprzednim wpisie na samym początku chciał bym zaznaczyć że są to moje początki z programowaniem obiektowym, tak w PHP jak i w ogóle. Dlatego w żadnym przypadku nie należy w ciemno przyswajać tego co tutaj przeczytacie.

Postanowienia wstępne

Skoro mamy już dane które przeszły walidację pasowało by sprawdzić czy użytkownik o podanym loginie bądź adresie e-mail rzeczywiście istnieje w bazie danych.

Jeżeli tak dokonujemy edycji 2 kolumn w bazie danych, mianowicie resetPass i resetPassTime. Do pierwszej zapisujemy losowy kod weryfikacyjny do drugiej datę ważności tego kodu, u nas będą to 3 dni.

Jeżeli edycja bazy danych zakończy się powodzeniem to oczywiście wysyłamy wiadomość e-mail do użytkownika ale te kwestę raczej zostawię na osobny artykuł.

Baza danych :MySQL

Oto nasz kod kliencki:


//wykonaj jeżeli nie wykryto błędów walidacji
require_once ($DOCUMENT_ROOT.'/../ini/FunkcjePHP/polacz_z_baza.php');// nawiąż połączenie z bazą danych
if($connection){
  $dbError = new DataBaseError();// obiekt błędów bazy danych
  $dbSelect = new DataBaseSelect($connection,$dbError);// obiekt łączący się z bazą danych
  $requirement = $input->getName().' = "'.$input->getValue().'"';// warunek dla bazy danych
  $user = NewUser::createUser($dbSelect,"id,login,email",$requirement);// pobierz dane użytkownika
  if($dbError->checkFeedback()){
    // wykonaj jeżeli udało się pobrać użytkownika
    $saveResetCode = new SetResetPass($user); // obiekt edytujący dane w bazie danych
    $saveResetCode->saveResetPassCode(new DataBaseEdit($connection,$dbError)); // edytuj dane usera

    // wyślij e-mail
    // dodaj informacje zwrotną
  }
  else{
    // wykryto błędy bazy danych
    $_SESSION['feedback'] = $dbError;// dodaj obiekt z komunikatem do sesji
    header("Location:reset_pass.php");// przekieruj spowrotem na stronę
  }
}

 

Połączenie z bazą danych

W drugiej linijce kodu includujemy plik PHP którego jedynym zadaniem jest nawiązanie połączenia z bazą danych.


 $host = "localhost";//nazwa hosta
 $db_user = "root";//nazwa użytkownika
 $db_password = "";//hasło użytkownika
 $db_name = "zlecenie";//nazwa bazy danych
 
 mysqli_report(MYSQLI_REPORT_STRICT);
 
 try
 {
   $connection = @new mysqli($host, $db_user, $db_password, $db_name);
   $connection ->set_charset("utf8");
 
   if ($polaczenie->connect_errno!=0){
     throw new Exception(mysqli_connect_errno());
     unset ($polaczenie);
   }
 }
 catch(Exception $e)
 {
   echo '<span style="color:red;">Błąd serwera! Przepraszamy za niedogodności i prosimy sprubować ponownie za chwilę.</span>';
   echo '
Informacja developerska: '.$e;
 }

Kolejno tworzę nowy obiekt klasy DataBaseError który nie różni się w zasadzie niczym od klasy ValidError opisanej w poprzedniej części, jedyna różnica to opisy wyłapanych błędów .

Następnie tworzę obiekt klasy DataBaseSelect który właściwie jest furtką do bazy danych dla wszystkich poleceń typu SELECT.  W konstruktorze wymaga obiektów klasy mysqli i DataBaseError które wcześniej utworzyłem.

Na razie jedyną metodą klasy jest metoda userDownload() której zadaniem jest pobranie konkretnych informacji o użytkowniku z bazy danych na podstawie przekazanego warunku i zwrócenie ich w postaci tablicy asocjacyjnej.

Klasa DataBaseSelect:


// abstrakcyjna klasa komunikująca się z bazą danych
abstract class DataBaseConnect
{
  protected $connection;// połączenie z bazą danych
  public $error;// błędy bazy danych
  // end components

  // konstruktor klas pochodnych od DataBaseConnect
  // przyjmuje połączenie z bazą danych i obiekt obsługujący błędy
  // zapisuje przyjęte parametry do składowych obiektu
  public function __construct(
    mysqli $connection,
    DataBaseError $error)
  {
    $this->connection = $connection;
    $this->error = $error;
  }// end construct()

}// end class DataBaseConnect

// klasa pobierająca dane z bazy danych
class DataBaseSelect extends DataBaseConnect
{
  // metoda pobierająca dane o użytkowniku z bazy danych
  // przyjmuje informacje do pobrania, i warunek identyfikujący użytkownika
  // jeżeli znaleziono dokładnie jeden rekord zwraca tablice asocjacyjną z danymi o użytkowniku
  // jeżeli nie zwraca pustą tablicę i dodaje błąd
  public function userDownload(
    string $what,
    string $requirement
  ):array
  {
    $query="SELECT ".$what." FROM users WHERE ".$requirement." AND deleted IS NULL";
    $score = $this->connection->query($query);
    if($score->num_rows == 1){
      $data = $score->fetch_assoc();
      return $data;
    }
    else{
      $this->error->addFeedback(1);
      return $data=[];
    }
  }// end userDownload()

}// end class DataBaseSelect

Pobieranie informacji o użytkowniku

W wierszu nr. 6 tworzę warunek na podstawie którego będę wyciągać dane użytkownika z bazy danych. Warunek to oczywiście nazwa Inputa i jego wartość.

Obiekt ten odwzorowuje element Input utworzyłem go w poprzedniej części tego artykułu.

Następnie a pomocą statycznej metody NewUser::createUser() tworzę obiekt klasy User który odwzorowuje dane o użytkowniku pobrane z bazy danych.

Metodzie tej przekazuję wcześniej utworzony obiekt wyciągający dane z bazy danych, nazwy kolumn które chcemy wyciągnąć oraz utworzony przed chwilą warunek na podstawie którego będzie przeszukiwana baza danych.

Klasa User:


abstract class NewUser
{
  // statyczna metoda fabryka obiektów klasy User
  // przyjmuje obiekt pobierający dane z bazy danych, potrzebne informacje o użytkowniku, warunek identyfikujący użytkownika
  // jeżeli $dbSelect zwróci tablicę z danymi użytkownika, metoda wywołuje konstruktr nowego użytkownika przekaując mu dane pobrane z bazy danych
  public static function createUser(
  DataBaseSelect $dbSelect,
  string $what,
  string $requirement
  ){
    $components = $dbSelect->userDownload($what,$requirement);
    if(!empty($components)){
      return new User($components);
    }
  }// end createUser()

}// end class NewUser





// klasa przechowująca dane o użytkowniku
// obiekty tej klasy konstruowane są jedynie za pomocą metody NewUser::createUser()
class User extends NewUser
{
  private $id = null;
  private $login = null;
  private $email = null;
  private $pass = null;
  private $codeVerivication = null;
  private $verification = null;
  private $joined = null;
  private $lately = null;
  private $personality = null;
  private $firstName = null;
  private $lastName = null;
  private $place = null;
  private $voivodeship = null;
  private $age = null;
  private $description = null;
  private $publicEmail = null;
  private $recomendation = null;
  private $resetPass = null;
  private $resetPassTime = null;
  private $deleted = null;
  // end component



  // konstruktor obiektów klasy User
  // przyjmuje tablice asocjacyjną z danymi o użytkowniku
  // zapisuje dane z tablicy w odpowiedznich składowych
  protected function __construct(array $components)
  {
    $keys = array_keys($components);
    for ($i=0;$i<sizeof($components);$i++){ $this->{$keys[$i]} = $components[$keys[$i]];
    }
  }// end __construct()


  // metoda przechwytująca chybione wywołania metod
  // jeżeli użytkownik wywoła metodę getParametr, metoda sprawdza czy istnieje wywoływany parametr
  // jeżeli parametr istnieje metoda zwraca go
  public function __call(
    string $method,
    array $args
  ){
    $property=lcfirst(substr($method,3));
    if(property_exists($this,$property)){
      return $this->{$property};
    }
  }// end __call()

}// end class User

Edycja bazy danych

W tej chwili wypadało by sprawdzić czy użytkownik którego szukamy rzeczywiście istnieje. I tak właśnie postępuję w linijce nr. 8 sprawdzam czy skrypt nie dodał błędu do obiektu przechowującego błędy bazy dych.

Jeżeli wszystko jest ok to powołuję nowy obiekt klasy SetResetPass któremu przekazuje obiekt przechowujący informację o użytkowniku $user.

W kolejnej komendzie wywołuje jego metodę saveResetPassCode() której przekazuje z kolei nowo utworzony obiekt który jest furtką do edycji bazy danych.

Klasa DataBaseEdit ma póki co jedną metodę czyli editUser() która wymaga trzech argumentów: kluczy, wartości i warunku.

Klasa DataBaseEdit:


// klasa edydująca rekordy w bazie danych
class DataBaseEdit extends DataBaseConnect
{
  // metoda edytująca dane użytkownika
  // przyjmuje tablice z nazwali kolumn, tablicę z wartościami do zapisania, id użytkownika do edytowania
  public function editUser(
    array $keys,
    array $value,
    string $requirement
  ){
    if(sizeof($keys) == sizeof($value)){
      // wykonaj jeżeli jest tyle samo kluczy i wartości
      $query='UPDATE users SET '.$keys[0].' = "'.$value[0].'" ';// zapytanie
      for($i=1;$i<sizeof($value);$i++){ $query .= ', '.$keys[$i].' = "'.$value[$i].'" ';// dodaj kolejne kolumny do zmodyfikowania } $query .='WHERE '.$requirement; // dodaj warunek $this->connection->query($query); // wykonaj zapytanie

      if($this->connection->affected_rows != 1){
        //jeżeli ilość zmodyfikowanych rekordów nie jest równa 1 zarzuć wyjątek
        throw new Exception("Nie udało się zmodyfikować unikatowego rekordu!");
      }
    }
  }// end editUser()

}// end class DataBaseEdit

I właściwie w tym miejscu całą robotę związaną z zapisywaniem kodu resetującego hasło składam na ręce kodu bibliotecznego.

Metoda SaveResetPassCode() generuje kod weryfikacyjny metodą setCode() zaczerpniętą z cechy typowej IntegerType, później ustala datę i dodaje do niej 3 dni a następnie przypisuje wszystko do par klucz/wartość i zapisuje dane na podstawie wcześniej pobranego ID użytkownika.


// klasa obsługująca moduł resetowania hasła
class SetResetPass
{
  private $user;// obiekt reprezentujący użytkownika
  private $code;// kod weryfikacyjny
  // end component



  //konstruktor obiektów klasy ResetPass
  //przyjmuje obiekt klasy User reprezętujący użytkownika
  //zapisuje obiekt $user do składowej $user
  public function __construct(User $user)
  {
    $this->user = $user;
  }// end __construct()

  use IntegerType;// załącz cechy typowe dla liczb całkowitych


  // metoda zapisująca kod reetujący i datę ważności kodu do bazy danych
  // przyjmuje obiekt klasy DataBaseEdit edytujący bazę danych
  public function saveResetPassCode(DataBaseEdit $dbEdit)
  {
    $this->code = self::setCode();// ustaw kod weryfikujący (cecha typowa IntegerType)
    $date = date("Y-m-d H:i:s", strtotime("+3 day"));// ustal datę
    $keys = ["resetPass","resetPassTime"];// ustal klucze do zapiania
    $value = [$this->code,$date]; // ustal wartości do zapisania
    $dbEdit->editUser($keys,$value,"id=".$this->user->getId()); // edytuj dane usera
  }// end saveResetPassCode()

}// end class SetResetPass


//cechy typowe dla wszystkich obiektów typu string
trait IntegerType
{
  // metoda ustalająca 10 cyfrowy pseudo losowy kod
  // zwraca ustalony kod
  protected function setCode()
  {
    $microtime = microtime(true);
    $micro = explode('.',$microtime);
    $code = rand(100000,429495).$micro[1];// utwórz kod resetujący
    return $code;
  }// end setCode()

}// end trait IntegerType

Już prawie koniec

Tak więc przefiltrowaliśmy już dane i zapisaliśmy informacje o próbie zresetowaniu hasła w bazie danych.

Jedyne co pozostało to wysłać użytkownikowi wiadomość e-mail z kodem resetującym ale myślę że o tym napiszę w osobnym artykule.

Pozdrawiam!

5 014 komentarzy