ORM w PHP wykorzystując Zend Framework

Atykuł ten przedstawia jedynie moje rozwiązanie tego problemu.


Na początek tworzymy dwie klasy abstrakcyjne po których będziemy dziedziczyć dla każdego przypadku.

Klasa dla elementu.

abstract class Elements_Abstract {
    /**
     * @var Elements_Table_Abstract
     */
    protected $_dbTable = null;

    /**
     * @var Zend_Db_Table_Row_Abstract
     */
    protected $_dane = null;

    /**
     * Robi INSERT do bazy
     *
     * @param array $param
     * @return int;
     */
    protected function setNewToDb($param) {
        $newRow = $this->_dbTable->createRow();

        $this->setToDb($param, $newRow);

        return $this->_dbTable->getDefaultAdapter()->lastInsertId();
    }

    /**
     * Robi UPDATE do bazy
     *
     * @throws Zend_Exception
     * @param array $param nowe dane
     * @param Zend_Db_Table_Row_Abstract $row stare dane
     * @return unknown_type
     */
    protected function setToDb($param, $row) {
        $describeTable = $this->_dbTable->describeTable();

        foreach ($describeTable as $nazwaKolumny => $opisKolumny) {
            if(isset($param[$nazwaKolumny])) {
                $row->$nazwaKolumny = $param[$nazwaKolumny];
            } else {
                if(!$opisKolumny["NULLABLE"] and !$opisKolumny["PRIMARY"]) {
                    throw new Zend_Exception("Nie można zmienić danych gdyż brakuje wartości ".$nazwaKolumny.".");
                }
            }
        }

    $w = $row->save();

    return $w;
    }

    /**
     * Pobiera dane z bazy
     *
     * @throws Zend_Exception
     * @param int $id
     * @return void
     */
    protected function getFromDb($id) {
        $tmp = $this->_dbTable->select()->where("id = ?",$id);

        $row = $this->_dbTable->fetchRow($tmp);

        if(is_null($row)) {
            throw new Zend_Exception("Nie ma danych o takim ID.");
        }

        $this->setDane($row);
    }

    /**
     * Ustawia dane
     *
     * @throws Zend_Exception
     * @param Zend_Db_Table_Row_Abstract $v
     * @return void
     */
    public function setDane($value) {
        if($value instanceof Zend_Db_Table_Row) {
            $this->_dane = $value;
        } else {
            throw new Zend_Exception("Wartość która ma zostać ustawiona nie jest obiektem klasy Zend_Db_Table_Row");
        }
        $this->setToDb($value, $this->_dane);
    }

    /**
     * Zwraca dane
     *
     * @return Zend_Db_Table_Row_Abstract
     */
    public function getDane() {
        return $this->_dane;
    }

    /**
     * Usuwa dane
     *
     * @return void
     */
    public function deleteDane() {
        $this->_dane->delete();
    }
}

Klasa dla tablicy w bazie danych.

abstract class Elements_Table_Abstract extends Zend_Db_Table {

    protected $_name = "";
    protected $_primary = "";

    /**
     * Returns the column descriptions for a table.
     *
     * The return value is an associative array keyed by the column name,
     * as returned by the RDBMS.
     *
     * The value of each array element is an associative array
     * with the following keys:
     *
     * SCHEMA_NAME => string; name of database or schema
     * TABLE_NAME => string;
     * COLUMN_NAME => string; column name
     * COLUMN_POSITION => number; ordinal position of column in table
     * DATA_TYPE => string; SQL datatype name of column
     * DEFAULT => string; default expression of column, null if none
     * NULLABLE => boolean; true if column can have nulls
     * LENGTH => number; length of CHAR/VARCHAR
     * SCALE => number; scale of NUMERIC/DECIMAL
     * PRECISION => number; precision of NUMERIC/DECIMAL
     * UNSIGNED => boolean; unsigned property of an integer type
     * PRIMARY => boolean; true if column is part of the primary key
     * PRIMARY_POSITION => integer; position of column in primary key
     *
     * @param string $tableName
     * @return array
     */
    public function describeTable() {
        return $this->getDefaultAdapter()->describeTable($this->_name);
    }
}

I teraz będziemy rozpatrywać konkretny przypadek użycia owych klas i zastosowania podejścia ORM.
Przykład to moduł obsługujący „Myśl dnia”.

Taka klasa jak ta poniżej jest tworzona dla każdej tabelki w bazie.

Zend_Loader::loadClass("Elements_Table_Abstract");
class Elements_Table_ThinkDay extends Elements_Table_Abstract {
    protected $_name = "think_day"; //nazwa tabeli w bazie
    protected $_primary = "id"; //nazwa kolumny która jest kluczem głównym

