Odkazy: dibi | API reference

Forum: [česky] [english]

dibi fórum

tiny ‘n’ smart
database layer

Nejste přihlášen(a)

#1 před 3 lety

romansklenar
Moderator
Registrovaný: 20. 7. 2008
Příspěvky: 780

Best Practice: Spouštění a zpracování uložených procedur

Uložené procedury v dibi se dají spouštět například pomocí fluent rozhraní:

dibi::command()->executeProcedure('DELETE_USER %l', array('ID' => 1))->execute();
// sql: EXECUTE PROCEDURE DELETE_USER(1)

Data vrácené procedurou se dají samozřejmě i získat:

$data = dibi::command()->executeProcedure('READ_USERS')->execute()->fetchAll();
// sql: EXECUTE PROCEDURE READ_USERS

Lepší ale je, si podporu pro procedury zapouzdřit do modelu.

$model->execute('DELETE_USER %l', array('ID' => 1));
$model->execute('READ_USERS')->fetchAll();

Uložené procedury mohou i vyhazovat výjimky na úrovní db a to ve dvou případech:

  1. když se procedura rovnou spoustí
EXECUTE PROCEDURE READ_USERS
  1. když se procedura použije za FROM místo tabulky
SELECT * FROM READ_USERS

V prvním případě je případná výjimka z procedury vyhozena hned při provedení dotazu (v dibi driverech funkce query), ale v druhém případě až při získávání dat (v dibi driverech funkce fetch).

Proto dotazy tohoto typu (2. případ) je nutné pro korektní zachycení výjimky v modelu použít ošetřenou funkci.

Spojíme-li takový přístup s fluent rozhraním, dá se s výsledkem procedur ještě v dotazu hezky pracovat:

$model->command()->select('*')->from('READ_USERS')->where("name LIKE 'John'")->orderBy('id')->execute()->safeFetch('all');
// sql: SELECT * FROM READ_USERS WHERE `name` LIKE 'John' ORDER BY `id`

Dost povídání, teď ukázka implementace do jednoduchého modelu.

Ukázka implementace procedur a zpracování chyb v modelu je pro dibi driver Firebird/Interbase, který na případnou výjimku v uložených procedurách reaguje vyhozením DibiProcedureException s metodou getSeverity() vracející název výjimky, tak je je definována v db.

Uvedený postup půjde ale použít i s ostatními drivery v dibi, jen je třeba zajistit vyhazování výjimek při výjimce v proceduře na straně db. Pokud ošetření výjimek nepotřebujete, můžete bloky try – catch v metodě execute() a metody processProcedureException() a safeFetch() vynechat.


/**
 * Simple model class.
 */
class Model extends DibiObject implements IModel
{
        ...


        /**
         * Creates fluent SQL builder.
         * @return DibiFluent
         */
        public function command()
        {
                return $this->connection->command();
        }


        /**
         * Executes procedure.
         * @param  string
         * @param  array
         * @return DibiResult
         */
        public function execute($procedure, array $data = NULL)
        {
                try {
                        $query = $this->command();

                        if ($data === NULL) {
                                $query->executeProcedure($procedure);

                        } else {
                                $query->executeProcedure("$procedure %l", $data);
                        }

                        return $query->execute();;

                } catch (DibiProcedureException $e) {
                        self::processProcedureException($e);
                }
        }


        /**
         * DibiResult fetch with handling possible exceptions.
         * @param  DibiResult
         * @param  string  possibile:fetch|fetchSingle|fetchAll|fetchPairs
         * @return DibiRow|array|FALSE
         */
        public static function safeFetch(DibiResult $result, $method = NULL, array $cols = NULL)
        {
                try {
                        switch ($method) {
                                case 'single':
                                case 'fetchSingle': $data = $result->fetchSingle(); break;
                                case 'all':
                                case 'fetchAll': $data = $result->fetchAll(); break;
                                case 'pairs':
                                case 'fetchPairs': $data = $result->fetchPairs($cols[0], $cols[1]); break;
                                default: $data = $result->fetch(); break;
                        }
                        return $data;

                } catch (DibiProcedureException $e) {
                        Model::processProcedureException($e);
                }
        }


        /**
         * Exception handling.
         * @param DibiProcedureException $e
         * @return void
         * @throws DibiProcedureException|InvalidStateException
         */
        public static function processProcedureException(DibiProcedureException $e)
        {
                if ($e->getSeverity() == 'SYSTEM') {
                        /* vážná systémová chyba -
                         *      zaloguj a ukonči běh aplikace / přenechej činnost ErrorPresenteru
                         */
                        Debug::processException($e);
                        throw $e;

                } elseif ($e->getSeverity() == 'ERROR' || $e->getCode() == 1) {
                        /* méně závažná chyba -
                         *      ulož do modelu pro pozdější zobrazení (např. validace formuláře) a pokračuj
                         */
                        $this->exception = $e;

                }
        }

        ...
}

A nakonec zbývá namapovat pro pohodlnější fetchnutí:

DibiResult::extensionMethod('DibiResult::safeFetch', 'Model::safeFetch');

Editoval romansklenar (21. 7. 2009 15:39)

 

#2 před rokem

musa
Člen
Registrovaný: 8. 9. 2009
Příspěvky: 33

Re: Best Practice: Spouštění a zpracování uložených procedur

Ahoj, jak bych pomocí výše uvedeného zavolal proceduru se dvěma parametry? Jedním IN a jedním OUT, který pak potřebuji přečíst.

 

#3 před rokem

cuga
dibi guru
Registrovaný: 22. 10. 2008
Příspěvky: 229

Re: Best Practice: Spouštění a zpracování uložených procedur

Je tenhle Best Practise stale aktualni, resp. je pouzitelny pro MySQL?

Mam ulozenou proceduru, pri volani z Adminera funguje, klasicke volani pres native

$query = $this->db->nativeQuery("call Podrizeni('$id')");
$data = $query;
$query->free();
return $data;

mi vyhodi chybu

PROCEDURE test.Podrizeni can't return a result set in the given context

S ulozenyma procedurama zatim nemam zadne zkusenosti, proto ani nevim, co by tohle mohlo zpusobovat. Mate nekdo tip?

 

Zápatí