News & Blog

CRUD – PDO – 1. rész

News & Blog

Forrás

A cikksorozat forrása egy UDEMY oktatóvideó, amely az alábbi linken érhető el. A videóban szereplő technikák kiegészítve, részletes magyarázatokkal ellátva jelennek itt meg.
A cikksorozat célja, hogy a programozást tanuló diákjaink magyarul, részletes magyarázattal érjenek el egy korszerű programozási technikát. Kérek mindenkit, hogy a cikkek felhasználása esetén mindenképpen jelölje meg a félreértések elkerülése végett az eredeti videó és a szöveges fordítás forrását is!

CRUD, PDO

Mi is az a CRUD? Egy mozaikszó, amely a Create-Read-Update-Delete szavak kezdőbetűiből áll össze. És mi a PDO? Egy olyan PHP objektum (PHP Data Object), mellyel az adatbázis kapcsolatokat, lekérdezéseket tudjuk egységes módon, az adatbáziskezelő rendszertől függetlenül kezelni. Szinte minden adatokkal foglalkozó programban ezeket a műveleteket meg kell valósítani, így célszerű erre írni egy olyan kódot, amelyet több helyen is felhasználhatunk. Ezt fogjuk most tenni.

Az adatbázis létrehozása, csatlakozás az adatbázishoz

MySQL környezetben hozzuk létre a testdb nevű adatbázist a userdetails táblával. (Az adatbázis és a táblák létrehozásával nem foglalkozunk, használhatjuk például a phpMyAdmin vagy más felületet is. ) A tábla 3 mezőt tartalmaz, a mezőnevek önmagukért beszélnek.

id (INT, autoincrement, primary key)
name (VARCHAR(100))
email (VARCHAR (100))

Az adatbázis kezeléséhez hozzunk létre egy DB objektumot, amelynek metódusai fogják az egyes adatbázis műveleteket végrehajtani. Az alábbi kódot a db.php állományban helyezzük el.

<?php

class DB{
    private $dbHost = "localhost";
    private $dbUser = "root";
    private $dbPassword = "mysqlpasswd";
    private $dbName = "testdb";
    private $conn;

