diff --git a/docs/api/index.md b/docs/api/index.md index fe3dda76fc39b6c9cdd6d5a3752aa373d6d2b795..76c86aa9232040db524a9b2ef3139d1ffcb9adad 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -1,28 +1,24 @@ # The CiviCRM API -CiviCRM has a stable comprehensive **API** (Application Programming Interface) that can be used to access and manage data in CiviCRM. The API is the recommended way for any CiviCRM extension, CMS module, or external program to interact with CiviCRM. +CiviCRM has a stable, comprehensive **API** (Application Programming Interface) that can be used to access and manage data in CiviCRM. The API is the recommended way for any CiviCRM extension, CMS module or external program to interact with CiviCRM. Utilizing the API is superior to accessing core functions directly (e.g.calling raw SQL, or calling functions within the BAO files) because the API offers a consistent interface to CiviCRM's features. It is designed to function predictably with every new release so as to preserve backwards compatibility of the API for several versions of CiviCRM. If you decide to use other ways to collect data (like your own SQL statements), you risk running into future problems when changes to the schema and BAO arguments inevitably occur. The best place to begin working with the API is your own *test* install of CiviCRM, using the API explorer and the API parameter list. -For help creating your own API custom calls, see [civix generate:api](/extensions/civix.md#generate-api) +Extensions can provide additional API functionality. For help creating your own additions to the API, see [civix generate:api](/extensions/civix.md#generate-api). -## API explorer +## API Versions -The API explorer is a powerful GUI tool for building and executing API calls. +CiviCRM's API has major versions (APIv2, APIv3, APIv4) which are independent of the CiviCRM version. The API version is incremented more slowly in order to maintain stability within the extension ecosystem. Typically, two versions of the API are maintained concurrently (currently v3 and v4) to allow gradual transitions. New releases of CiviCRM may add features to the API but will not break backward-compatibility within an API version. -### Access the APIv3 explorer: +## API Explorer -1. Go to any CiviCRM site - * This can even be the [demo site](http://dmaster.demo.civicrm.org/). -1. Within the CivCRM menu, go to **Support > Developer > APIv3 Explorer** or go to the URL `/civicrm/api`. - -### Access the APIv4 explorer: +The API explorer is a powerful GUI tool for building and executing API calls. To access it: -1. Go to any CiviCRM site +1. Log in to a CiviCRM site as an administrator. * This can even be the [demo site](http://dmaster.demo.civicrm.org/). -1. Within the CivCRM menu, go to **Support > Developer > APIv4 Explorer** or go to the URL `/civicrm/api4`. +2. Within the CivCRM menu, go to **Support > Developer** and either **API Explorer v3** or **API Explorer v4** (URL `/civicrm/api` or `/civicrm/api4`). !!! warning The API explorer actually executes real API calls. It can modify data! So if you execute a `Contact` `delete` call, it will really delete the contact. As such, any experimenting is best done within a test site. @@ -33,14 +29,14 @@ You can select the entity you want to use, for example `Contact` and the action From the API explorer, you can get documentation on how to construct your API query. This is done either in the screen as you fill out the GUI to create your API call or in the v3 Explorer there are docs under the Code Docs tab which will point at the relevant aspects of the v3 code base that run the API calls. -## API examples +## API Examples (APIv3 Only) -Within the API Explorer you will be able to attain an example of the code that you should write to call the API. In APIv3, you can also access epscific examples of some API calls from the Examples tab within the explorer. You can also [explore these examples on GitHub](https://github.com/civicrm/civicrm-core/tree/master/api/v3/examples). +Within the API Explorer you will be able to attain an example of the code that you should write to call the API. In APIv3, you can also access specific examples of some API calls from the Examples tab within the explorer. You can also [explore these examples on GitHub](https://github.com/civicrm/civicrm-core/tree/master/api/v3/examples). -### API Examples in your extensions +### API examples in your extensions -From CiviCRM v5.8 the APIv3 explorer will now be able to show examples that are stored in your extension. The only requirement is that they are found in the same sort of directory structure as core e.g. in `<yourextension>/api/v3/examples/<entity>/<file>` +Beginning in CiviCRM v5.8, the APIv3 explorer will now be able to show examples that are stored in your extension. The only requirement is that they are found in the same sort of directory structure as core e.g. in `<yourextension>/api/v3/examples/<entity>/<file>` ## Changelog -All important changes made to the APIv3 are recorded in [APIv3 changes](/api/v3/changes.md). +All important changes made to the API are recorded in [APIv3 changes](/api/v3/changes.md) and [APIv4 changes](/api/v4/changes.md). diff --git a/docs/api/v4/changes.md b/docs/api/v4/changes.md new file mode 100644 index 0000000000000000000000000000000000000000..fbaaca542097531a480eaa7278bfd0b51a2fdaea --- /dev/null +++ b/docs/api/v4/changes.md @@ -0,0 +1,9 @@ +# API v4 Changelog + +This page lists additions to the v4 api with each new release of CiviCRM Core. + +Also see: [Differences Between Api v3 and v4](/api/v4/differences-with-v3.md). + +## Nothing here yet. + +API v4 is brand new. No changes yet! diff --git a/docs/api/v4/custom-data.md b/docs/api/v4/custom-data.md index e98e74f09189de31ebbd0a20b1e61a7c36585c43..0fb5cc277005d0fa78d5fd5f979cf9998068b29f 100644 --- a/docs/api/v4/custom-data.md +++ b/docs/api/v4/custom-data.md @@ -6,16 +6,39 @@ CiviCRM has a flexible custom data system which allows nearly any entity to be e Because single-record fields extend an entity 1-to-1, the API treats the custom fields as an extension of the regular fields. For example, normally an Event has fields like `id`, `title`, `start_date`, `end_date`, etc. Adding a custom group named "Event_Extra" and a field named "Room_number" would be accessible from the API as "Event_Extra.Room_number". You would retrieve it and set it as if it were any other field. Note that the `name` of a field is not to be confused with the `label` The API refers to custom groups/fields by name and not user-facing labels which are subject to translation and alteration. -For setting custom date fields, date format is anything understood by `strtotime`, e.g. "now" or "-1 week" or "2020-12-25". - ## Multi-Record Custom Data -Multiple record custom data sets are treated by the API as entities, which work similarly to other entities attached to contacts (Phone, Email, Address, etc.). For example, creating a multi-record set named "Work_History" could then be accessed via the API as an entity named "Custom_Work_History". Creating a record would be done like so: +Multiple record custom data sets are treated by the API as entities, which work similarly to other entities attached to contacts (Phone, Email, Address, etc.). For example, creating a multi-record set named "Work_History" could then be accessed via the API as an entity named "Custom_Work_History" (traditional style) or via the `CustomValue` php class (OO style). Creating a record would be done like so: +**PHP (traditional):** ```php -civicrm_api4('Custom_Work_history', 'create', $params); +civicrm_api4('Custom_Work_History', 'create', [ + 'values': ['entity_id': 202, 'Test_Field': 555] +]); +``` +**Javascript:** +```javascript +CRM.api4('Custom_Work_History', 'create', { + values: {"entity_id":202, "Test_Field":555} +}); ``` +**PHP (OO):** Note that the object-oriented style uses the `CustomValue` class: + +```php +\Civi\Api4\CustomValue::create('Work_History') + ->addValue('entity_id', 202) + ->addValue('Test_Field', 555) + ->execute(); +``` + +## Field Types and I/O Formats + +New custom fields can be configured to store different types of data (Text, Date, Number, URL, etc.). In most cases the I/O format via the api will be a string, however there are a few exceptions: + +- **Date fields:** Input format is anything understood by `strtotime`, e.g. "now" or "-1 week" or "2020-12-25". Output format is the ISO string "YYYY-MM-DD HH:MM:SS". +- **Checkbox/multi-select fields:** I/O format is an array of option values. + ## Try It Out -Once you have created some custom data in your system, look for it in the API explorer. Single-record data will appear as fields on the entities they extend, and multi-record data will appear in the list of entities (look under "C" alphabetically as they all start with the prefix "Custom_". +Once you have created some custom data in your system, look for it in the API Explorer. Single-record data will appear as fields on the entities they extend, and multi-record data will appear in the list of entities (look under "C" alphabetically as they all start with the prefix "Custom_". diff --git a/docs/api/v4/differences-with-v3.md b/docs/api/v4/differences-with-v3.md new file mode 100644 index 0000000000000000000000000000000000000000..7c04e3815b667ff4c14e43503972e216e087baea --- /dev/null +++ b/docs/api/v4/differences-with-v3.md @@ -0,0 +1,166 @@ +# Differences Between API v3 and v4 + +APIv4 is broadly similar to APIv3. Both are designed for reading and writing data. +Both use *entities*, *actions*, and *parameters*. However, APIv4 is specifically a +breaking-change which aims to reduce *ambiguity* and improve *flexibility* and *consistency*. + +This document walks through a list of specific differences. As you consider +them, it may help to have a concrete example expressed in both APIv3 and APIv4: + +<!-- Would be nice if Markdown made it easier to do side-by-side comparison... --> +<table> + <thead> + <tr> + <th>APIv3</th> + <th>APIv4</th> + </tr> + </thead> + <tbody> + <tr> +<td> + <em>Procedural-array style:</em><br/> + <div class="codehilite"><pre> + 1: $res = civicrm_api3('Contact', 'get', [ + 2: 'sequential' => 1, + 3: 'check_permissions' => 0, + 4: 'first_name' => 'Bob', + 5: 'return' => 'id,display_name', + 6: 'options' => [ + 7: 'limit' => 2, + 8: 'offset' => 2, + 9: ], +10: ]); +11: +12: foreach ($res['values'] as $row) { +13: echo $row['display_name']; +14: } +</pre></div> +</td> +<td> + <em>Procedural-array style:</em><br/> + <div class="codehilite"><pre> + 1: $res = civicrm_api4('Contact', 'get', [ + 2: 'checkPermissions' => FALSE, + 3: 'where' => [['first_name', '=', 'Bob']], + 4: 'select' => ['id', 'display_name'], + + 6: 'limit' => 2, + 7: 'offset' => 2, + + 9: ]); +10: +11: foreach ($res as $row) { +12: echo $row['display_name']; +13: } +</pre></div> + + <em>Object-oriented style:</em><br/> + <div class="codehilite"><pre> + 1: $res = \Civi\Api4\Contact::get() + 2: ->setCheckPermissions(FALSE) + 3: ->addWhere(['first_name', '=', 'Bob']) + 4: ->addSelect(['id', 'display_name']) + + 6: ->setLimit(2) + 7: ->setOffset(2) + + 9: ->execute(); +10: +11: foreach ($res as $row) { +12: echo $row['display_name']; +13: } +</pre></div> +</td> + </tr> + </tbody> +</table> + + +## API Wrapper + +* APIv4 supports two notations in PHP: + * Procedural/array style: `civicrm_api4('Entity', 'action', $params)` + * Object-oriented style: `\Civi\Api4\Entity::action()->...->execute()` +* When using OOP style in an IDE, most actions and parameters can benefit from auto-completion and type-checking. +* `$checkPermissions` always defaults to `TRUE`. In APIv3, the default depended on the environment (`TRUE` in REST/Javascript; `FALSE` in PHP). +* A 4th param `index` controls how results are returned: + * Passing a string will index all results by that key e.g. `civicrm_api4('Contact', 'get', $params, 'id')` will index by id. + * Passing a number will return the result at that index e.g. `civicrm_api4('Contact', 'get', $params, 0)` will return the first result and is the same as `\Civi\Api4\Contact::get()->execute()->first()`. `-1` is the equivalent of `$result->last()`. +* When chaining API calls together, back-references to values from the main API call must be explicitly given (discoverable in the API Explorer). + +## Actions +* For `Get`, the default `limit` has changed. If you send an API call without an explicit limit, then it will return *all* records. (In v3, it would silently apply a default of 25.) However, if you use the API Explorer, it will *recommend* a default limit of 25. +* The `Create` action is now only used for creating *new* items (no more implicit update by passing an id to v3 `create`). +* The `Save` action in v4 is most similar to v3's `create` - it accepts one or more records to create or update, infering the action based on the presence of `id` in each record. +* `Update` and `Delete` can be performed on multiple items at once by specifying a `where` clause, vs a single item by id in v3. +* `getsingle` is gone, use `$result->first()` or `index` `0`. +* `getoptions` is no longer a standalone action, but part of `getFields`. + +## Output +* Output is an object (`Result` aka [`ArrayObject`](https://www.php.net/manual/en/class.arrayobject.php)) rather than a plain `array`. +* In PHP, you can iterate over the `ArrayObject` (`foreach ($myResult as $record)`), or you can call methods like `$result->first()` or `$result->indexBy('foo')`. +* By default, results are indexed sequentially (`0,1,2,3,...` - like APIv3's `sequential => 1`). You may optionally index by `id`, `name`, or any other field, as in: + * (Procedural-style; use `$index` parameter): `civicrm_api4('Contact', 'get', [], 'id')` + * (OOP-style; use `indexBy()` method): `\Civi\Api4\Contact::get()->execute()->indexBy('id')` + +## Input +* Instead of a single `$params` array containing a mishmash of fields, options and parameters, each APIv4 parameter is distinct (see section on Params below). +* Custom fields are refered to by name rather than id. E.g. use `constituent_information.Most_Important_Issue` instead of `custom_4`. + +## Params + +If you used early versions of APIv3, you might have written some code like this: + +```php +civicrm_api3('Contact', 'get', array( + 'check_permissions' => 0, + 'first_name' => 'Elizabeth', + 'return' => 'id,display_name', + 'rowCount' => 2, + 'offset' => 2, +)); +``` + +You may notice that there are no subordinate arrays -- everything goes into one flat list of parameters. +As the system grew, this became a bit awkward: + +* What happens if you want to filter on a field named `return` or `action` or `rowCount`? +* How do you ensure that the same option gets the same name across all entities (`rowCount` vs `limit`)? +* Why does `first_name` use snake_case while `rowCount` uses lowerCamelCase? +* Why is `Contact.get` the only API to support `rowCount`? + +Over time, APIv3 evolved so that this example would be more typical: + +```php +civicrm_api3('Contact', 'get', [ + 'check_permissions' => FALSE, + 'first_name' => 'Elizabeth', + 'return' => ['id','display_name'], + 'options' => ['limit' => 1000, 'offset' => 2], +]); +``` + +Observe: + +* The `options` adds a place where you can define parameters without concern for conflicts. +* The new generation of `options` are more standardized - they often have generic implementations that work with multiple entities/actions. +* The top-level still contains a mix of *option* fields (like `return`) and *data* or *query* fields (like `first_name`). +* The old options at the top-level are deprecated but still around. + +APIv4 presented an opportunity to *break backward compatibility* and thereby *become more consistent*. In APIv4, a typical call would look like: + +```php +civicrm_api4('Contact', 'get', [ + 'checkPermissions' => FALSE, + 'where' => [['first_name', '=', 'Elizabeth']], + 'select' => ['id', 'display_name'], + 'limit' => 2, + 'offset' => 2, +]); +``` + +Key things to note: + +* The `options` array is completely gone. The params array *is* the list of options. +* Most of the options in the params array have shared/generic implementations - ensuring consistent naming and behavior. +* The *data* fields (e.g. `id`, `display_name`, and `first_name`) no longer appear at the top. They always appear beneath some other option, such as `where` or `select`. diff --git a/docs/extensions/civix.md b/docs/extensions/civix.md index 4c9287228d3bd6652b5b32fa87d6c7da41ac09c6..aa99c7d74e550a2d793f94605046c83228dbf455 100644 --- a/docs/extensions/civix.md +++ b/docs/extensions/civix.md @@ -159,8 +159,8 @@ If you want your extension to store data in the database, then you will need to 1. Re-install your extension. ```bash - $ cv ext:uninstall org.example.myextension - $ cv ext:enable org.example.myextension + $ cv ext:uninstall myextension + $ cv ext:enable myextension ``` Now your entity should be ready to use. Try it out with `cv api MyEntity.create` and `cv api MyEntity.get`. Then [add some tests](#generate-test). diff --git a/docs/financial/orderAPI.md b/docs/financial/orderAPI.md index 5594f74afc8b02faa11e1f2a907cc43bf5e4a026..82fc08b738c459908551c157396687f96c7ee3f4 100644 --- a/docs/financial/orderAPI.md +++ b/docs/financial/orderAPI.md @@ -3,21 +3,20 @@ will be documented and the core code underlying this area may change from version to version. -The Order API is intended to be used as the primary API for adding, updating, and deleting orders. +An 'order' is our developer term for a pseudo-entity that maps to the CiviCRM contribution object but also encompasses related entities like line items, memberships, event registrations and underlying financial entities. There is no single 'order' table but the top level order information is stored in the civicrm_contribution table. -An 'order' is a non-CiviCRM term that corresponds to how CiviCRM uses its contribution object in terms of handling the full life-cycle of a purchase of memberships, event registrations or making a donation. Unlike most APIs, there is no table directly associated with the Order API. +The Order API is intended to be used as the primary API for adding, updating, and deleting orders. When using the Order API you should: -Donations, memberships and event registrations are all potential line items in an order/contribution. Pledge payments via a contribution's line item are a potential future enhancement. +1. Rely on the Order API to create related objects like line items, memberships and event registrations. (Don't pre-create them) +2. Always create orders in a pending state (unfortunately you need to pass contribution_status_id = Pending in for historical reasons). +3. Expect the status of contribution and any related memberships or event registrations to be Pending. +4. Call Payment.create to record any payments that have been made against the order. +5. Rely on adding payments to transition any relate entities to completed. -The Order API wraps the creation of associated objects like memberships and event registrations. In other words, don't create the objects first before adding them as an array of `line_item.create` parameters; instead rely on the Order API to create them for you. +You should NOT -On creation, the status of contribution and any related memberships or event registrations is Pending if the contribution is pending. - -If you later remove a line item for a membership or event registration on an update to an order, the Order API will look after changing the status for the related membership and event registration objects. - -Do not try to update the status of a contribution, for example to Completed to reflect a payment, either directly or through the Order API. Instead, do a call to the Payment API for an amount that will complete the required payment. This will transition the status of the contribution to Completed and related membership(s) to New or Current and event registration(s). - -As a best practice the `Order.create` should be called with a status of Pending. Then a `Payment.create` should be called to record a payment. Calling Order.create without "contribution_status_id": "Pending" id deprecated. +1. Pre-create line items memberships or event registrations +1. Update the status of an order to Completed using any method OTHER than adding payments to it (Payment.create api) ## Sample `Order.create` for Simple Contribution @@ -327,3 +326,37 @@ Notes: Here is how to create an order for a membership, an event registration, and two separate contribution line items. [ Rich to provide] + +## Transitioning from Contribution.transact api to Order api + +Contribution.transact api was a v2 api that we left in place in v3. It has never had unit tests & has never been supported. Unfortunately by not being more aggressive about deprecating it some sites have adopted it. + +The Contribution.transact api will create a 'simple' contribution and process a payment. It will not create the line items correctly for anything other than a straight forward donation and does not follow our practice of creating a pending contribution and then adding a payment. It's likely there are other unknown gaps in how it works. + +The simplest first step to migrate off it is to replace the order api call with a call that follows the recommended flow but still does not address the line item creation gaps & it is recommended you look at the patterns above to do that. This first step looks like + +``` +# start with the same parameters as Contribution.transact. +$params = $transactParams; +# it would be better just to include the relevant params but.... +$paymentParams = $transactParams; +$params['contribution_status_id'] = 'Pending'; +if (!isset($params['invoice_id')) { + // Set an invoice_id here if you have not already done so. + // Potentially Order api should do this https://lab.civicrm.org/dev/financial/issues/78 +} +if (!isset($params['invoiceID']) { + // This would be required prior to https://lab.civicrm.org/dev/financial/issues/77 + $params['invoiceID'] = $params['invoice_id']; +} +$order = civicrm_api3('Order', 'create' $params); +try { + civicrm_api3('PaymentProcessor', 'pay', ['contribution_id' => $order['id']]); + civicrm_api3('Payment', 'create', ['contribution_id' => $order['id'], 'amount' => $params['amount']]); +} +catch { + // it failed +} +``` + +The above is a few more lines but it is an important step towards transitioning to a supported method and away from a flawed api. diff --git a/docs/financial/paymentAPI.md b/docs/financial/paymentAPI.md index 38eb41ea80b0bee8381ace02aacf36e327aa3c98..fe86e57e1009405502a4a36d60d736c2db1c6ead 100644 --- a/docs/financial/paymentAPI.md +++ b/docs/financial/paymentAPI.md @@ -11,10 +11,10 @@ It is now best practice to use the Payment.create API call. Note that paymentprocessor.pay handles the communication with a payment processor to instigate a payment. Similarly, paymentprocessor.refund handles the communication with a payment processor to instigate a refund. -After a contribution has been created, for example using the best practice Order.create api call, use the Payment API to: +After a contribution has been created, for example using the best practice Order.create API call, use the Payment.create API action to -- record a full payment -- record a partial payment or subsequent payment -- record that a payment was cancelled (not the same as cancelling the whole contribution) -- record the refund of a payment +* record a payment against the contribution - either fully or partially paying the contribution amount +* record a refund against the contribution + +Use the Payment.cancel api to reverse a refunded payment. diff --git a/mkdocs.yml b/mkdocs.yml index 5742f9139dfc1c11042d1c1828fe7a4f9f8e77ae..013f4b09079c829e132106f73d6478b12ab29a05 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,12 +56,11 @@ pages: - APIv4: - APIv4 Usage: api/v4/usage.md - APIv4 Actions: api/v4/actions.md - # - APIv4 Options: api/v4/options.md - APIv4 Joins: api/v4/joins.md - APIv4 Chaining: api/v4/chaining.md - APIv4 Custom Data: api/v4/custom-data.md - # - APIv4 Examples: api/v4/examples.md - # - APIv4 Changes: api/v4/changes.md + - Differences Between Api v3 and v4: api/v4/differences-with-v3.md + - APIv4 Changes: api/v4/changes.md - APIv3: - APIv3 Usage: api/v3/usage.md - APIv3 Actions: api/v3/actions.md