    public function getThink() {
        $tmp = $this->select()->where("data_wykorzystania = ?",date("Y-m-d"));

        $result = $this->fetchRow($tmp);

        if(is_null($result)) {
            $result = $this->getRandomThink();
        }

        return $result;
    }

    private function getRandomThink() {
        $tmp = $this->select()
            ->where("data_wykorzystania IS NULL")
            ->order("RAND()")
            ->limit(1);

        $result = $this->fetchRow($tmp);

        if(is_null($result)) {
            $this->resetUsedThink();
            $result = $this->getRandomThink();
        }

        $this->setUsedThink($result->id);

        return $result;
    }

    private function resetUsedThink() {
        $this->update(
            array(
                "data_wykorzystania" => NULL
            ),
            "data_wykorzystania IS NOT NULL"
        );
    }

    private function setUsedThink($id) {
        $this->update(
            array(
                "data_wykorzystania" => date("Y-m-d")
            ),
            "id = `".$id."`"
        );
    }
}

 

 
W tej klasie znajdują się metody które wykonują operacje na bazie danych zwracając jako wynik obiekt lub tablicę obiektów klasy Zend_Db_Table_Row_Abstract.

A tu klasa która opisuje dane elementu. W zasadzie to na obiektach stworzonych z niej wykonujemy wszelkie operacje.

Zend_Loader::loadClass("Elements_Abstract");
class Elements_ThinkDay extends Elements_Abstract {

    /**
     * Konstruktor do który tworzy nowy rekord z podanymi danymi lub odczytuje dane o podanym ID
     *
     * @throws Zend_Exception
     * @param array|int $param
     * @return void
     */
    public function __construct($param) {
        Zend_Loader::loadClass("Elements_Table_ThinkDay");
        $this->_dbTable = new Elements_Table_ThinkDay();

        if(is_numeric($param)) {
            //pobrać dane z bazy
            $this->getFromDb($param);
        } elseif(is_array($param)) {
            //dodać dane do bazy i klasy
            $id = $this->setNewToDb($param);
            $this->getFromDb($id);
        } elseif($param instanceof Zend_Db_Table_Row_Abstract) {
            $this->_dane = $param;
        } elseif(is_null($param)) {
            $this->_dane = $this->_dbTable->getThink();
        } else {
            throw new Zend_Exception("Nie właściwe dane dla konstruktora.");
        }
    }

    public function setNotUsed() {
        $this->_dbTable->update(
            array(
                "data_wykorzystania" => NULL
            ),
            "id = `".$this->_dane->id."`"
        );
    }
}

A tu przykłady zastosowania (modułu artykułów):

Zend_Loader::loadClass("Elements_Table_ArticleCategory");
$a = new Elements_Table_ArticleCategory();
$b = $a->getDataByName("Test");
$idKategorii = $b->id;

Zend_Loader::loadClass("Elements_Article");

//Dodanie nowego rekordu do bazy danych
$c = new Elements_Article(array(
    "tytul" => "Test",
    "tresc" => "Tylko testuje",
    "id_kategorii" => $idKategorii,
    "data_modyfikacji" => date("Y-m-d H:i:s"),
    "data_dodania" => date("Y-m-d H:i:s")
));

//Pobranie danych z bazy
$d = new Elements_Article(3);
var_dump($d->getDane());
var_dump($d->getDane()->toArray());

//Pobranie informacji o tabeli w bazie danych
$d = new Elements_Article(4);
$tmp = $d->describeTable();
var_dump($tmp);

//Usunięcie rekordu z bazy danych
$d = new Elements_Article(3);
$d->deleteDane();

5 thoughts on “ORM w PHP wykorzystując Zend Framework

  1. Interesujaca strona, napewno bede teraz wpadal tu czesciej, pozdrawiam

  2. Ciekawy artykul, bede tu teraz wpadal czesciej, pozdrawiam bzerwiusz

  3. Ciekawy blog, zatrzymam sie tu na dluzej
    pozdro kxawriusz

  4. Interesujacy post, stronka ladna graficznie, jeszcze tu napewno zajrze
    pozdro lukkolini

  5. Marne podejście, zupełnie nie generyczne, nadpisujące gotowe funkcjonalności ( Zend_Db_Table::find() ) i to jeszcze w mało generyczny sposób ( Primary Key mozna łatwo wyciągnąć, nie tracąc zasobów, bo Zend_Db_table i tak robi „describe” na tabeli przy każdym uruchomieniu… )

Comments are closed.