Reputation: 303
I want to fetch multiple result sets from a stored procedure in laravel. Is there a way I can do this? Currently, I can get a single row's data using the below code:
$result = DB::statement('CALL user_login(' . $userId . ',"'
. $password . '",'
. '@success'.','
. '@first_Name'
);
$res = DB::select('select @success AS success, @first_Name AS firstName);
Here is my stored procedure:
DELIMITER //
DROP PROCEDURE IF EXISTS user_login//
create procedure user_login (IN userid VARCHAR(50),
IN password VARCHAR(50),
out success int,
OUT first_Name VARCHAR(255),
)
begin
declare count int(1);
set count =0;
select firstName, count(*)
into first_Name, count
from `tmc`.user where user_id = userid and pwd=password;
if count >0 then
set success =0;
else
set success=1;
end if;
end//
Upvotes: 3
Views: 7311
Reputation: 1300
If your stored procedure returns multiple outputs, In this case, you can handle this situation by two methods.
vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php
and set PDO::ATTR_EMULATE_PREPARES
as true
.It will create problems when you will developing APIs, because of all APIs return numbers as a string when response return in JSON Like: {"status": "1"}
but it should be {"status": 1}
Add connection in the config/database.php
use below code into connections
'mysql_procedure' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => 'sv_',
'prefix_indexes' => true,
'strict' => false,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),PDO::ATTR_EMULATE_PREPARES => true
]) : [],
],
update details as per your configuration.
$procRslts = DB::connection('mysql_procedure')
->select("CALL user_login(?,?,?,?)", array( $userId ,$password,$success,$firstName ));
I think in this you don't need to pass the last two parameters, you can write logic int procedure to get from the database.
You can get multiple outputs returned by the stored procedure.
Upvotes: 1
Reputation: 810
I am using the following code and it works flawlessly. Change it to suit your needs.
public static function CallRaw($procName, $parameters = null, $isExecute = false)
{
$syntax = '';
for ($i = 0; $i < count($parameters); $i++) {
$syntax .= (!empty($syntax) ? ',' : '') . '?';
}
$syntax = 'CALL ' . $procName . '(' . $syntax . ');';
$pdo = DB::connection()->getPdo();
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);
$stmt = $pdo->prepare($syntax,[\PDO::ATTR_CURSOR=>\PDO::CURSOR_SCROLL]);
for ($i = 0; $i < count($parameters); $i++) {
$stmt->bindValue((1 + $i), $parameters[$i]);
}
$exec = $stmt->execute();
if (!$exec) return $pdo->errorInfo();
if ($isExecute) return $exec;
$results = [];
do {
try {
$results[] = $stmt->fetchAll(\PDO::FETCH_OBJ);
} catch (\Exception $ex) {
}
} while ($stmt->nextRowset());
if (1 === count($results)) return $results[0];
return $results;
}
Example call:
$params = ['2014-01-01','2014-12-31',100];
$results = APIDB::CallRaw('spGetData',$params);
The resulting call will be:
CALL spGetData(?,?,?)
If there is only one resultset, it will be returned as is. If there are more, it will return an array of result sets. The key is using $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);
. Without it, a horrible SQLSTATE[HY000]: General error: 2053
exception will be thrown.
The try{} catch() block is used to eliminate the resultsets that cannot be fetched. Particularly, I have procedures that returns two resultsets, one as a result of an update (or other execute statements) and the last one as the real data. The exception thrown on fetchAll()
with an execute query will be PDOException
.
Warning: the function is not optimised. You can rewrite it with one single pass through the parameters.
Upvotes: 7