Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Trigger Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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');
    }
};

Implementation Patterns

  • Two entry points, same SQL. 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.
  • Wrap trigger creation in a dedicated migration after the table it depends on, and always implement down(). Triggers aren't covered by migrate:fresh unless you drop them in rollback.
  • Use 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.
  • Keep trigger bodies SQL-only. Avoid interpolating PHP values (table names aside) — bindless DB::unprepared() can't parameterise, so anything user-controlled is an injection vector. Use trigger bodies only with static SQL.
  • Pair with model events carefully. If a trigger already writes an audit row, don't also do it in an Eloquent observer — you'll double-log. Choose one place of truth: triggers for cross-cutting, at-the-DB level; observers for app logic.
  • Test against a real database. SQLite's trigger syntax differs (no 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.
  • CI matrix: on v6.1.0 you can add Laravel 13 to the test matrix alongside 11/12 without conditionals. Trigger SQL generation itself is version-agnostic.

Gotchas and Tips

  • Laravel 13 support (v6.1.0) is a pure compatibility bump. No API changes, no new methods, no deprecations, no config keys. Safe in-place upgrade from v6.0.x; lockstep the Laravel version in composer.json (laravel/framework: ^11|^12|^13 pattern) and you're done.
  • SQL dialect differences bite. MySQL triggers can't modify the table that fired them (error 1442). PostgreSQL requires 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.
  • SQLite doesn't support 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.
  • Privileges. Managed databases (RDS, Aurora, PlanetScale) often deny 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.
  • No transaction safety. DDL (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().
  • Logging is silent by default. If a trigger fails at runtime (e.g. subquery returns NULL into a NOT NULL column), the error surfaces on the writing query, not the trigger — stack traces point at INSERT INTO users, not the trigger body. Enable MySQL's general log or use SHOW TRIGGERS / information_schema.TRIGGERS when debugging.
  • Extension point: the 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.
  • Docker/CI images: mysql:8 and mariadb differ on trigger recursion and SET inside triggers. Pin the image version if trigger behaviour is load-bearing.
  • Don't commit trigger bodies as raw strings without review. They're invisible to static analysis and to IDE navigation; consider a database/triggers/ folder of .sql files loaded via file_get_contents() in the migration, so SQL gets syntax highlighting and diff review.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo