Skip to content

APIv4 Implicit Joins

In a relational database, you often need to work with data from multiple entities (tables) at once.

APIv4 provides a simple syntax for automatically reading and writing from entities related by a foreign key:

Read

Example: Get the title of an event's campaign to show to the user

Event::get()
  ->addSelect('campaign_id.title')
  ...

Write

Example: Create an event linked to a campaign without needing to look up the campaign's ID

Event::create()
  ->addValue('campaign_id.name', 'My_Cool_Campaign')
  ...

These are called implicit because you do not need to tell the API to join tables, it does so automatically by looking at the foreign key of the campaign_id field. For more complex use-cases, an explicit join allows a greater flexibility and control for get actions.

Joins or Chaining

There is some overlap between joins and Chaining; in some cases either technique can accomplish the same thing. Because chaining executes a new query for every result, joins are usually more efficient when given a choice between the two.

Identifying fields eligible for a join

Using getFields with the following params will identify fields that are eligible for an API join, in this example for the Contact entity:

Contact::getFields()
  ->addWhere('fk_entity', 'IS NOT EMPTY')
  ...

Multiple joins

Note: multiple joins are only available to get actions

Joining across the same foreign key multiple times:

civicrm_api4('Event', 'get', [
  'select' => ['title', 'campaign_id.name', 'campaign_id.campaign_type_id'],
  ...

Joining multiple levels deep:

Civi\Api4\Email::get()
  ->addSelect('contact_id.employer_id.display_name')
  ...

Combining a join with an option transformation:

Civi\Api4\Email::get()
  ->addSelect('contact_id.contact_type:label')
  ...