Mixins: Create a new mixin¶
About this document
This page presents how to develop a mixin. It assumes you have read about the general concepts.
Tutorial¶
Let's create a mixin called hello-world
with version 1.0.0
. It will
announce "Hello world".
First, enable hello-world
in info.xml
:
<extension type="module">
<mixins>
<mixin>menu-xml@1.0.0</mixin>
<mixin>mgd-php@1.0.0</mixin>
+ <mixin>hello-world@1.0.0</mixin>
</mixins>
</extension>
Next, create the mixin PHP file:
mkdir 'mixin/hello-world@1/'
nano 'mixin/hello-world@1/mixin.php'
This file will need some annotations:
<?php
/**
* @mixinName hello-world
* @mixinVersion 1.0.0
*/
Finally, we define a function. This function receives information about your extension.
It can use that information to do something pleasant, like say "Hello".
return function (CRM_Extension_MixInfo $mixInfo, CRM_Extension_BootCache $bootCache) {
printf("Hello world. I am extension \"%s\"!\n", $mixInfo->longName);
};
The mixin function runs very early -- and very frequently. Calling printf()
here would be too much.
Instead, we should be more selective -- we should use CiviCRM hooks to find the opportune moment. For example, hook_civicrm_alterContent allows you to append content to the HTML page-output.
return function (CRM_Extension_MixInfo $mixInfo, CRM_Extension_BootCache $bootCache) {
Civi::dispatcher()->addListener('hook_civicrm_alterContent', function ($event) use ($mixInfo) {
if (!$mixInfo->isActive()) {
return;
}
$event->content .= sprintf("<div>Hello world. I am extension \"%s\"!\n", $mixInfo->longName);
});
};
Finally, to activate the new mixin, you will need to clear system caches.
cv flush
FAQ¶
What is $mixInfo
?
A mixin like hello-world
or menu-xml
can be enabled on many different extensions.
The $mixInfo
tells you about the name, location, and status of the extension.
For more information, see CRM_Extension_MixInfo.
Note: The class CRM_Extension_MixInfo
was added in CiviCRM 5.45. If you are using the polyfill to run
older versions of CiviCRM, then it will provide a similar object - but the class-name may differ. Use relaxed type-hints.
What is $bootCache
?
Many mixins perform a scan over the extension source-code. Depending on the specific scan, this may be fast or slow. The boot-cache is optimized for storing the scan-results during boot.
Of course, like any cache, there is a trade-off between performance and complexity. I would only suggest it if (1) the scan-process is expensive and (2) the scan-data needs to be used frequently.
For more information, see CRM_Extension_BootCache.
Note: The class CRM_Extension_BootCache
was added in CiviCRM 5.45. If you are using the polyfill to run
older versions of CiviCRM, then it will provide a similar object - but the class-name may differ. Use relaxed type-hints.
Why does it return
an anonymous function?
Mixins are designed to be multi-version safe. This notation allows the system to load multiple versions of the same mixin -- without any conflicts over function-names or class-names.
Can I define named classes or named functions?
Sort of.
Remember that mixins are multi-version safe. Specifically, the system must be allowed to load major versions in parallel. As long as the major versions don't conflict, it should work.
For example, to prevent a conflict between hello-world@1.0.0
and hello-world@2.0.0
, you could use versioned-namespaces:
// FILE: mixin/hello-world@1.0.0.php
namespace HelloWorldV1; // <== Notice "V1"
class Greeter {
}
return function($mixInfo, $bootCache) {
$g = new Greeter();
};
// FILE: mixin/hello-world@2.0.0.php
namespace HelloWorldV2; // <== Notice "V2"
class Greeter {
}
return function($mixInfo, $bootCache) {
$g = new Greeter();
};
Another approach is to refrain from ever issuing a major update. This gist outlines such a scenario.
However, this has only been tested lightly. At time of writing, standard mixins do not use named-classes or named-functions.