daite / laravel-procedures
Execute allowlisted SQL Server stored procedures safely from Laravel applications.
Requires
- php: ^8.2
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- psr/log: ^3.0
Requires (Dev)
- laravel/pint: ^1.27
- mockery/mockery: ^1.6
- pestphp/pest: ^3.0|^4.0
README
Execute allowlisted SQL Server stored procedures from Laravel with bound parameters, validated identifiers, configurable connections and explicit return types.
Requirements
- PHP 8.2+
- Laravel 11, 12, or 13
- A SQL Server connection configured in Laravel
Installation
composer require daite/laravel-procedures php artisan vendor:publish --tag=procedures-config
Configure the published config/procedures.php file:
return [ 'connection' => env('PROCEDURES_DB_CONNECTION', 'sqlsrv'), 'schema' => env('PROCEDURES_DB_SCHEMA', 'dbo'), 'allowed' => [ 'get_products', 'create_user', ], 'null_as_empty_string' => false, 'logging' => [ 'enabled' => false, ], ];
Only procedures listed in allowed can be executed.
Selecting rows
Inject the executor contract into your service:
use Daite\LaravelProcedures\Contracts\ProcedureExecutor; final class ProductRepository { public function __construct( private readonly ProcedureExecutor $procedures, ) {} public function all(int $companyId): array { return $this->procedures->select( 'get_products', ['company_id' => $companyId], ); } }
Executing statements
Use statement() for procedures without a result set:
$success = $procedures->statement( 'create_user', ['name' => $name], );
Connection and schema overrides
Both values can be overridden per call:
$rows = $procedures->select( procedure: 'get_products', parameters: ['company_id' => 10], schema: 'catalog', connection: 'reporting_sqlsrv', );
Only pass trusted application values as schema and connection overrides. Schema identifiers are validated, but connection names select configuration from the consuming Laravel application.
Compatibility trait
Applications using the original payload API can migrate with:
use Daite\LaravelProcedures\Concerns\ExecutesProcedures; final class CatalogService { use ExecutesProcedures; }
The payload accepts procedure, fields, schema, and return_data:
$rows = $this->executeProcedure([ 'procedure' => 'get_products', 'return_data' => 1, 'fields' => ['company_id' => 10], ]);
ExecuteProcedureTrait remains available as a deprecated alias for projects
using the historical trait name. Database failures throw
ProcedureExecutionException; they are not converted to empty results.
Procedure fields helper
Some stored procedures expect separate comma-delimited campos and valores
strings:
use Daite\LaravelProcedures\Support\ProcedureHelper; $payload = ProcedureHelper::buildFieldsPayload([ 'name' => 'Coffee', 'active' => true, ]);
Result:
[
'campos' => 'name,active',
'valores' => 'Coffee,1',
]
The helper accepts scalar, null, and stringable values. It preserves input
order, converts null to an empty value, converts booleans to 1/0, and
removes commas inside values to preserve the delimited contract. Arrays and
non-stringable objects are rejected. The historical buildJsonFields() method
remains as a deprecated alias.
Error handling
Invalid or disallowed procedure calls throw InvalidProcedureException.
Database failures throw ProcedureExecutionException and preserve the driver
exception as the previous exception.
Security
- Procedure names require an explicit allowlist entry.
- Schema, procedure, and parameter names accept only safe identifier characters.
- Parameter values always use database bindings.
- Package logs never include parameter values.
- The underlying database driver exception may contain SQL or bindings when logged by the consuming application. Configure application logging according to its data-sensitivity requirements.
Testing
composer test
composer lint:check
composer audit
License
Laravel Procedures is open-source software licensed under the MIT license.