    public function __construct() {
      try {
        $dsn="mysql:=".$this->dbHost.";dbname=".$this->dbName;
        $this->conn = new PDO($dsn,$this->dbUser,$this->dbPassword);
        $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      } catch (PDOException $e) {
        die("Az adatbázis csatlakozás nem sikerült!".$e->getMessage());
      }
    }
?>

Az adatbáziskezeléshez a PDO-t használjuk. Az osztály privát változóiban adjuk meg az adatbázis helyét és nevét, és az adatbázist kezelő felhasználó hozzáférését. Így a program más környezetben is könnyen használható, ezen tulajdonságok módosításával. A DB osztály konstruktorában valósítjuk meg a csatlakozást, így ha valahol példányosítjuk a DB-t, ez azonnal megtörténik. PDO az adatbázishoz való csatlakozáshoz a DSN (Data Source Name) karakterláncot használjuk. Ebben megadjuk, hogy mysql adatbázis használunk, amely a localhost-on keresztül érhető el, és a neve testdb. (Csak itt ugye az objektumunk tulajdonságain keresztül adjuk meg ezeket az adatokat. Behelyettesítve a DSN karakterlánc így néz ki:)

$dsn = "mysql:host=localhost;dbname=testdb";

A példányosított PDO objektum referenciáját a conn tulajdonságban tároljuk, ezt használjuk majd minden adatbázis műveletnél. A PDO objektum létrehozásához a DSN karakterláncra, és az adatbázist létrehozó felhasználó adataira (név és jelszó) van szükség. Behelyettesítve ez a következőképpen néz ki:

$this->conn = new PDO($dsn,"root","mysqlpasswd");

Természetesen a példányosítást kivételkezeléssel valósítjuk meg, így ha nem sikerül a csatlakozás, a program hibaüzenettel leáll. Miután sikeresen csatlakoztunk az adatbázishoz, beállítjuk, hogy hibás SQL lekérdezés esetén is kivételt dobjon a program.

Adatok beszúrása

A következő lépésben megírjuk azt a kódot, amellyel adatokat tudunk beszúrni a táblába. Ehhez hozzunk létre először egy HTML űrlapot az index.php fájlban, amely bekéri a nevet és az email címet. Ebben a fájlban példányosítjuk az adatbázis objektumunkat is.

<?php
    require_once("db.php");
    $db = new DB();
?>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width", initial-scale=1.0>
  <meta http-equiv="X-UA-Comaptible" content="ie-edge">
  <title>User details</title>
</head>
<body>
  <form action="insert.php" method="POST">
    <input type="text" placeholder="Name" name="name">
    <input type="text" placeholder="Email" name="email">
    <input type="submit" value="Insert" name="insertData">
  </form>
</body>
</head>
</html>

Az űrlapunk POST metódussal az insert.php fájlnak adja át feldolgozásra a bevitt adatokat. A fájlban a bevitt adatokat kiemeljük a $_POST tömbből, létrehozzuk a DB objektumot (így csatlakozunk az adatbázishoz), majd átadjuk az argumentumokat a DB objektumunk insertData() metódusának. A beszúrás után újra az index.php-t töltjük be.

<?php
require_once("db.php");
if(isset($_POST["insertData"])){
  $name = $_POST['name'];
  $email = $_POST["email"];
  $db = new DB();
  $db->insertData($name,$email);
  header('Location: index.php');
}
?>

Írjuk meg most a DB osztályunk insertData() metódusát.

public function insertData($name,$email){
      $sql="INSERT INTO userdetails (name,email) VALUES (:name,:email)";
      $stmt=$this->conn->prepare($sql);
      $stmt->execute(['name'=>$name,'email'=>$email]);
    }

Az SQL utasítást paraméteres SQL utasításként hozzuk létre. (A paraméteres SQL utasításban a konkrét adatokat nem drótozzuk be az SQL parancsba, hanem azokat később adjuk át. A PDO-ban a paramétereket vagy a ? (kérdőjel) szimbólum, vagy nevesített jelölők (:name) használatával adjuk meg. Itt ezt az utóbbi módszert használjuk. A paraméteres SQL utasítást a prepare() metódussal készítjük elő, majd az eredményül kapott odbjektum (stmt) execute() metódusával egy lépésben összekötjük a megfelelő adatokkal és végre is hajtjuk az utasítást. (Az SQL utasítást a PDO a query() metódussal is végre tudja hajtani, de ez nem véd az SQL befecskendezés típusú támadásoktól.)

Adatok megjelenítése

A következőkben írjuk meg azt a kódot, amelyik megjeleníti a userdetails tábla tartalmát az űrlap alatt. Ehhez egészítsük ki a DB osztályt egy getData() metódussal, mely valamennyi adatot lekérdez, és visszaadja egy asszociatív tömb formájában:

    public function getData(){
      $sql="SELECT * FROM userdetails";
      $stmt=$this->conn->prepare($sql);
      $stmt->execute();
      $data=$stmt->fetchAll(PDO::FETCH_ASSOC);
      return $data;
    }

Magyarázatra egyedül a fetchAll() metódus szorul. Ezzel a metódussal tudjuk egy lekérdezés eredményét kinyerni különböző formában. A PDO::FETCH_ASSOC paraméter határozza meg, hogy az eredményt asszociatív tömb formájában szeretnénk megkapni, vagyis az egyes mezők értékei a mező nevével, mint kulccsal érhetők el. (Lehetőségünk lenne még indexelt tömbként (PDO::FETCH_NUM), objektumként (PDO::FETCH_OBJ), illetve ezek kombinációjaként is elérni.)

Az adatok lekérdezését az index.php fájlban közvetlenül az űrlap megjelenítése után tesszük meg, ahol egy foreach() ciklussal végigsöprünk az asszociatív tömbünkön, és kiírjuk a tartalmát:

  <?php
    $data = $db->getData();
    foreach($data as $i){
      echo $i['id'] . " - " . $i['name']." - ".$i['email']."<br/>";
    }
  ?>

Adatok törlése

Valósítsuk meg most az adatok törlését. Mivel valahogy azonosítanunk kell a törlendő rekordot, kicsit bővítenünk kell az index.php oldalt. Elkészítünk rajta egy újabb űrlapot, amelyben a törlendő rekord azonosítóját kérjük be, illetve az oldal egyes szakaszait fejlécekkel látjuk el.

<body>
  <h3>Insert</h3>
  <form action="insert.php" method="POST">
    <input type="text" placeholder="Name" name="name">
    <input type="text" placeholder="Email" name="email">
    <input type="submit" value="Insert" name="insertData">
  </form>

  <h3>Delete</h3>
  <form method="POST">
    <input type="text" placeholder="Id" name="id">
    <input type="submit" value="Delete" name="deleteData">
  </form>

  <h3>Data</h3>
  <?php
    $data = $db->getData();
    foreach($data as $i){
      echo $i['name']." - ".$i['email']."<br/>";
    }
  ?>
</body>

Mint láthatjuk, a törlendő adatot az azonosítójával (id) fogjuk kijelölni. Vegyük észre, hogy a törlést végző űrlap action mezőjét elhagytuk, ami annyit jelent, hogy a Delete gomb megnyomása után újra az index.php oldal fog betöltődni, vagyis ebben kell a törlést végző deleteData() metódust meghívni.

Egészítsük ki a DB osztályt ezzel a deleteData() metódussal, amelynek működése már nem szorul magyarázatra:

    public function deleteData($id){
      $sql="DELETE FROM userdetails WHERE id = :id";
      $stmt=$this->conn->prepare($sql);
      $stmt->execute(['id'=>$id]);
    }

Hogyan hívjuk meg ezt a metódust? Amikor az index.php fájl betöltődik, és létrehoztuk az elején a DB objektumot, vizsgáljuk meg, hogy a $_POST tömb tartalmazza-e a deleteData kulcsot. Ha igen, akkor a DELETE gomb megnyomásával jutottunk idáig, és meghívható a törlés. Az index.php elején lévő PHP szakasz ennek megfelelően így módosul:

<?php
    require_once("db.php");
    $db = new DB();

    if(isset($_POST['deleteData'])){
      $id=$_POST['id'];
      $db->deleteData($id);
    }
?>

Amennyiben olyan azonosítót írunk be, amelyik nem létezik, akkor nem kerül törlésre egyetlen rekord sem. Egészítsük ki a programot úgy, hogy az kiírja, hány sor került törlése a művelet során. (Persze ez itt értelemszerűen 0 vagy 1, de a bemutatott kód mindenhol használható.) A műveletben érintett sorok száma a rowCount() metódussal kérdezhető le.

    public function deleteData($id){
      $sql="DELETE FROM userdetails WHERE id = :id";
      $stmt=$this->conn->prepare($sql);
      $stmt->execute(['id'=>$id]);
      echo $stmt->rowCount() . " sort érintett a törlés.";
    }

Adatok szerkesztése

Írjuk meg most azt a kódot, amelyikkel a már bevitt rekordokat szerkeszthetjük. (Konkrétabban a name mező tartalmát.) A szerkesztendő rekordot, akár a törlésnél, itt is az azonosítóval (id) jelöljük ki. Az index.php fájlt kiegészítjük egy újabb űrlappal:

  <h3>Edit</h3>
  <form action="editdata.php" method="POST">
    <input type="text" placeholder="Id" name="id">
    <input type="text" placeholder="Name" name="name">
    <input type="submit" value="Edit" name="editData">
  </form>

Mint látjuk, az űrlap az editdata.php állományt hívja meg az Edit gomb megnyomásakor. Ebben a fájlban gyakorlatilag az adatbázishoz való csatlakozás mellett, csak kiemeljük a $_POST tömbből a bevitt adatokat, és átadjuk a DB objektum editData() metódusának:

<?php
require_once("db.php");
if(isset($_POST["editData"])){
  $id = $_POST['id'];
  $name = $_POST["name"];
  $db = new DB();
  $db->editData($id,$name);
  header('Location: index.php');
}
?>

Az editData() metódus a már megszokott módon, paraméteres SQL utasítás használatával végrehajtja a kívánt műveletet:

    public function editData($id,$name){
      $sql="UPDATE userdetails SET name = :name WHERE id = :id";
      $stmt=$this->conn->prepare($sql);
      $stmt->execute(['id' => $id, 'name' => $name]);
    }

Adatok keresés

Már csak egyetlen művelet hiányzik, az adatok keresése. A keresést név alapján valósítjuk meg, mégpedig úgy, hogy ne csak teljes névre, hanem a név egy részletére is rá tudjunk keresni. Először egészítsük ki egy új űrlappal az index.php állományt. A változatosság kedvéért itt most GET metódussal adjuk át a keresendő nevet, és ismét maga az index.php állomány fogja feldolgozni a bevitt adatot, vagyis az űrlap action mezőjét kihagyjuk.

  <h3>Search</h3>
  <form method="GET">
    <input type="text" placeholder="Name" name="name">
    <input type="submit" value="Search" name="searchData">
  </form>

A DB osztály searchData() metódusa ismét nem igényel magyarázatot. Mint látjuk a bevitt nevet a LIKE operátorral hasonlítjuk össze az adatbázis tartalmával úgy, hogy elé és mögé is beszúrjuk a % szimbólumot. A lekérdezett adatokat ismét a $data változóból adjuk vissza.

    public function searchData($name){
      $sql="SELECT * FROM userdetails WHERE name LIKE :name";
      $stmt=$this->conn->prepare($sql);
      $stmt->execute(['name' => "%" . $name . "%"]);
      $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
      return $data;
    }

A lekérdezett adatok megjelenítését ismét az index.php fájlra bízzuk. Ehhez egy kicsit át kell alakítani a már működő adatmegjelenítést is. Nézzük most meg ezt. (Az űrlapokat létrehozó sorok nem szerepelnek most a kódban.)

<?php
    require_once("db.php");
    $db = new DB();

    if(isset($_POST['deleteData'])){
      $id=$_POST['id'];
      $db->deleteData($id);
    }

    if(isset($_GET['searchData'])){
      $name=$_GET['name'];
      $data=$db->searchData($name);
    } else {
      $data = $db->getData();
    }

?>
<html>
<head> ... </head>
<body>
   <!--Itt vannak az űrlapok kódjai-->
  <h3>Data</h3>
  <?php
    foreach($data as $i){
      echo $i['id'] . " - " . $i['name']." - ".$i['email']."<br/>";
    }
  ?>

</body>
</head>
</html>

Jól látható, hogy mivel most GET metódust használunk, a $_GET tömbben kell keresnünk az átadott adatokat. Amennyiben ebben a tömbben megtaláljuk a serachData kulcsot, akkor megnyomták a keresés gombot. Kiemeljük a keresendő nevet, és átadva a getData() metódusnak, és az visszaadja a megtalált rekordokat tartalmazó tömböt (annak referenciáját). Ha nem a keresés gombot nyomták meg (a törlést már korábban lekezeltük, vagyis az nem lehet), akkor csak simán meghívjuk a getData() metódust, hogy megkapjuk a táblánk adatait. Emiatt viszont a korábbi kódunkból a megjelenítést végrehajtó foreach ciklus elől eltávolítjuk a getData() meghívását.

Ha érdekelt a bejegyzés, olvasd el ezt is: CRUD – PDO – 2. rész

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük