Sixtens hemsida Uppgifter Blogg Om

Bankapplikation med databas

Gå till sida

Källkod

bank_sql/

logout.php

transfer.php

index.php

login.php

manage.php

signup.php

change_password.php

PREP/

POPULATE.php

QUERIES.sql

modules/

user.php

page.php

database.php

session.php

utility.php

wesweb01/bank_sql/modules/user.php

1 lines
<?php
# Fil som skapar klass för användare och användardata.

require "utility.php";
require 
"database.php";

# $db är definierad i database.php

class User extends DatabaseTable
    
# En klass för en användare. Innehåller funktioner för transaktioner och datalagring.
{
    protected static 
$dbPrimaryKey "userId"# PK för user-tabellen (läs mer i database.php)
    
protected static $dbTable "user"# Namn på databastabellen
    
public static function GetUserFromName($username): ?User
    
# Statisk funktion som hämtar ett användarobjekt utifrån användarnamn.
    
{
        global 
$db;
        
$userData $db->query("SELECT userId FROM user WHERE username=:username", array("username" => $username));
        if (!
$userData# Kontrollerar att användaren finns.
            
return null;
        return new 
User($userData["userId"]);
    }

    public static function 
RegisterUser($username$password): ?int
    
# Statisk funktion som skapar ett konto från användarnamn och lösenord.
    
{
        global 
$db$masterBankAccount;
        
$passwordSalt generate_salt(); # Skapar lösenordssalt.
        
$passwordHash hash_password($password$passwordSalt); # Hashar lösenordet
        
try {
            
# Försöker lägga till användaren
            
$db->query(
                
"INSERT INTO user (username, passwordHash, passwordSalt) VALUES (:username, :passwordHash, :passwordSalt)",
                array(
"username" => $username"passwordHash" => $passwordHash"passwordSalt" => $passwordSalt)
            );
        } catch (
PDOException $ex) {
            
# Eftersom det finns en UNIQUE-constraint på username kommer databasen ge en error ifall användarnamnet redan finns.
            
return null;
        }

        
$user = new User($db->getLastId()); # Skapar användarobjekt
        
$account $user->createAccount("Bankkonto"); # Lägger till ett bankkonto
        # Ger detta konto en välkomstbonus på 1000 kr.
        
Transaction::AddTransaction($masterBankAccount$account1000"signup_bonus");

        return 
$user->id;
    }

    public static function 
TryLoginUser($username$password): ?User
    
# Statisk funktion som försöker logga in användare med användarnamn och lösenord.
    
{
        global 
$db;
        
$userData $db->query("SELECT userId, passwordHash, passwordSalt FROM user WHERE username=:username", array("username" => $username));
        if (!
$userData# Kontrollerar att användaren finns.
            
return null;

        
$passwordHash hash_password($password$userData["passwordSalt"]); # Hashar lösenordet och kollar att de stämmer överens.
        
if ($userData["passwordHash"] != $passwordHash)
            return 
null;

        return new 
User($userData["userId"]); # Skapar ett nytt användarobjekt ifall användarnamn och lösenord stämde.
    
}

    public function 
createAccount($name): Account
    
# Funktion som skapar ett nytt (bank)konto. Koden förklarar sig själv.
    
{
        global 
$db;
        
$db->query(
            
"INSERT INTO userAccount(userId, `name`) VALUES (:userId, :accountName)",
            array(
                
"userId" => $this->id,
                
"accountName" => $name ?? "Nytt konto"
            
)
        );
        return new 
Account($db->getLastId());
    }

    public function 
getMainAccount(): ?Account
    
# Hämtar huvudkontot. Huvudkontot är ett konto som insättningar, uttag och mottagning av externa transaktioner sker på.
    # Det kan inte tas bort. 
    
{
        foreach (
$this->accounts as $account) {
            if (
$account->isMainAccount)
                return 
$account;
        }
        return 
null# Bör aldrig inträffa
    
}

    public function 
getAccountFromId($accountId): ?Account
    
# Hämtar ett kontoobjekt utifrån konto-id.
    
{
        foreach (
$this->accounts as $account) {
            if (
$account->accountId == $accountId)
                return 
$account;
        }
        return 
null;
    }

    public function 
logDeposit($amount): void
    
# Sparar ett uttag från huvudkontot.
    
{
        global 
$masterBankAccount;
        
Transaction::AddTransaction($masterBankAccount$this->getMainAccount(), $amount"deposit");
    }

    public function 
logWithdrawal($amount): void
    
# Sparar en insättning till huvudkontot.
    
{
        global 
$masterBankAccount;
        
Transaction::AddTransaction($this->getMainAccount(), $masterBankAccount$amount"withdrawal");
    }

    public function 
logInternalTransaction($senderAccount$recipientAccount$amount): void
    
# Sparar en intern transkation (mellan två konton hos samma användare).
    
{
        
Transaction::AddTransaction($senderAccount$recipientAccount$amount"transaction");
    }

    public function 
logExternalTransaction($senderAccount$recipientUser$amount): void
    
# Sparar en extern transkation (från en användare till en annan).
    
{
        
$recipientAccount $recipientUser->getMainAccount(); # Pengarna skickas till mottagarens huvudkonto.
        
Transaction::AddTransaction($senderAccount$recipientAccount$amount"transaction");
    }

    public function 
deleteAccount($account): void
    
# Funktion som tar bort ett bankkonto.
    
{
        
# Resterande pengar skickas till huvudkontot.
        
Transaction::AddTransaction($account$this->getMainAccount(), $account->balance"deletec_account");
        
$account->isActive 0# Kontot tas inte faktiskt bort, utan inaktiveras bara.
        
$account->updateValues(array("isActive"));
    }
}

# Skapar två relationer, en för User->accounts och en för Account->transactions.
# Detta gör det möjligt att enkelt komma åt relationerna, exempelvis en användares konton. 
DatabaseTable::AddRelation(User::class, Account::class, "userId""accounts");
DatabaseTable::AddRelation(Account::class, Transaction::class, "accountId""transactions""`date` DESC");
class 
Account extends DatabaseTable
    
# Klass för en användares bankkonto
{
    protected static 
$dbPrimaryKey "accountId"# PK för user-tabellen.
    
protected static $dbTable "userAccount"# Namn på databastabellen.
    
protected static $dbReadTable "userAccountBalanceView"# Namn på databastabellen/vyn som den enbart läser ifrån.

    
public static function GetAccountFromId($accountId): ?Account
    
# Hämtar ett kontoobjekt utifrån konto-id.
    
{
        global 
$db;
        
$pk self::$dbPrimaryKey# Namnet på PK-property
        
$table self::$dbReadTable ?? self::$dbTable# Tabellen som den ska läsa ifrån
        # Kör SQL-satsen och hämtar kontot som har PK = $accountId.
        
$instanceData $db->query(
            
"SELECT * FROM {$table} WHERE {$pk}=:id",
            array(
                
"id" => $accountId
            
)
        );
        if (!
$instanceData)
            return 
null# Ifall inget konto hittades

        
$instance = new Account($accountId); # En ny kontoinstans skapas.
        
foreach ($instanceData as $index => $value) { # Eftersom vi redan har hämtat data fyller vi den direkt,
            # och så slipper vi köra ett till databasanrop senare.
            
$instance->{$index} = $value;
        }
        return 
$instance;
    }
}

$masterBankAccount = new Account(1); # Bankkonto som ägs av banken.
# Finns för att ge välkomstbonus, samt insättningar och uttag.

class Transaction extends DatabaseTable
    
# Klass för transaktioner.
{
    protected static 
$dbPrimaryKey "transactionId"# PK för user-tabellen.
    
protected static $dbTable "transaction"# Namn på databastabellen.
    
protected static $dbReadTable "userAccountTransactionView"# Namn på databastabellen/vyn som den enbart läser ifrån.

    
public static function AddTransaction($senderAccount$recipientAccount$amount$type)
    {
        
# Statisk funktion som lägger till en transaktion mellan två bankkonton.
        # Använder lite SQL-magi som jag lärde mig, där man kan använda outputen av en SELECT-sats istället för
        # att använda VALUES-satsen. Detta gör det möjligt att hämta ett transactionTypeId utifrån name-parametern
        # i ett svep.
        
global $db;
        
$db->query("INSERT INTO `transaction`(senderAccountId, recipientAccountId, transactionTypeId, amount, `date`)
        SELECT :senderAccountId, :recipientAccountId, tt.transactionTypeId, :amount, NOW()
        FROM transactionType tt WHERE tt.name=:transactionType"
,
            array(
                
"senderAccountId" => $senderAccount->id,
                
"recipientAccountId" => $recipientAccount->id,
                
"amount" => $amount,
                
"transactionType" => $type ?? "transaction"
            
)
        );
    }
}
?>