flow php

Trigger and Rule Query Builder

This document describes the PostgreSQL Trigger and Rule Query Builder components.

Trigger Commands

CREATE TRIGGER

Create triggers using the create_trigger() DSL function:

use function Flow\PostgreSql\DSL\create;
use Flow\PostgreSql\QueryBuilder\Schema\Trigger\TriggerEvent;

// Basic AFTER INSERT trigger
$builder = create()->trigger('audit_trigger')
    ->after(TriggerEvent::INSERT)
    ->on('users')
    ->execute('audit_function');
// CREATE TRIGGER audit_trigger AFTER INSERT ON users EXECUTE FUNCTION audit_function()

// Multiple events with FOR EACH ROW
$builder = create()->trigger('changes_trigger')
    ->after(TriggerEvent::INSERT, TriggerEvent::UPDATE, TriggerEvent::DELETE)
    ->on('orders')
    ->forEachRow()
    ->execute('track_changes');
// CREATE TRIGGER changes_trigger AFTER INSERT OR UPDATE OR DELETE ON orders FOR EACH ROW EXECUTE FUNCTION track_changes()

// BEFORE trigger on specific columns
$builder = create()->trigger('track_status')
    ->beforeUpdateOf('status', 'priority')
    ->on('tickets')
    ->forEachRow()
    ->execute('log_status_change');
// CREATE TRIGGER track_status BEFORE UPDATE OF status, priority ON tickets FOR EACH ROW EXECUTE FUNCTION log_status_change()

// INSTEAD OF trigger (for views)
$builder = create()->trigger('view_insert')
    ->insteadOf(TriggerEvent::INSERT)
    ->on('users_view')
    ->forEachRow()
    ->execute('handle_insert');
// CREATE TRIGGER view_insert INSTEAD OF INSERT ON users_view FOR EACH ROW EXECUTE FUNCTION handle_insert()

// With WHEN condition
$builder = create()->trigger('protect_admin')
    ->before(TriggerEvent::DELETE)
    ->on('users')
    ->forEachRow()
    ->when(raw_cond('OLD.role = \'admin\''))
    ->execute('raise_error');
// CREATE TRIGGER protect_admin BEFORE DELETE ON users FOR EACH ROW WHEN (old.role = 'admin') EXECUTE FUNCTION raise_error()

// OR REPLACE
$builder = create()->trigger('my_trigger')
    ->orReplace()
    ->after(TriggerEvent::INSERT)
    ->on('users')
    ->execute('my_function');
// CREATE OR REPLACE TRIGGER my_trigger AFTER INSERT ON users EXECUTE FUNCTION my_function()

// Constraint trigger with deferrable
$builder = create()->trigger('fk_check')
    ->constraint()
    ->after(TriggerEvent::INSERT)
    ->on('orders')
    ->from('users')
    ->deferrable()
    ->initiallyDeferred()
    ->forEachRow()
    ->execute('check_foreign_key');
// CREATE CONSTRAINT TRIGGER fk_check AFTER INSERT ON orders FROM users DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION check_foreign_key()

// With REFERENCING (transition tables)
$builder = create()->trigger('batch_process')
    ->after(TriggerEvent::INSERT)
    ->on('events')
    ->referencingNewTableAs('new_events')
    ->referencingOldTableAs('old_events')
    ->forEachStatement()
    ->execute('process_batch');
// CREATE TRIGGER batch_process AFTER INSERT ON events REFERENCING NEW TABLE new_events OLD TABLE old_events EXECUTE FUNCTION process_batch()

ALTER TRIGGER

Rename triggers or manage extension dependencies:

use function Flow\PostgreSql\DSL\alter;

// Rename trigger
$builder = alter()->trigger('old_name')
    ->on('users')
    ->renameTo('new_name');
// ALTER TRIGGER old_name ON users RENAME TO new_name

// Depends on extension
$builder = alter()->trigger('my_trigger')
    ->on('users')
    ->dependsOnExtension('my_extension');
// ALTER TRIGGER my_trigger ON users DEPENDS ON EXTENSION my_extension

// Remove dependency on extension
$builder = alter()->trigger('my_trigger')
    ->on('users')
    ->noDependsOnExtension('my_extension');
// ALTER TRIGGER my_trigger ON users NO DEPENDS ON EXTENSION my_extension

DROP TRIGGER

Drop triggers with optional IF EXISTS and CASCADE/RESTRICT:

use function Flow\PostgreSql\DSL\drop;

// Basic drop
$builder = drop()->trigger('audit_trigger')->on('users');
// DROP TRIGGER audit_trigger ON users

// With IF EXISTS
$builder = drop()->trigger('audit_trigger')
    ->ifExists()
    ->on('users');
// DROP TRIGGER IF EXISTS audit_trigger ON users

// With CASCADE
$builder = drop()->trigger('audit_trigger')
    ->on('users')
    ->cascade();
// DROP TRIGGER audit_trigger ON users CASCADE

// With schema
$builder = drop()->trigger('audit_trigger')
    ->on('public.users')
    ->restrict();
// DROP TRIGGER audit_trigger ON public.users

ENABLE/DISABLE TRIGGER

Use alter()->table() to enable or disable triggers:

use function Flow\PostgreSql\DSL\alter;

// Enable trigger
$builder = alter()->table('users')->enableTrigger('audit_trigger');
// ALTER TABLE users ENABLE TRIGGER audit_trigger

// Enable all triggers
$builder = alter()->table('users')->enableTriggerAll();
// ALTER TABLE users ENABLE TRIGGER ALL

