Bindowanie PDO we własnej klasie

0

Piszę sobie jakąś prostą klasę do obsługi zapytań. Ogólnie działa ale, zawsze znajdzie się jakieś ale. Ogólnie nie mam jeszcze obsłużonego bindowania danych. Zasada działania metody select() jest to, że jeśli parametr $type przyjmuje 1 to pobierz wiele rekordów, jeśli 2 to pobierz jeden konkretny określony przy pomocy zapytania. Coś mi się wydaje, że nieco przekombinowałem dlatego czekam na Wasze opinie i rady jak to napisać lepiej. Jednym słowem czekam na konstruktywną krytykę :) Wrzucam wszystko w "jednym ciągu" bez dzielenia na pliki, żeby wyraźniej było widać. Wie ktoś jak w kontekście tego kodu powinienem obsłużyć metodę bindValue() ?

<?php
	class Database {
		public $conn;
		/**
		 * @param $host, $dbname, $user, $pass
	  	 * @return string
	 	 */
		public function connectDatabase($host, $dbname, $user, $pass) {
			try {
				$conn = new PDO('mysql:host='.$host.'; dbname='.$dbname.'', ''.$user.'', ''.$pass.'');
				$conn->exec('SET CHARACTER SET utf8');
				$this->conn = $conn;
			} catch (Exception $error) {
				print_r ('Invalid connection with database: '.$error->getMessage().'');
			}
			return $conn;
		}
		/**
		 * If $ type = 1 to select multiple records otherwise $type = 2 and to select only one record
		 * @param $type, $query
	  	 * @return int, string
	 	 */
		public function select($type, $query) {
			if ($type === null) {
				return false;
			} else if ($type === 1) {
				$get = $this->conn->prepare($query);
				if (!$get->execute()) {
					print_r($get->errorInfo());
				}
			} else if ($type === 2) {
				$get = $this->conn->prepare($query);
				if (!$get->execute()) {
					print_r($get->errorInfo());
				} else {
				   	$row = $get->fetch();
				}
			} else {
				return false;
			}
			return $get;
		}
	}
	# Initialize function
	$data = array(
		'localhost' => define('HOST', 'localhost'),
		'dbname' => define('DBNAME', 'tests'),
		'user' => define('USER', 'root'),
		'pass' => define('PASS', '') 
	);
	$object = new Database;
	$connection = $object->connectDatabase(HOST, DBNAME, USER, PASS);

	# Display multiple records = $type = 1;
	$get = $object->select(1, "SELECT `id`, `text` FROM `example_table`");	
	foreach ($get as $row) {
		echo '<p>'.$row['text'].'</p>';
	}
	# Display of one record - $type = 2;
	$get = $object->select(2, "SELECT `id`, `text` FROM `example_table`");	
	foreach ($get as $row) {
		echo '<p>'.$row['text'].'</p>';
	}
?>
0

Hmmmm, w sumie to wynajdujesz koło na nowo. Teraz masz problem z bindowaniem, potem będziesz miał z joinami, potem z aliasami, orderem..... itp itd. Jeśli upierasz się nadal, to zobacz jak wygląda taki doctrine albo propel. Poza tym Twoja metoda select - jeśli już to powinna być rozbita na dwie np fetchOne i fetchAll, obie metody by przyjmowały odpowiednie zapytanie i nie musiałbyś ciągle wstawiać typu. No można tu długo móżdżyć nad tym, ale prędzej czy później zakopiesz się w tym żeby to zrobić idealnie.

0

Zazwyczaj pisze się metody, które będą obsługiwały tylko z góry określone zapytania, np.

getAllUsers - gdzie masz selecta na tabeli z userami bez warunków, a metoda nie przyjmuje parametrów
getUser - gdzie masz selecta na tabeli z userami z warunkiem, a metoda przyjmuje parametr np. id_user

bindValue w takim wypadku wyglądałoby jakoś tak

  $query = 'SELECT name FROM user WHERE id_user = :id_user';
 
  $get = $this->conn->prepare($query)
  $get -> bindValue(':id_userl', $id_user);
  $get -> execute();
