bank_sql/
wesweb01/bank_sql/modules/database.php1 lines
<?php
# Fil som sköter anslutning till databas, samt metoder för att komma åt datan på ett enklare sätt.
class DatabaseConnection
{
# Din kod för att ansluta till en databas. Rätt så självklar.
public static $host = "server1.serverdrift.com";
public static $user = "Intentionally removed by CSource";
public static $pwd = "Intentionally removed by CSource";
public static $db = "als070804sn_bank";
public static $options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'",
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES,
false
);
private $pdo;
public function __construct()
{
# Här etablerar vi anslutningen när vi skapar en ny instans av DatabaseConnection.
$host = self::$host;
$db = self::$db;
$dsn = "mysql:host=$host;dbname=$db";
try {
$this->pdo = new PDO($dsn, self::$user, self::$pwd, self::$options);
} catch (Exception $e) {
die("<p>Could not connect to the database:<br/>{$e}</p>");
}
}
private function execute($sql, $params = null): PDOStatement
# Privat funktion som används av query och queryAll. Exekverar en SQL-sats.
{
//echo "<p>{$sql}</p>";
$stm = $this->pdo->prepare($sql);
if (isset($params))
$stm->execute($params);
else
$stm->execute();
return $stm;
}
public function query($sql, $params = null): mixed
# Gör en query och kör fetch.
{
return $this->execute($sql, $params)->fetch(PDO::FETCH_ASSOC);
}
public function queryAll($sql, $params = null): array
# Gör en query och kör fetchAll.
{
return $this->execute($sql, $params)->fetchAll(PDO::FETCH_ASSOC);
}
public function getLastId(): bool|string
# Ger den sista PK-id som skapades på databasen.
{
return $this->pdo->lastInsertId();
}
}
$db = new DatabaseConnection(); # Etablerar en anslutning till databasen.
# Detta ser även till att alla filer som kör require på denna fil skapar en databasanslutning.
abstract class DatabaseTable
# En klass jag har skapat för att göra kopplingen mellan databasen och PHP-koden enklare.
# Klassen i sig är abstract och extendas av andra klasser som motsvarar tabeller i databasen.
# Detta leder till att en ny instans av klassen som ärver DatabaseTable automatiskt ansluts till databasen.
# Läs mer om den på WordPress.
{
protected static $dbPrimaryKey; # PK för tabellen.
protected static $dbTable; # Tabellens namn på databasen.
protected static $dbReadTable; # Nullable namn på tabell(vyn) som den läser ifrån.
# Används ifall den ska läsa från en vy medans den skriver på själva tabellen.
# Notera att dessa tre är static, men de överskrids av klassen som ärver DatabaseTable.
private static $relations = array(); # En static array som kopplar ihop relationerna mellan flera tabeller.
public $id; # ID(PK)-värdet för detta objektet som motsvarar databaskolumnen.
private $dbData = null; # Variabel som senare blir en array, vilket innehåller all data som databasen har att ge.
public static function AddRelation($table1, $table2, $foreignKey, $propertyName, $order = null): void
# Skapar en property i $table1 vilket låter en instans av den klassen komma åt alla instanser av $table2 som har en foreign key
# med värdet av instansen i $table1.
# Exempelvis om variabel $user1 är av klass User och User har en 1:N relation med Account, ska man kunna köra
# $user1->accounts för att få en lista på alla Account-instanser.
{
array_push(self::$relations, array(
"table1" => $table1,
"table2" => $table2,
"foreignKey" => $foreignKey,
"propertyName" => $propertyName,
"order" => $order
));
}
public function __construct($id)
# När en ny instans skapas sätts ID-värdet.
{
$this->id = $id;
}
public function __get($name): mixed
# Här kommer den krångligaste funktionen i klassen. Den ser till att när man indexerar properties i ärvda instanser av DatabaseTable
# laddas datan från databasen ifall den inte redan är laddad. Detta gör det mycket smidigt att läsa data från databasen och använda
# funktioner som kommer med klasserna.
{
# I __get är $name i det här fallet den indexen man försöker indexera objektet med.
# Om $user = new User() där class User extends DatabaseTable skulle jag exempelvis
# kunna köra $user->username och då kommer $name="username"
# Det som funktionen returnerar blir värdet på $user->username.
global $db;
$readTable = $this::$dbReadTable ?? $this::$dbTable; # Läser av dbReadTable om den finns, annars dbTable.
$sqlQuery = "{$readTable} WHERE {$this::$dbPrimaryKey}=:id"; # Del av flera SQL-satser.
if ($this->dbData === null) # Ifall dbData är null, alltså om ingen data redan är laddad körs en SELECT *, vilket laddar all data.
$this->dbData = $db->query("SELECT * FROM {$sqlQuery}", array("id" => $this->id));
# Nu kan vi kolla ifall propertyn vi indexerar finns i dbData.
if (isset($this->dbData[$name]))
return $this->dbData[$name];
# Ifall den inte gör det går vi igenom alla relationer och ser ifall vi har definierat en relation som en property.
foreach (self::$relations as $relation) {
# Kollar ifall relationen finns.
if ($relation["table1"] != $this::class || $relation["propertyName"] != $name)
continue;
# Om en relation finns, exempelvis $user->accounts, kommer en lista på alla kolumner av account som har matchande userId
# läggas till i listan som ett objekt av Account.
$instances = array(); # En ny lista för alla instanser.
$table2 = $relation["table2"]; # Andra tabellen i relationen, typ: klass.
$order = isset($relation["order"]) ? "ORDER BY {$relation['order']}" : ""; # Ifall det finns en ORDER BY.
# Här kör vi själva satsen. Vi hämtar alla kolumner i andra tabellen där FK matchar PK i vårt objekt.
$dbInstances = $db->queryAll(
"SELECT * FROM {$table2::$dbReadTable}
WHERE {$relation['foreignKey']}=:id {$order}",
array("id" => $this->id)
);
# Resultatet itereras igenom och skapar objekt av den klassen. Exempelvis när $user->accounts indexeras skapas
# en lista av Account.
foreach ($dbInstances as $instanceData) {
$instanceId = $instanceData[$table2::$dbPrimaryKey]; # PK för instansen
$instance = new $relation["table2"]($instanceId); # Nytt objekt av instansens klass.
foreach ($instanceData as $index => $value) { # Alla properties loopas igenom och läggs i vår instans.
$instance->{$index} = $value;
}
array_push($instances, $instance); # Instansen läggs till i listan av instanser.
}
$this->{$name} = $instances; # Till sist sparas alla instanser i vårt objekt med index $name, exempelvis "accounts".
return $instances;
}
# Här behövde jag lägga in värdet rakt i SQL-satsen, utan att lägga till det i PDO-prepare parametrarna.
# Det bör inte vara ett problem, eftersom $name är alltid förprorgammerad, och beror aldrig på användarinput.
# Ifall inget värde hittats än är det möjligt att det inte är laddat. Därför försöker kolumnen laddas in.
$value = $db->query("SELECT `{$name}` FROM {$sqlQuery}", array("id" => $this->id));
if ($value) {
$this->dbData[$name] = $value[$name];
return $value[$name];
}
# Ifall ingenting hittas får man en varning(om man inte redan fått en error av databasförfrågan kort innan).
trigger_error("Undefined property: $name", E_USER_WARNING);
return null;
}
public function __set($name, $value): void
# Den här funktionen körs när man försöker ändra ett värde som inte finns i objektet.
# Det gör det möjligt att i vissa fall skapa ett tomt objekt och fylla den med data ifall man redan har
# gjort en databasförfrågan.
{
# Om $this->dbData === null innebär det att raden i databasen kommer laddas in när man indexerar en property i objektet.
# Ifall man ändrar ett värde(exempelvis en funktion som hämtar användare utifrån användarnamn, som redan har hämtat nödvändig data från databasen)
# kommer den inte ladda in datan igen nästa gång man indexerar någit i objektet.
if (!isset($this->dbData))
$this->dbData = array(); # Skapar en array ifall det inte redan finns.
$this->dbData[$name] = $value; # Sätter värdet i dbData till det nya värdet.
}
public function updateValues($values): void
# Funktion som uppdaterar alla givna värden på databasen till motsvarande värden i objektet.
{
global $db;
$newValues = array(); # Lista på alla databaskolumner som ska uppdateras.
$params = array("id" => $this->id); # Lista på parametrar som ska användas i SQL-satsen.
# Vi går igenom varje värde som ska uppdateras.
foreach ($values as $value) {
# För varje värde lägger vi till i newValues, exempelvis "`username` = :parameter_username".
array_push($newValues, "`{$value}` = :parameter_{$value}");
# Värdet i objektet på den kolumn som ska uppdateras läggs till som parameter.
$params["parameter_{$value}"] = $this->{$value};
}
$stringValues = implode(",", $newValues); # Alla newValues omvandlas till en sträng med komma mellan.
# SQL-satsen körs så att uppdateringen sker.
$db->query("UPDATE {$this::$dbTable} SET {$stringValues} WHERE {$this::$dbPrimaryKey}=:id", $params);
}
public function rereadValues(): void
# I vissa fall har en rad på databasen ändrats efter att alla värden har laddats in.
# Då kör man den här funktionen för att tvinga att datan laddas om nästa gång den indexeras.
{
$this->dbData = null;
}
}
?>