diff --git a/docs/hooks/hook_civicrm_buildProfile.md b/docs/hooks/hook_civicrm_buildProfile.md index 1899d8e10835961f4054da9806fb9f61ef2bc0c4..90a5dab5c4b50d5c1f6532ca8458b30b8e0cd0b0 100644 --- a/docs/hooks/hook_civicrm_buildProfile.md +++ b/docs/hooks/hook_civicrm_buildProfile.md @@ -6,11 +6,11 @@ This hook is called while preparing a profile form. ## Definition - buildProfile($name) + buildProfile($profileName) ## Parameters -- $name - the (machine readable) name of the profile. +- $profileName - the (machine readable) name of the profile. ## Returns @@ -21,9 +21,9 @@ immediately obvious how I can use this. I could do something like this: - function myext_civicrm_buildProfile($name) { + function myext_civicrm_buildProfile($profileName) { - if ($name === 'MyTargetedProfile) { + if ($profileName === 'MyTargetedProfile) { CRM_Core_Resources::singleton()->addScriptFile('org.example.myext', 'some/fancy.js', 100); diff --git a/docs/hooks/hook_civicrm_contactListQuery.md b/docs/hooks/hook_civicrm_contactListQuery.md index 50dc83159aaecc07aa9e5b10d6dad0a84e4667bf..d2e9663fe334201cffb1dd6324c1035325e8f801 100644 --- a/docs/hooks/hook_civicrm_contactListQuery.md +++ b/docs/hooks/hook_civicrm_contactListQuery.md @@ -18,7 +18,7 @@ the list of contacts to display. ## Definition - hook_civicrm_contactListQuery( &$query, $name, $context, $id ) + hook_civicrm_contactListQuery( &$query, $queryText, $context, $id ) ## Parameters @@ -29,7 +29,7 @@ the list of contacts to display. - the contact 'data' to display in the autocomplete dropdown (usually contact.sort_name - aliased as 'data') - the contact IDs -- $name - the name string to execute the query against (this is the +- $queryText - the name string to execute the query against (this is the value being typed in by the user) - $context - the context in which this ajax call is being made (for example: 'customfield', 'caseview') @@ -56,7 +56,7 @@ This example limits contacts in my contact reference field lookup $query = " SELECT c.sort_name as data, c.id FROM civicrm_contact c, civicrm_group_contact cg - WHERE c.sort_name LIKE '$name%' + WHERE c.sort_name LIKE '$queryText%' AND cg.group_id IN ( 5 ) AND cg.contact_id = c.id AND cg.status = 'Added' diff --git a/docs/hooks/hook_civicrm_optionValues.md b/docs/hooks/hook_civicrm_optionValues.md index 8078fc804110c014a0a07f01db757135755be484..fcc0f5dc6234f291cac768dd140c6c6f696319c9 100644 --- a/docs/hooks/hook_civicrm_optionValues.md +++ b/docs/hooks/hook_civicrm_optionValues.md @@ -12,13 +12,13 @@ hook to add/remove options from the option group. ## Definition ```php -hook_civicrm_optionValues(&$options, $name) +hook_civicrm_optionValues(&$options, $groupName) ``` ## Parameters - array `$options` - the current set of options -- string `$name` - the name of the option group +- string `$groupName` - the name of the option group ## Returns diff --git a/docs/hooks/hook_civicrm_processProfile.md b/docs/hooks/hook_civicrm_processProfile.md index 05888e9afa48981d6ea899b250a25760e872f8f6..3b6401549423edf295b0644e48e743d33b18b845 100644 --- a/docs/hooks/hook_civicrm_processProfile.md +++ b/docs/hooks/hook_civicrm_processProfile.md @@ -6,11 +6,11 @@ This hook is called processing a valid profile form submission (e.g. for "civicr ## Definition - processProfile($name) + processProfile($profileName) ## Parameters -- $name - the (machine readable) name of the profile. +- $profileName - the (machine readable) name of the profile. !!! Tip In SQL, this corresponds to the "name" column of table "civicrm_uf_group" diff --git a/docs/hooks/hook_civicrm_searchProfile.md b/docs/hooks/hook_civicrm_searchProfile.md index d8216db6f22014845f56c88b2178c74e525594a8..f79739384439cdd4c6ada631f4ef8a4a8da8b342 100644 --- a/docs/hooks/hook_civicrm_searchProfile.md +++ b/docs/hooks/hook_civicrm_searchProfile.md @@ -7,11 +7,11 @@ profile). ## Definition - searchProfile($name) + searchProfile($profileName) ## Parameters -- $name - the (machine readable) name of the profile. +- $profileName - the (machine readable) name of the profile. ## Returns diff --git a/docs/hooks/hook_civicrm_validateProfile.md b/docs/hooks/hook_civicrm_validateProfile.md index b961c48a575d7b156b34a59097f78f7ec36099b4..fbba815fa5899051467fd73f91096d2142a7b60f 100644 --- a/docs/hooks/hook_civicrm_validateProfile.md +++ b/docs/hooks/hook_civicrm_validateProfile.md @@ -6,11 +6,11 @@ This hook is called while validating a profile form submission. ## Definition - validateProfile($name) + validateProfile($profileName) ## Parameters -- $name - the (machine readable) name of the profile. +- $profileName - the (machine readable) name of the profile. ## Returns diff --git a/docs/hooks/hook_civicrm_viewProfile.md b/docs/hooks/hook_civicrm_viewProfile.md index 3e3636f7b1eb0d5ff76405fd58634e59d5687a10..40bfda32d846376436d4191bcc7afaa0a28cbbb8 100644 --- a/docs/hooks/hook_civicrm_viewProfile.md +++ b/docs/hooks/hook_civicrm_viewProfile.md @@ -6,11 +6,11 @@ This hook is called while preparing a read-only profile screen. ## Definition - viewProfile($name) + viewProfile($profileName) ## Parameters -- $name - the (machine readable) name of the profile. +- $profileName - the (machine readable) name of the profile. ## Returns diff --git a/docs/hooks/setup/symfony.md b/docs/hooks/setup/symfony.md new file mode 100644 index 0000000000000000000000000000000000000000..1345298fbabefa64e3f764d4738c50d2a57118d1 --- /dev/null +++ b/docs/hooks/setup/symfony.md @@ -0,0 +1,133 @@ +## Overview + +The [__Symfony EventDispatcher__](http://symfony.com/components/EventDispatcher) is an +event library used by several PHP applications and frameworks. For example, +Symfony SE, Drupal 8, Magento, Laravel, CiviCRM, and many others support +`EventDispatcher`. It provides a common mechanism for dispatching and listening +to events. + +In CiviCRM v4.7.19+, you can use Symfony `EventDispatcher` with hooks. + +```php +Civi::dispatcher()->addListener('hook_civicrm_EXAMPLE', $callback, $priority); +``` + +Using `EventDispatcher` is useful if you need more advanced features, such as: + + * Setting the priority of an event-listener + * Registering an object-oriented event-listener + * Registering a dynamic, on-the-fly event-listener + * Registering multiple listeners for the same event + * Registering for internal/unpublished events + +For a general introduction or background on `EventDispatcher`, consult the [Symfony documentation](http://symfony.com/doc/2.7/components/event_dispatcher.html). + +## Example: `Civi::dispatcher()` + +In this case, we have a CiviCRM extension or Drupal module named `example`. +During the system initialization, we lookup the `EventDispatcher`, call +`addListener()`, and listen for `hook_civicrm_alterContent`. + +```php +function example_civicrm_config(&$config) { + if (isset(Civi::$statics[__FUNCTION__])) { return; } + Civi::$statics[__FUNCTION__] = 1; + + Civi::dispatcher()->addListener('hook_civicrm_alterContent', '_example_say_hello'); +} + +function _example_say_hello($event) { + $event->content = 'hello ' . $event->content; +} +``` + +!!! tip "Using the `$event` object" + Hook parameters are passed as an object, `$event`. + For example, [`hook_civicrm_alterContent`](/hooks/hook_civicrm_alterContent/) + has the parameters `(&$content, $context, $tplName, &$object)`. + You can access the data as `$event->content`, `$event->context`, `$event->tplName`, and `$event->object`. + +!!! tip "Using `hook_civicrm_config`" + In some environments, `hook_civicrm_config` runs multiple times. The flag + `Civi::$statics[__FUNCTION__]` prevents duplicate listeners. + +## Example: `Container::findDefinition()` + +In this case, we have a CiviCRM extension or Drupal module named `example`. +We lookup the defintion of the `dispatcher` service and amend it. + +```php +function example_civicrm_container($container) { + $container->findDefinition('dispatcher') + ->addMethodCall('addListener', array('hook_civicrm_alterContent', '_example_say_hello')); +} + +function _example_say_hello($event) { + $event->content = 'hello ' . $event->content; +} +``` + +<!-- + TODO: an example using a container-service and tag. See "Registering Event Listeners + in the Service Container" from http://symfony.com/doc/2.7/components/event_dispatcher.html +--> + +## Events + +CiviCRM broadcasts many different events through the `EventDispatcher`. These +events fall into two categories: + + * __External Events/Hooks__ (v4.7.19+): These have a prefix `hook_civicrm_*`. They extend + the class [`GenericHookEvent`](https://github.com/civicrm/civicrm-core/blob/master/Civi/Core/Event/GenericHookEvent.php) + (which, in turn, extends [`Event`](http://api.symfony.com/2.7/Symfony/Component/EventDispatcher/Event.html)). + Hooks are simulcast across `EventDispatcher` as well as CMS-specific event systems. + * __Internal Events__ (v4.5.0+): These have a prefix `civi.*`. They extend + the class [`Event`](http://api.symfony.com/2.7/Symfony/Component/EventDispatcher/Event.html). + They are *only* broadcast via `EventDispatcher` (**not** CMS-specific event systems). + +You can recognize these events by their naming convention. Compare: + +```php +// Listen to a hook. Note the prefix, "hook_civicrm_*". +Civi::dispatcher()->addListener('hook_civicrm_alterContent', $callback, $priority); + +// Listen to an internal event. Note the prefix, "civi.*". +Civi::dispatcher()->addListener('civi.api.resolve', $callback, $priority); +``` + +## Methods + +The `EventDispatcher` has several different methods for registering a +listener. Our examples have focused on the simplest one, `addListener()`, +but the Symfony documentation describes other methods (`addSubscriber()`, +`addListenerService()`, and `addSubscriberService()`). See also: + + * [Symfony EventDispatcher](http://symfony.com/doc/2.7/components/event_dispatcher.html) + * [Symfony ContainerAwareEventDispatcher](http://symfony.com/doc/2.7/components/event_dispatcher/container_aware_dispatcher.html) + +!!! tip "Using `addListener()`" + When calling `addListener()`, you _can_ pass any [PHP callable](http://php.net/manual/en/language.types.callable.php). + However, _in practice_, the safest bet is to pass a string (function-name) or array + (class-name, function-name). Other formats may not work with the + [container-cache](http://symfony.com/doc/2.7/components/dependency_injection/compilation.html). + +## History + + * _CiviCRM v4.5.0_: Introduced Symfony EventDispatcher for internal use (within `civicrm-core`). For example, + APIv3 dispatches the events `civi.api.resolve` and `civi.api.authorize` while executing an API call. + * _CiviCRM v4.7.0_: Introduced `hook_civicrm_container`. + * _CiviCRM v4.7.0_: Integrated the Symfony `Container` and `EventDispatcher`. + * _CiviCRM v4.7.19_: Integrated `hook_civicrm_*` with the Symfony `EventDispatcher`. + * _CiviCRM v4.7.19_: Added the `Civi::dispatcher()` function. + +## Limitations + + * _Boot-critical hooks_: `hook_civicrm_config`, `hook_civicrm_container`, and `hook_civicrm_entityTypes` + are fired during the bootstrap process -- before the Symfony subsystems are fully online. Consequently, + you may not be able to listen for these hooks. + * _Opaque CMS listeners_: Most hooks are dispatched through `EventDispatcher` as well as the traditional + hook systems for Drupal modules, Joomla plugins, WordPress plugins, and/or CiviCRM extensions. + This is accomplished by _daisy-chaining_: first, the event is dispatched with `EventDispatcher`; then, the + listener `CiviEventDispatcher::delegateToUF()` passes the event down to the other systems. + If you inspect `EventDispatcher`, there will be one listener (`delegateToUF()`) + which represents _all_ CMS-based listeners. diff --git a/mkdocs.yml b/mkdocs.yml index e75e479c6810952bc78f56859bd796ab38276668..1574a42ddf16d2b760095b0735b89e4086b1ce40 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -86,6 +86,7 @@ pages: - Hooks: - Using hooks: hooks.md # page-tree = NEED_PAGE_MOVE to /hooks/usage.md - Setup: + - Hooks with Symfony: hooks/setup/symfony.md - Hooks with Joomla: hooks/setup/joomla.md - Hooks with Drupal: hooks/setup/drupal.md - Hooks with Wordpress: hooks/setup/wordpress.md