[PHP] [MySQLi] INSERT INTO wird doppelt ausgefuehrt.

philishake

javascript enthusiast
Hey Community,

ich versuche mich gerade an MySQLi Transaktionen mit PHP. Hier mal erst mal mein Setup:

PHP 5.6.12
MySQL 5.6.19

Alle meine Tabellen sind mit InnoDB Engine.

Ich habe einen endpoint /api/account/get.php. Dieser endpoint schickt ein Account objekt zurueck. Sollte kein Account existieren, dann wird automatisch ein neuer erstellt. Mein Databasemanager mapped eigentlich nur die Funktionen, die es im mysqli Objekt gibt. Der unten aufgefuehrte Code ist fuer einen Prototyp, kein echter Produktionscode, daher ist er vielleicht nicht ganz optimal. Die ganzen printf sind zum testen, dadurch weiss ich auch, dass der Code nicht doppelt ausgefuehrt wird.

NUR der INSERT in die accounts Tabelle wird doppelt ausgefuehrt, alle anderen nur einmal, so wie es auch sein sollte.

Hier mal ein bisschen Code:

/api/account/get.php
PHP:
<?php
require_once '../../php/bootstrap.php';
require_once '../../php/config/database.php';
require_once '../../php/DatabaseManager.php';
require_once '../../php/Account.php';

printf("Receive request data<br />");
$username = $_REQUEST['username'];
$externalId = $_REQUEST['externalId'];

printf("Setup database<br />");
$dbms = new DatabaseManager();
$dbms->setHostname($config->database->hostname);
$dbms->setUsername($config->database->username);
$dbms->setPassword($config->database->password);
$dbms->setTable($config->database->table);

printf("Account handler<br />");
$account = new Account($externalId, $username);
$account->setDBMS($dbms);
$account->fetchAccountId();

printf("create account if not already there<br />");
$message = "";

if ($account->hasAccount()) {
    $message = "Account Found";
    printf("Account found<br />");
} else {
    $message = "No Account Found. New one was created";
    printf("Account not found<br />");
    $account->createAccount();
}

printf("create resopnse<br />");
$response = new stdClass();
$response->username = $account->getUsername();
$response->userId = $account->getAccountId();
$response->externalId = $account->getExternalId();
$response->message = $message;

die(json_encode($response));
?>

Account class
PHP:
<?php
require_once '../../php/bootstrap.php';
require_once '../../php/DatabaseManager.php';
require_once '../../php/config/game.php';

class Account {
    private $id = null;
    private $externalId;
    private $username;
    private $dbms;
   
    function __construct($externalId, $username) {
        printf("Account->__construct<br/>");
        $this->externalId = $externalId;
        $this->username = $username;
    }
   
    public function setDBMS($dbManager) {
        printf("Account->setDBMS<br/>");
        $this->dbms = $dbManager;
    }
   
    public function hasAccount() {
        printf("Account->hasAccount<br/>");
        return $this->id != null;
    }
   
    public function fetchAccountId() {
        printf("Account->fetchAccountId<br/>");
        $this->dbms->connect();
       
        $accountRows = $this->dbms->query(
            "SELECT id FROM accounts WHERE externalId='" .
            $this->externalId . "'"
        );
       
        if ($accountRows->num_rows == 0) {
            printf("Account->fetchAccountId: not found<br/>");
        }
        else if ($accountRows->num_rows == 1) {
            printf("Account->fetchAccountId: found<br/>");
            $account = $accountRows->fetch_object();
            $this->id = $account->id;
        }
        else {
            //TODO: Handle error somehow    
            printf("Account->fetchAccountId: weird stuff just happend<br/>");
        }
       
        $this->dbms->disconnect();
    }
   