0

Czy coś w ten deseń było by w porządku? Jak to teraz wygląda? Jakieś może jeszcze rady? Ogólnie czy jest to napisane "po Bożemu" czy lepiej jakoś inaczej?

<?php
	class Database {
		public $conn;
		/**
		 * @param $host, $dbname, $user, $pass
	  	 * @return string
	 	 */
		public function connectDatabase($host, $dbname, $user, $pass) {
			try {
				$conn = new PDO('mysql:host='.$host.'; dbname='.$dbname.'', ''.$user.'', ''.$pass.'');
				$conn->exec('SET CHARACTER SET utf8');
				$this->conn = $conn;
			} catch (Exception $error) {
				print_r ('Invalid connection with database: '.$error->getMessage().'');
			}
			return $conn;
		}
		/**
	  	 * @return false, PDO array()
	 	 */
		public function getText() {
			static $id = 3;
			static $query = 'SELECT `text` FROM `example_table` WHERE `id` = :id';
			$get = $this->conn->prepare($query);
			$get->bindValue(':id', $id);
			if(!$get->execute()){
				print_r($get->errorInfo());
				return false;
			}else{
				return $get->fetchAll(PDO::FETCH_ASSOC);
			}
		}
	}
	# Initialize function
	$data = array(
		'localhost' => define('HOST', 'localhost'),
		'dbname' => define('DBNAME', 'tests'),
		'user' => define('USER', 'root'),
		'pass' => define('PASS', '') 
	);
	$object = new Database;
	$connection = $object->connectDatabase(HOST, DBNAME, USER, PASS);
	$getData = $object->getText();

	if ($getData) {
		foreach($getData as $rowData) {
			echo $rowData['text'];
		}
	}else{
		echo '<strong>Error:</strong> Query error.';
	}
?>
0

Po co używasz tam zmiennych static? Z kodu wygląda na to jakbys nie wiedział do czego to służy. Teraz już nie potrzebujesz podziału na jeden/wiele wyników?

0
axelbest napisał(a):

Po co używasz tam zmiennych static? Z kodu wygląda na to jakbys nie wiedział do czego to służy. Teraz już nie potrzebujesz podziału na jeden/wiele wyników?

Nie no wiem o co chodzi. Wyżej kolega podał przykład bindowania, skleiłem to do metoody i po prostu się pytam czy to miał na myśli czy coś innego. A tamtą metodę "uciąłem", żeby się nie mieszała.

0

W kontekście bindowania i wyświetlania danych(jednego i wielu) chyba już sobie poradziłem. Mam inny problem Próbuję teraz to rozdzielić na 2 klasy i "wdrożyć" w to dziedziczenie. Pojawia mi się taki oto błąd:

Fatal error: Uncaught Error: Call to a member function prepare() on null in /opt/lampp/htdocs/tests/connect4.php:24 Stack trace: #0 /opt/lampp/htdocs/tests/connect4.php(45): Home->getText() #1 {main} thrown in /opt/lampp/htdocs/tests/connect4.php on line 24

Kod wygląda tak:

<?php
	# Function to connect database
	class Database {
		public $conn;
		/**
		 * @param $host, $dbname, $user, $pass
	  	 * @return string
	 	 */
		public function connectDatabase($host, $dbname, $user, $pass) {
			try {
				$conn = new PDO('mysql:host='.$host.'; dbname='.$dbname.'', ''.$user.'', ''.$pass.'');
				$conn->exec('SET CHARACTER SET utf8');
				$this->conn = $conn;
			} catch (Exception $error) {
				print_r ('Invalid connection with database: '.$error->getMessage().'');
			}
			return $conn;
		}
	}
	class Home extends Database {
		public function getText() {
			static $id = 3;
			static $query = 'SELECT `text` FROM `example_table` WHERE `id` = :id';
			$get = $this->conn->prepare($query);
			$get->bindValue(':id', $id);
			if(!$get->execute()){
				print_r($get->errorInfo());
				return false;
			}else{
				return $get->fetchAll(PDO::FETCH_ASSOC);
			}
		}
	}
	# Initialize function
	$data = array(
		'localhost' => define('HOST', 'localhost'),
		'dbname' => define('DBNAME', 'tests'),
		'user' => define('USER', 'root'),
		'pass' => define('PASS', '') 
	);
	$object = new Database;
	$connection = $object->connectDatabase(HOST, DBNAME, USER, PASS);

	$object2 = new Home;
	$getData = $object2->getText();

	var_dump($getData);
	if ($getData) {
		foreach($getData as $rowData) {
			echo $rowData['text'];
		}
	}else{
		echo '<strong>Error:</strong> Query error.';
	}