// Enable user triggers only
$builder = alter()->table('users')->enableTriggerUser();
// ALTER TABLE users ENABLE TRIGGER USER

// Enable always (fires even during replication)
$builder = alter()->table('users')->enableTriggerAlways('critical_trigger');
// ALTER TABLE users ENABLE ALWAYS TRIGGER critical_trigger

// Enable replica (fires only during replication)
$builder = alter()->table('users')->enableTriggerReplica('sync_trigger');
// ALTER TABLE users ENABLE REPLICA TRIGGER sync_trigger

// Disable trigger
$builder = alter()->table('users')->disableTrigger('audit_trigger');
// ALTER TABLE users DISABLE TRIGGER audit_trigger

// Disable all triggers
$builder = alter()->table('users')->disableTriggerAll();
// ALTER TABLE users DISABLE TRIGGER ALL

// Disable user triggers only
$builder = alter()->table('users')->disableTriggerUser();
// ALTER TABLE users DISABLE TRIGGER USER

Rule Commands

CREATE RULE

Create rules using the create_rule() DSL function:

use function Flow\PostgreSql\DSL\create;

// DO NOTHING rule
$builder = create()->rule('prevent_delete')
    ->asOnDelete()
    ->to('users')
    ->doNothing();
// CREATE RULE prevent_delete AS ON DELETE TO users DO NOTHING

// DO INSTEAD rule
$builder = create()->rule('soft_delete')
    ->asOnDelete()
    ->to('users')
    ->doInstead("UPDATE users SET deleted = true WHERE id = OLD.id");
// CREATE RULE soft_delete AS ON DELETE TO users DO INSTEAD UPDATE users SET deleted = true WHERE id = old.id

// DO ALSO rule (additional action)
$builder = create()->rule('audit_insert')
    ->asOnInsert()
    ->to('orders')
    ->doAlso("INSERT INTO audit_log (action) VALUES ('insert')");
// CREATE RULE audit_insert AS ON INSERT TO orders DO INSERT INTO audit_log (action) VALUES ('insert')

// With WHERE condition
$builder = create()->rule('protect_admin')
    ->asOnDelete()
    ->to('users')
    ->where('OLD.role = \'admin\'')
    ->doNothing();
// CREATE RULE protect_admin AS ON DELETE TO users WHERE old.role = 'admin' DO NOTHING

// OR REPLACE
$builder = create()->rule('my_rule')
    ->orReplace()
    ->asOnUpdate()
    ->to('users')
    ->doNothing();
// CREATE OR REPLACE RULE my_rule AS ON UPDATE TO users DO NOTHING

// On SELECT (for views)
$builder = create()->rule('redirect_select')
    ->asOnSelect()
    ->to('old_view')
    ->doInstead('SELECT * FROM new_table');
// CREATE RULE redirect_select AS ON SELECT TO old_view DO INSTEAD SELECT * FROM new_table

// With schema
$builder = create()->rule('audit_rule')
    ->asOnInsert()
    ->to('public.users')
    ->doAlso("INSERT INTO audit VALUES ('insert')");
// CREATE RULE audit_rule AS ON INSERT TO public.users DO INSERT INTO audit VALUES ('insert')

DROP RULE

Drop rules with optional IF EXISTS and CASCADE/RESTRICT:

use function Flow\PostgreSql\DSL\drop;

// Basic drop
$builder = drop()->rule('prevent_delete')->on('users');
// DROP RULE prevent_delete ON users

// With IF EXISTS
$builder = drop()->rule('prevent_delete')
    ->ifExists()
    ->on('users');
// DROP RULE IF EXISTS prevent_delete ON users

// With CASCADE
$builder = drop()->rule('prevent_delete')
    ->on('users')
    ->cascade();
// DROP RULE prevent_delete ON users CASCADE

// With schema
$builder = drop()->rule('audit_rule')
    ->on('public.users')
    ->restrict();
// DROP RULE audit_rule ON public.users

Enums

TriggerTiming

use Flow\PostgreSql\QueryBuilder\Schema\Trigger\TriggerTiming;

TriggerTiming::BEFORE;      // BEFORE timing
TriggerTiming::AFTER;       // AFTER timing
TriggerTiming::INSTEAD_OF;  // INSTEAD OF timing (for views)

TriggerEvent

use Flow\PostgreSql\QueryBuilder\Schema\Trigger\TriggerEvent;

TriggerEvent::INSERT;    // INSERT event
TriggerEvent::UPDATE;    // UPDATE event
TriggerEvent::DELETE;    // DELETE event
TriggerEvent::TRUNCATE;  // TRUNCATE event

TriggerLevel

use Flow\PostgreSql\QueryBuilder\Schema\Trigger\TriggerLevel;

TriggerLevel::ROW;        // FOR EACH ROW
TriggerLevel::STATEMENT;  // FOR EACH STATEMENT (default)

RuleEvent

use Flow\PostgreSql\QueryBuilder\Schema\Rule\RuleEvent;

RuleEvent::SELECT;  // ON SELECT
RuleEvent::INSERT;  // ON INSERT
RuleEvent::UPDATE;  // ON UPDATE
RuleEvent::DELETE;  // ON DELETE

RuleAction

use Flow\PostgreSql\QueryBuilder\Schema\Rule\RuleAction;

RuleAction::ALSO;     // DO ALSO
RuleAction::INSTEAD;  // DO INSTEAD

Contributors

Join us on GitHub external resource
scroll back to top