Oznámení
Best Practice: Spouštění a zpracování uložených procedur
před 10 lety
- romansklenar
- Člen | 657
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:
- když se procedura rovnou spoustí
EXECUTE PROCEDURE READ_USERS
- 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)
před 9 lety
- musa
- Člen | 30
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.
před 9 lety
- cuga
- Člen | 212
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?