• User

    Un ciò voglia di dormì stanotte: Mo beccateve sto ORM va

    Asd ... cosa vuol dire ORM ?

    ObjectRelationMapping ovvero Mappatura delle Relazioni tra gli Oggetti.

    Senza stare troppo a sottilizzare sul fatto che ogni database ha un linguaggio a se stante (bella rottura di beep beep .. autocensura) possiamo mappare gli oggetti che arrivano da una form con i campi del database in modo da non smaronarci più di tanto dato che le operazioni CRUD sono sempre 4 e di solito 1 non si attua in questa forma. CRUD = Create, Read, Update, Delete e solitamente quando si lavora su un database succede che:

    • Quando vengono inseriti dei dati si ha un C=Create
    • Quando si aggiornano dei dati si ha un U=Update
    • Quando si cancellano dei dati si ha un D=Delete che solitamente avviene a metodo esterno (nomeOggetto->delete(IDDaDeletareComprensivoDiTuttoQuelloCheLoRiguarda))

    Mentre l'R (Read) solitamente avviene mediante query che poi vengono più o meno artisticamente impaginate. Ergo una buona classe ORM deve contenere solamente 2 metodi + 1 che marca i dati per la cancellazione.

    La classe in questione (la terza C o i ragazzi del muretto si vi aggrada) si chiama DataBoudObject e non è niente di eccezionale. Semplicemente si appoggia su una sua figlia (class AggiungiRecord extends DataBoundObject) per ciò che concerne le relazioni tra i campi della form e quelli del database oltre a costruirsi le query da sola.

    Ok mo vediamo come va:

    [php]
    <?php

    abstract class DataBoundObject
    {

    protected $ID;
    protected $objPDO;
    protected $strTableName;
    protected $arRelationMap;
    protected $blForDeletion;
    protected $blIsLoaded;
    protected $arModifiedRelation;
    
    abstract protected function DefinedTableName();
    abstract protected function DefineRelationMap();
    
    public function __construct(PDO $objPDO, $id = null)
    {
    
        $this->strTableName     = $this->DefineTableName();
        $this->arRelationMap     = $this->DefineRelationMap();
        $this->objPDO             = $objPDO;
        $this->blIsLoaded        = false;
        if (isset($id))
        {
            $this->ID = $id;
        }
        $this->arModifiedRelations = array;
        
    }
    
    public function Load()
    {
    
        if (isset($this->ID))
        {
            $strQuery = "SELECT ";
            foreach ($this->arRelationMap as $key => $value)
            {
                $strQuery .= "\"" . $key . "\",";
            }
            $strQuery = substr($strQuery, 0, strlen($strQuery) -1 );
            $strQuery .= " FROM " . $this->strTableName . " WHERE \"id\" = :eid";
            $objStatement = $this->objPDO->prepare($strQuery);
            $objStatement->bindParam(':eid', $this->ID, PDO::PARAM_INT);
            $objStatement->execute();
            $arRow = $objStatement->fetch(PDO::FETCH_ASSOC);
            foreach ($arRow as $key => $value)
            {
                $strMember = $this->arRelationMap[$key];
                if (property_exists($this, $strMember))
                {
                    if (is_numeric($value))
                    {
                        eval('$this->' . $strMember . ' = ' . $value . ';');
                    } else {
                        eval('$this->' . $strMember . ' = "' . $value . '";');
                    }
                }
            }
        $this->blIsLoaded = true;
    }
    
    public function Save()
    {
        if (isset($this->ID))
        {
            $strQuery = 'UPDATE "' . $this->strTableName . '" SET ';
                foreach ($this->arRelationMap as $key => $value)
                {
                    eval ('$actualVal = &$this->' . $value . ';');
                    if (array_key_exists($value, $this->arModifiedRelations))
                    {
                        $strQuery .= '"' . $key . "\" = :$value, ";
                    }
                }
            $strQuery = substr($strQuery, 0, strlen($strQuery) -2);
            $strQuery .= ' WHERE "id" = :eid';
            unset($objPDOStatement);
            $objStatement = $this->ObjPDO->prepare($strQuery);
            $objStatement->bindValue(':eid', $this->ID, PDO::PARAM_INT);
                foreach ($this->arRelationMap as $key => $value)
                {
                    eval('$actualVal = &$this->' . $value . ';');
                    if (array_key_exists($value, $this->arModifiedRelations))
                    {
                        if ((is_int($actualVal) || ($actualVal == null))
                        {
                            $objStatement->bindValue(':' . $value, $actualVal, PDO::PARAM_STR);
                        }
                    }
                }
            $objStatement->execute();
        } else {
            $strValueList = "";
            $strQuery = 'INSERT INTO "' . $this->strTableName . '"(';
            foreach ($this->arRelationMap as $key => $value)
            {
                eval('$actualVal = &$this->' . $value . ';');
                if (isset($actualVal))
                {
                    if (array_key_exists($value, $this->arModifiedRelation))
                    {
                        $strQuery .= '"' . $key . '", ';
                        $strValueList .= ":$valuem ";
                    }
                }
            }
            $strQuery = substr($strQuery, 0, strlen($strQuery) -2);
            $strValueList = substr($strValueList, 0, strlen($strVaueList) -2);
            $strQuery .= ") VALUES (";
            $strQuery .= $strValueList;
            $strQuery .= ")";
            unset ($objStatement);
            $objStatement = $this->objPDO->prepare($strQuery);
            foreach ( $this->arRelationMap as $key => $value)
            {            
                eval('$actualVal = &$this->' . $value . ';');
                if (isset($actualVal))
                {
                    if (array_key_exists($value, $this->arModifiedRelations))
                    {
                        if ((is_int($actualVal) || ($actualVal == null))
                        {
                            $objStatement->bindValue(':' . $value, $actualVal, PDO::PARAM_STR);
                        ]
                    }
                }
            }
            $objStatement->execute();
            $this->ID = $this->objPDO->LastInsertId($this->strTableName . "_id_seq");
        }    
    }
    
    public function MarkForDeletion()
    {
        $this->blForDeletion = true;
    }
    
    public function __destruct()
    {
        if (isset($this->ID))
        {
            if ($this->blForDeletion == true)
            {
                $strQuery = 'DELETE FROM "' . $this->strTableName . '" WHERE "id" = :eid';
                $objStatement = $this->objPDO->prepare($strQuery);
                $objStatement->bindValue(':eid', $this->ID, PDO::PARAM_INT);
                $objStatement->execute();
            }
        ]
    ]
    
    public function __call($strFunction, $arArguments)
    {
        $strMethodType = substr($strFunction, 0 ,3);
        $strMethodMember = substr($strFunction, 3);
        switch ($strMethodType)
        {
            case "set":
                return ($this->SetAccessor($strMethodMember, $arArguments[0]));
                break;
                
            case "get":
                return ($this->GetAccessor($strMethodMember));
                break;
        }
        return false;
        
    }
    
    private function setAccessor($strMember, $strNewValue)
    {
        if (property_exists($this, $strMember))
        {
            if (is_numeric($strNewValue))
            {
                eval ('$this->' . $strMember . ' = ' . $strNewValue . ';');
            } else {
                eval ('$this->' . $strMember . ' = "' . $strNewValue . '";');
            }
            $this->arModifieldRelations[$strMember] = "1";
        } else {
            return false;
        }
    }
    
    private function GetAccessor($strMember))
    {
        if ($this->blIsLoaded != true)
        {
            $this->Load();
        }
        if (property_exists($this, $strMember)
        {
            eval ('$strRetVal = $this->' . $strMember . ';');
            return $strRetVal;
        } else {
            return false; 
        }
    }
    

    }
    [/php]

    Ok mo che abbiamo fatto ? Nulla di che. Solamente definito una classe astratta che a sua volta definisce, ma non implementa, due metodi astratti che ci permetteranno di gestire il database oltre al fatto che i metodi Load e Save le query se le fanno da soli 🙂

    Il motivo per cui si ridefiniscono i metodi astratti è semplice. Supponiamo che voglia immettere nel mio database i seguenti dati relativi a un utente: Nome, Cognome, Username, Password, Mail, Avatar e che il mio collega pinco pallo voglia mettrci gli stessi miei ma con in più Indirizzo reale, cap, comune, città, numero di cellulare.

    A questo punto possiamo usare tutti e due la stessa classe solo che io definirò una sottoclasse come la seguente, lui una con alcuni campi in più.

    [php]
    class User extends DataBoundObject
    {

    protected $FirstName;
    protected $LastName;
    protected $Username; 
    protected $Password;
    protected $EmailAddress;
    protected $DataLastLogin;
    protected $TimeLastLogin;
    protected $DataAccountCreated;
    protected $TimeAccountCreated;
    
    protected function DefineTableName()
    {
        return "User";
    }
    
    protected function DefineRelationMap()
    {
        return array(
                        "id"                     => "ID",
                        "firstName"             => "FirstName",
                        "lastName"             => "LastName",
                        "username"                 => "Username",
                        "md5Pw"                 => "Password",
                        "emailAddress"         => "EmailAddress",
                        "dateLastLogin"        => "DateLastLogin",
                        "timeLastLogin"        => "TimeLastLogin",
                        "dateAccountCreated"    => "DateAccountCreated",
                        "timeAccountCreated"    => "TimeAccountCreated"
                    );
    }
    

    }
    [/php]

    Come si nota estendendo la classe principale implemento due metodi.

    Il primo indica al motore del database su che tabella lavorare
    Il secondo su quali campi.

    Si potrebbe estendere la classe principale anche in questo modo:

    [php]
    class Product extends DataBoundObject
    {

    protected $ProductName;
    protected $ProductDescription;
    protected $ProductQuantity; 
    protected $ProductPrice;
    protected $ProductImage;
    
    protected function DefineTableName()
    {
        return "Product";
    }
    
    protected function DefineRelationMap()
    {
        return array(
                        "id"                     => "ID",
                        "productName"             => "ProductName",
                        "productDescription"             => "ProductDescription",
                        "productQantity"                 => "ProductQuantity",
                        "productPrice"                 => "ProductPrice",
                        "productImage"         => "ProductImage"
                    );
    }
    

    }
    [/php]

    Ovviamente in entrambi i casi bisogna che si conoscano i veri campi nel database. In questi esempi ho usato le mie nomenclature