-
Notifications
You must be signed in to change notification settings - Fork 58
Tutorial
On this page you can find a few samples of code using this toolkit.
Namespace \AlexaCRM\WebAPI
is assumed by default in text. Code listings try to use fully-qualified class names.
Before you start using this library, you need to install it via Composer and include the generated autoloader into your application.
require_once 'vendor/autoload.php';
The library exposes a public API which includes an IOrganizationService-compatible Client
. There are a few options to create a new instance of the client.
ClientFactory
provides a simple way to instantiate a new Client
with all dependencies in place. As Dynamics 365 (online) is currently the only supported type of deployment, ClientFactory
contains only one static method, ClientFactory::createOnlineClient
. For IFD and On-Premises support please refer to Roadmap.
The toolkit uses server-to-server authentication and application users supported by Dynamics 365. Read the walkthroughs how to register your application on Azure AD and how to create a Dynamics 365 application user.
The minimal set of values you need to connect to Dynamics 365 Online is as follows:
- Organization URI, e.g.
https://contoso.crm.dynamics.com
- Application ID - identifies the Azure AD application registration associated with your Dynamics 365 organization
- Application Secret - a password generated in Settings / API Access / Keys section of your Azure AD application registration
Once you have all three items, it's time to create the client:
$client = \AlexaCRM\WebAPI\ClientFactory::createOnlineClient(
'https://contoso.crm.dynamics.com',
'00000000-0000-0000-0000-000000000000',
'Application Secret'
);
ClientFactory::createOnlineClient()
accepts an optional fourth argument. The method expects a map with logger
and cachePool
keys which hold as values PSR-3 compliant logger and PSR-6 compliant cache adapter correspondingly.
The library follows the lazy initialization pattern and doesn't actually validate the connection at client initialization time. To see how to handle errors, see below.
Sometimes you may want to create all necessary objects yourself. One case when this is useful is when you need to specify extra options in the client settings.
The first step is to create an OData\Settings
object. For online deployments, use OData\OnlineSettings
.
$settings = new \AlexaCRM\WebAPI\OData\OnlineSettings();
$settings->instanceURI = 'https://contoso.crm.dynamics.com';
$settings->applicationID = '00000000-0000-0000-0000-000000000000';
$settings->applicationSecret = 'Application Secret';
You can optionally supply a PSR-3 compliant logger.
$settings->setLogger( $logger );
You can optionally supply a PSR-6 compliant cache adapter.
$settings->cachePool = $cacheAdapter;
By default, the HTTP client used by the library verifies SSL certificates against the local CA certificate bundle. If it is broken, like as what happens often on development environments in Windows and OS X, you can either supply a valid CA bundle or disable verification.
$settings->caBundlePath = '/path/to/ca-bundle.crt';
// or
$settings->tlsVerifyPeers = false;
The default Web API version which the plugin connects to is 9.0
. It is the minimal tested Web API version as well. (Although not tested, it might very well work with earlier versions, perhaps with some limitations.)
If you need to change the API version, it is also configured in the Settings object:
$settings->apiVersion = '9.1';
The authentication process kicks in when the request is being made. For Online deployments, that means receiving a new access token from Azure AD and putting it into the Authorization
header of the request.
$middleware = new \AlexaCRM\WebAPI\OData\OnlineAuthMiddleware( $settings );
If a valid PSR-6 cache adapter is supplied, the token will be cached for the duration of its TTL.
Under the hood works a helper object that creates OData-compliant requests to the service endpoint. It consumes the OData\Settings
and OData\AuthMiddlewareInterface
objects.
$odataClient = new \AlexaCRM\WebAPI\OData\Client( $settings, $middleware );
The Web API Client
class is compatible with IOrganizationService
from the CRM SDK. To create one, you need to provide the OData client created earlier.
$client = new \AlexaCRM\WebAPI\Client( $odataClient );
After this step, the client is ready to make requests to Dynamics 365 Web API.
To create a new record in CRM, make an Entity object and pass it to the Client::Create()
method.
$contact = new \AlexaCRM\Xrm\Entity( 'contact' );
$contact['firstname'] = 'Nancy';
$contact['lastname'] = 'Anderson';
$contact['emailaddress1'] = '[email protected]';
$contactId = $client->Create( $contact );
To retrieve a record, you need to specify its entity name, entity ID and a column set. Although you can retrieve all columns, it is strongly advised to select only necessary attributes for performance reasons, both at CRM and client side.
$retrievedContact = $client->Retrieve( 'contact', $contactId, new \AlexaCRM\Xrm\ColumnSet( [ 'fullname', 'emailaddress1' ] ) );
To retrieve all columns:
new \AlexaCRM\Xrm\ColumnSet( true )
Client::RetrieveMultiple()
is used to retrieve multiple records from Dynamics 365. It supports FetchExpression
and QueryByAttribute
. QueryExpression
is not currently supported. For advanced OData queries in Web API see below.
Client::RetrieveMultiple()
returns an \AlexaCRM\Xrm\EntityCollection
instance.
Querying data with FetchXML is pretty straightforward. Use FetchExpression
and supply a stringt with a FetchXML query.
$fetchXML = <<<FETCHXML
<fetch mapping="logical">
<entity name="contact">
<attribute name="accountid" />
<attribute name="fullname" />
<attribute name="emailaddress1" />
</entity>
</fetch>
FETCHXML;
$fetchExpression = new \AlexaCRM\Xrm\Query\FetchExpression( $fetchXML );
$collection = $client->RetrieveMultiple( $fetchExpression );
To enable pagination, add the count
integer attribute to the fetch
element of the query. On consecutive requests, add the page number and the paging cookie to the fetch
element as well. MoreRecords
in the returned EntityCollection
will tell whether there are more pages to retrieve, and PagingCookie
will contain the paging cookie string.
$fetchXML = <<<FETCHXML
<fetch mapping="logical" count="10" paging-cookie="%s" page="%d">
<entity name="contact">
<attribute name="fullname"/>
<attribute name="emailaddress1"/>
</entity>
</fetch>
FETCHXML;
$page = 1;
$query = new \AlexaCRM\Xrm\Query\FetchExpression( sprintf( $fetchXML, '', $page ) );
$result = $client->RetrieveMultiple( $query );
foreach ( $result->Entities as $contact ) {
printf( "%s <%s>\n", $contact['fullname'], $contact['emailaddress1'] );
}
while ( $result->MoreRecords ) {
$query->Query = sprintf( $fetchXML, htmlentities( $result->PagingCookie, ENT_COMPAT | ENT_XML1 ), ++$page );
$result = $client->RetrieveMultiple( $query );
foreach ( $result->Entities as $contact ) {
printf( "%s <%s>\n", $contact['fullname'], $contact['emailaddress1'] );
}
}
$query = new \AlexaCRM\Xrm\Query\QueryByAttribute( 'contact' );
$query->AddAttributeValue( 'lastname', 'Example' );
$query->AddOrder( 'firstname', \AlexaCRM\Xrm\Query\OrderType::Descending() );
$query->ColumnSet = new \AlexaCRM\Xrm\ColumnSet( [ 'fullname', 'emailaddress1' ] );
$collection = $client->RetrieveMultiple( $query );
To enable pagination, specify the PageInfo
property of the QueryByAttribute
instance with \AlexaCRM\Xrm\Query\PagingInfo
as value. On consecutive requests, copy the value of PagingCookie
from the returned EntityCollection
to the PagingInfo
object (PageInfo
property of QueryByAttribute
). MoreRecords
in the returned EntityCollection
will tell whether there are more pages to retrieve, and PagingCookie
will contain the paging cookie string.
$pagingInfo = new \AlexaCRM\Xrm\Query\PagingInfo();
$pagingInfo->Count = 10;
$query = new QueryByAttribute( 'contact' );
$query->ColumnSet = new \AlexaCRM\Xrm\ColumnSet( [ 'fullname', 'emailaddress1'] );
$query->AddOrder( 'lastname', \AlexaCRM\Xrm\Query\OrderType::Ascending() );
$query->PageInfo = $pagingInfo;
$result = $client->RetrieveMultiple( $query );
foreach ( $result->Entities as $contact ) {
printf( "%s <%s>\n", $contact['fullname'], $contact['emailaddress1'] );
}
while ( $result->MoreRecords ) {
$pagingInfo->PagingCookie = $result->PagingCookie;
$result = $client->RetrieveMultiple( $query );
foreach ( $result->Entities as $contact ) {
printf( "%s <%s>\n", $contact['fullname'], $contact['emailaddress1'] );
}
}
$record = new \AlexaCRM\Xrm\Entity( 'contact', '00000000-0000-0000-0000-000000000000' );
$record['emailaddress1'] = '[email protected]';
$client->Update( $record );
$client->Delete( 'contact', '00000000-0000-0000-0000-000000000000' );
You can associate/disassociate records in two different ways: in a separate request (Associate
and Disassociate
) and during Create
/Update
.
The example given below will associate the account record with three contact records by setting contact[parentcustomerid]
lookup (Customer) attribute to the corresponding account lookup value.
$client->Associate(
'account',
'00000000-0000-0000-0000-000000000009',
new \AlexaCRM\Xrm\Relationship( 'contact_customer_accounts' ),
[
new \AlexaCRM\Xrm\EntityReference( 'contact', '00000000-0000-0000-0000-000000000001' ),
new \AlexaCRM\Xrm\EntityReference( 'contact', '00000000-0000-0000-0000-000000000002' ),
new \AlexaCRM\Xrm\EntityReference( 'contact', '00000000-0000-0000-0000-000000000003' ),
]
);
Client::Associate()
and Client::Disassociate()
have the same signature.
Dynamics 365 Web API allows you making associations between records during create/update request. When you prepare such a request, you need to know the corresponding navigation property for the given entity. In addition, lookup values with multiple targets like Customer lookups, require specifying different navigation properties for different entities (accounts and contacts for the Customer type).
This library does the heavy-lifting for you:
$contact = new \AlexaCRM\Xrm\Entity( 'contact', '00000000-0000-0000-0000-000000000001' );
$contact['parentcustomerid'] = new \AlexaCRM\Xrm\EntityReference( 'account', '00000000-0000-0000-0000-000000000009' );
// or
$contact['parentcustomerid'] = new \AlexaCRM\Xrm\EntityReference( 'contact', '00000000-0000-0000-0000-000000000002' );
$client->Update( $contact );
Similar to associating, you can disassociate records in one-to-many relationships during the update request. Simply set the attribute to NULL
to remove the association.
$account = new \AlexaCRM\Xrm\Entity( 'account', '00000000-0000-0000-0000-000000000002' );
$account['primarycontactid'] = null;
$client->Update( $account );
At this point, calling Client::Execute()
will throw an exception saying "Execute request not implemented". To execute actions and functions, you will need to use the underlying OData helper client which is accessed via Client::getClient()
method.
Actions can be invoked via OData\Client::executeAction()
method. Only the first argument, $actionName
, is required. Specify action parameters in the second argument. If the action is bound, third and fourth optional arguments specify the collection name and record ID.
The return value is given without the OData context annotation.
Functions can be invoked via OData\Client::executeFunction()
method. Only the first argument, $functionName
, is required. Specify function parameters in the second argument. If the function is bound, third and fourth optional arguments specify the collection name and record ID.
The return value is given without the OData context annotation.
If you want to construct an advanced OData query, you will need to use the HTTP client and query the data directly. The HTTP client is available via OData\Client::getHttpClient()
, and OData\Client
is available via Client::getClient()
.
Note: The data retrieved is not deserialized into Entity objects.
Have fun!