?>

(Zawsze odkładałem OOP na później, dotychczas pisałem "Sphagetti code", więc moje pytanie może się wydawać dla niektórych nieco trywialne.)
Jak to poprawić aby poprawnie działało?

1

Oj źle..... W klasie Database przypisujesz do $conn połączenie za pomocą metody connectDatabase. Później tworzysz obiekt $object i tam wywołujesz tą metodę, ok, w tej klasie masz połączenie i jest ok. Natomiast w obiekcie klasy Home($object2), mimo że dziedziczysz - to nie masz żadnego związku z wcześniej utworzonym obiektem $object. Aby Ci działało, musisz na obiekcie $object2 - wykonać metodę connectDatabase.

Ogólnie powiem tak - musisz albo przejść dobry tutorial, albo kupić książkę, albo przeglądać kod innych programistów i analizować jak co jest zrobione i dlaczego działa, tak a nie inaczej. Twój pomysł z podziałem na dwie klasy może i miałby jakiś sens gdybyś to co masz w metodzie connectDatabase umieścił w konstruktorze tejże klasy.

Poza tym widzę że brakuje Ci podstaw - bo powiedz po kiego czorta tworzysz tablicę, w środku której definiujesz stałe, a później w kodzie wcale się nie odnosisz do tej tablicy? Poza tym nadal w tej metodzie masz static - zobacz do czego to służy, pomyśl i zastanów się czy to jest tam aż tak potrzebne.

No ja już tu więcej raczej nie pomogę, a Tobie polecam więcej nauki, a mniej bezmyślnego przeklejania kodu lub wpisywania poleceń na chybił trafił.

0

Ogólnie powiem tak - musisz albo przejść dobry tutorial, albo kupić książkę, albo przeglądać kod innych programistów i analizować jak co jest zrobione i dlaczego działa, tak a nie inaczej.

No to prawda. Muszę coś poczytać, bo aż wstyd pytać o niektóre rzeczy.

Twój pomysł z podziałem na dwie klasy może i miałby jakiś sens gdybyś to co masz w metodzie connectDatabase umieścił w konstruktorze tejże klasy.

Sprawdziłem w rzeczywistości to co napisałeś. Mimo zastosowania konstruktora nadal muszę w każdej nowo inicjalizowanej klasie i tak ustawiać argumenty dla niej(nie wiem czy się dobrze wyraziłem

$object = new Database(HOST, DBNAME, USER, PASS);
$object2 = new Home(HOST, DBNAME, USER, PASS);

No chyba, że ja coś źle rozumiem. Ogólnie jakbyś mógł się jeszcze do tego odnieść to będę wdzieczny.

0

A skąd ta "nowo" zainicjalizowana klasa, miałaby wiedzieć o czymkolwiek z innej klasy? Zadaj też sobie zasadnicze pytanie - po co Ci dwie klasy w których łączysz się z bazą? (podpowiedź, wystarczy Ci jedno połączenie)
Jeśli już chcesz to jakoś bardziej 'zobiektowić' :) - to zrób klase A która rozszerza klasę PDO, następnie tą klasę przekazuj poprzez konstruktor do klasy B oraz C - jeśli C będzie dziedziczyła po B, to wystarczy ze konstruktor dasz tylko w B.

1 użytkowników online, w tym zalogowanych: 0, gości: 1