Skip to content

Hooks

The Hooks framework provides the ability to catch some actions in the system in order to call custom logic.

Common hooks for all entity types (called from the ORM Repository class):

  • beforeSave – just before a record is saved;
  • afterSave – after a record is saved;
  • beforeRemove – before a record is removed;
  • afterRemove – after a record is removed;
  • afterRelate – when two records are related through a many-to-many relationship;
  • afterUnrelate – when two records are unrelated through a many-to-many relationship;
  • afterMassRelate

Creating hook

  • create a file custom/Espo/Custom/Hooks/{EntityType}/{HookName}.php (you can also use a module directory);
  • declare a hook action method with a name the same as a hook name (e.g. beforeSave);
  • clear cache in Administration.

Note

{HookName} must be unique within {EntityType}. If there are two hooks (in different modules) with the same name for the same entity type, only one will be applied (honoring order).

Hook order

If you have several hooks, related to one Entity Type and with the same hook type, and running order is important, you can set a public static int $order property with an integer value.

Ascending order is applied – a hook with the smallest order number runs first.

If the order property is omitted, the value 9 is applied for a hook.

Order values of existing hooks:

  • Before-Save formula script: 11
  • After-Save workflow actions: 99 (advanced pack)

Interfaces

As of v7.4.

There are interfaces for built-in hooks. It's recommended that your hooks implement these interfaces. This makes easier for a developer to obtain needed data (passed to the hook) as each interface has a unique method signature. The list of interfaces:

  • Espo\Core\Hook\Hook\BeforeSave
  • Espo\Core\Hook\Hook\AfterSave
  • Espo\Core\Hook\Hook\BeforeRemove
  • Espo\Core\Hook\Hook\AfterRemove
  • Espo\Core\Hook\Hook\AfterRelate
  • Espo\Core\Hook\Hook\AfterUnrelate
  • Espo\Core\Hook\Hook\AfterMassRelate

Example

This example sets an Account Name for new Leads, if it's not set.

custom/Espo/Custom/Hooks/Lead/MyHook.php

<?php
namespace Espo\Custom\Hooks\Lead;

use Espo\ORM\Entity;

class MyHook
{    
    // An optional parameter, defines in which order hooks will be processed.
    // Lesser value means sooner.
    public static int $order = 5; 

    public function __construct(
        // Define needed dependencies.
    ) {}

    public function beforeSave(Entity $entity, array $options): void
    {
        if ($entity->isNew() && !$entity->get('accountName')) { 
            $entity->set('accountName', 'No Account');
        }
    }
}

Global hooks

If you need to apply a hook for all entities, you can use common hooks. To do this, put your hook class in Common directory, e.g. custom/Espo/Custom/Hooks/Common/{HookName}.php.

Additional default hooks

TargetList

  • afterOptOut – when a target clicks an opt-out link, data are passed in the 3rd $data argument
  • afterCancelOptOut – when a target subscribes again
  • afterOptIn – when a target opts-in through Lead Capture, data are passed in the 3rd $data argument

Meeting / Call

  • afterConfirmation – when an event attendee clicks on accept/decline/tentative link; details are passed in the 3rd $data argument

Contact

  • afterLeadCapture – when a contact (existing in crm) opts-in through Lead Capture, leadCaptureId is passed in the 3rd $data argument
  • afterOptOut
  • afterCancelOptOut

Lead

  • afterLeadCapture – when a lead opts-in through Lead Capture, leadCaptureId is passed in the 3rd $data argument
  • afterOptOut
  • afterCancelOptOut

LeadCapture

  • afterLeadCapture – when a target (lead or contact) opts-in through Lead Capture, target data are passed in the 3rd $data argument

CampaignTrackingUrl

  • afterClick - when a target (lead/contact/account) opened a tracking url in email

Examples

custom/Espo/Custom/Hooks/TargetList/MyHook.php

<?php
namespace Espo\Custom\Hooks\TargetList;

use Espo\ORM\Entity;

class MyHook
{    
    public function afterOptOut(Entity $targetList, array $options, array $data): void
    {
        $targetId = $data['targetId'];
        $targetType = $data['targetType'];
        $link = $data['link'];
    }
}

custom/Espo/Custom/Hooks/Meeting/MyHook.php

<?php
namespace Espo\Custom\Hooks\Meeting;

use Espo\ORM\Entity;

class MyHook
{    
    public function afterConfirmation(Entity $meeting, array $options, array $data): void
    {
        $status = $data['status'];
        $inviteeType = $data['inviteeType'];
        $inviteeId = $data['inviteeId'];

        if ($status === 'Accepted') {

        }
    }
}

Triggering hook

Inject the hook manager Espo\Core\HookManager to your class. Use the process method.

<?php
$this->hookManager->process($entityType, $hookType, $entity, $options);

Note

A hook name can't start with set. It's reserved for a dependency injection.