huangdijia/laravel-trigger
A lightweight Laravel package for defining and firing triggers tied to events or model changes. Register reusable trigger actions in configuration or service providers, then let them run automatically when the matching event occurs, keeping side effects decoupled from your controllers and models.
huangdijia/laravel-trigger adds database-trigger support to Laravel's schema builder, so triggers can be versioned alongside migrations instead of living as hand-run SQL.
composer require huangdijia/laravel-trigger
The service provider is auto-discovered on Laravel 5.5+, including the new Laravel 13 target added in v6.1.0. First place to look is config/trigger.php (publish with php artisan vendor:publish --tag=trigger-config) and the Trigger facade's source — the package is thin, and reading the ~200-line facade tells you more than the README.
First use case: pair a write log with an insert. In a migration:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::createTrigger('users_after_insert', "
CREATE TRIGGER users_after_insert
AFTER INSERT ON users
FOR EACH ROW
BEGIN
INSERT INTO user_audits (user_id, event, created_at)
VALUES (NEW.id, 'created', NOW());
END
");
}
public function down(): void
{
Schema::dropTriggerIfExists('users_after_insert');
}
};
Schema::createTrigger($name, $sql) and the Trigger facade (Trigger::create(), Trigger::drop(), Trigger::dropIfExists(), Trigger::get()) both ultimately run DB::unprepared(). Pick one and stick to it — mixing them in a codebase confuses reviewers about where trigger DDL lives.down(). Triggers aren't covered by migrate:fresh unless you drop them in rollback.Trigger::get() for introspection in health checks or a php artisan trigger:list-style command — useful to detect drift between environments where someone created a trigger by hand.DB::unprepared() can't parameterise, so anything user-controlled is an injection vector. Use trigger bodies only with static SQL.BEGIN…END blocks with multiple statements in the same way, no NEW/OLD aliasing semantics identical to MySQL). RefreshDatabase on an in-memory SQLite connection will pass locally and fail in production MySQL — always run trigger migrations against the actual engine.composer.json (laravel/framework: ^11|^12|^13 pattern) and you're done.CREATE OR REPLACE FUNCTION + CREATE TRIGGER ... EXECUTE FUNCTION, which is not what createTrigger's single-string body expects. Treat the package as MySQL/SQLite-friendly first; for Postgres expect to write the function DDL yourself and only use the package for the CREATE TRIGGER statement.DROP TRIGGER IF EXISTS on older versions and ignores FOR EACH ROW — some migrations that are fine on MySQL will need a DB::getDriverName() branch.TRIGGER on the app user. Trigger migrations will fail in CI against a read-only replica or restricted role — gate them behind an environment check or run as a migration with elevated credentials.migrate:rollback ordering. Rollback runs migrations in reverse; if a later migration drops a table a trigger references, the down() for the trigger must come after in migration order — which means it's defined in an earlier migration file than the table it touches. This is the single most common source of failed rollbacks.CREATE TRIGGER) is implicitly committed on MySQL; a failed migration mid-way may leave the trigger behind. Always use dropTriggerIfExists in down() and, ideally, at the top of up().INSERT INTO users, not the trigger body. Enable MySQL's general log or use SHOW TRIGGERS / information_schema.TRIGGERS when debugging.Trigger facade resolves from the container, so you can swap in your own implementation via $this->app->bind() in a service provider if you need per-driver SQL generation (e.g. a Postgres-aware subclass). That's the seam to use if you want dialect abstraction without forking.mysql:8 and mariadb differ on trigger recursion and SET inside triggers. Pin the image version if trigger behaviour is load-bearing.database/triggers/ folder of .sql files loaded via file_get_contents() in the migration, so SQL gets syntax highlighting and diff review.How can I help you explore Laravel packages today?