    public function createAccount() {
        printf("Account->createAccount<br/>");
        $this->dbms->connect();
        $this->dbms->autocommit(FALSE);
        $this->dbms->beginTransaction(
            DatabaseManager::$TRANSACTION_READ_WRITE,
            "CREATE_NEW_ACCOUNT_" . time()
        );
       
        // Add to base tables
        $this->addToAccountTable();
        $this->addToCountryTable();
        $this->addToResearchTable();
        $this->addToResourcesTable();
        $this->addToSettingsTable();
        $this->addToStatsTable();
        $this->addToUnitsTable();
       
        // Complete transactions
        $this->dbms->commit();
        $this->dbms->disconnect();
    }
   
    private function addToAccountTable() {
        printf("Account->addToAccountTable<br/>");
        $currentTime = time();
        $protection = $currentTime + $gameConfig->newbieProtection;
       
        $this->dbms->query(
            "INSERT INTO accounts (externalId, username, login, protection, registered) VALUES (
                '{$this->externalId}', '{$this->username}', '{$currentTime}', '{$protection}', '{$currentTime}'
            )"
        );
       
        // Receive account id after creating
        $accountRow = $this->dbms->query("SELECT id FROM accounts WHERE externalId='{$this->externalId}'");
        $account = $accountRow->fetch_object();
       
        // Update accountId
        $this->id = $account->id;
    }
   
    private function addToCountryTable() {
        //TODO: TBD    
    }
   
    private function addToResearchTable() {
        $this->dbms->query(
            "INSERT INTO research (userId, ironMining, silverMining, coining, espionage) VALUES (
                '{$this->id}', 0, 0, 0, 0
            )"
        );
    }
   
    private function addToResourcesTable() {
        $this->dbms->query(
            "INSERT INTO resources (userId, iron, silver, gold) VALUES (
                '{$this->id}',
                '{$gameConfig->startingResources->iron}',
                '{$gameConfig->startingResources->silver}',
                '{$gameConfig->startingResources->gold}'
            )"
        );    
    }
   
    private function addToSettingsTable() {
        $this->dbms->query(
            "INSERT INTO settings (userId) VALUES ('{$this->id}')"
        );
    }
   
    private function addToStatsTable() {
        $this->dbms->query(
            "INSERT INTO stats (userId, points, general) VALUES (
                '{$this->id}', 0, 0
            )"
        );
    }
   
    private function addToUnitsTable() {
        $this->dbms->query(
            "INSERT INTO units (userId, workers, soldiers, rocketeers, tanks, tigertanks, engineers) VALUES (
                '{$this->id}', 0, 0, 0, 0, 0, 0
            )"
        );
    }
   
    public function getAccountId() {
        return $this->id;
    }
   
    public function getExternalId() {
        return $this->externalId;
    }
   
    public function getUsername() {
        return $this->username;
    }
}
?>
 
Erstelle mal ein unique key auf das Feld externalId. Dann geht das schon gar nicht.

Im Moment ist mehre einträge mit derselben externalId möglich. Wenn die externalId bereits mehr als einmal vorkommt, dann wird für diese externalId immer ein neuer Eintrag erstellt.
PHP:
        if ($accountRows->num_rows == 0) {
            printf("Account->fetchAccountId: not found<br/>");
        }
        else if ($accountRows->num_rows == 1) {
            printf("Account->fetchAccountId: found<br/>");
            $account = $accountRows->fetch_object();
            $this->id = $account->id;
        }
        else {
            //TODO: Handle error somehow    
            printf("Account->fetchAccountId: weird stuff just happend<br/>");
        }
Wie du siehst, fällt ab 2 Einträgen alles unter das letzte else. Es wird keine id übernommen und hasAccount() gibt dann an, dass der Eintrag nicht exisitiert.

Also, bereinige die Daten. Dann setz den unique key.
 
Hey,

danke fuer deine Antwort. Habe aus dem externalId field ein UNIQUE field gemacht. Zusaetzlich schmeiss ich eine Exception, wenn es vorkommt, dass ich mehrere Eintraege finde. Jetzt schein alles zu klappen. Besten Dank!
 

Neue Beiträge

Zurück