Code Monkey home page Code Monkey logo

business-central-php's Introduction

Business Central for PHP

License: MIT
This software is provided as is and without any warrenties of any kind.
If you find a bug or have a feature request, please create an issue

Install using Composer

composer require coolrunner/business-central-sdk

Getting Started

  • Register for a set of Business Central credentials
    • The SDK uses Basic Authentication
  • Install the library into your application
  • Load the SDK

Building Models

As Business Central's web services are dynamically created all entities could be pre generated.

BusinessCentral\Constructor::buildModels(
    'my-tenant-id.onmicrosoft.com',
    'Application (client) ID',
    'CLIENT_SECRET'
);

This can be generated using a post-autoload-dump composer script using your credentials to get the entities exposed through your web services.

Connecting to Business Central

Business Central for PHP uses a singleton pattern for SDK instances.

Once an instance has been initialized it will fetch the schema from your Business Central. (Standard entities are included).

$sdk = \BusinessCentral\SDK::instance('my-tenant-id.onmicrosoft.com', [
	// OAuth2 [Required]
    'client_id' => 'Application (client) ID',
    
    // Basic auth token [Required]
    'client_secret'    => '***',
    
    // Default collection size [Optional]
    // Amount of entities to load initially and per page per collection
    // Can be changed on the fly on any collection instance
    'default_collection_size' => 20,
    
    // Default environment [Optional - 'production' by default]
    'environment' => 'dev'
]);

Fetching Entities Manually

Business Central for PHP uses an internal query builder to navigate and fetch entities.

Accessing the query
$query = $sdk->query();
Navigating using the query
$query = $sdk->query();

// Navigate using ->navigateTo(...) (or shorthand ->to(...))
$query->to('Company','CompanyName')->to('invoicingCustomers'); // Equivalent of fetching from 'Company(CompanyName)/invoicingCustomers'
Fetching results
$collection = $query->fetch(); // Fetches the results of the query as a collection

// Fetches the first result of the query 
$entity = $query->first(); // equivalent of $query->fetch()->first()

// Fetches all the results of the query 
$entity = $query->all(); // Fetches all matching entities

Fetching Entities

// Fetch a single company
$company = $sdk->company('CompanyName');
// Fetch all companies available to the authorized user
$companies = $sdk->companies();

Class Reference

Entity

Class for Entity fetched from Business Central

Entity Properties / Relations

Due to the dynamics of Business Central, the Entities from Business Central doesn't necessary have standardized properties across all implementations. Please refer to your specific implementation.

Alternatively check the entities.md generated when building models.

Fetch Relation

You can fetch relations from a given entity by calling the name of the relation as a property or method:

// Returns a collection of entities if the relation is a collection,
// else returns the single instance of the related entity or if none is found
$customers = $entity->relation;
// Returns a query builder pointing at the relation - Use this if you have further filters (See [Builer/Filters](#builderfilters))
$customers = $entity->relation();

If the relations isn't pointing at a collection, then only the single related entity will be returned.

Check Entities Overview to see if the relation is a collection type or not.

Entity Methods

  • fill(array $attributes) : Entity

    • Update/set multiple properties as once - Only fillable properties will be set
    • Check the individual entity type on Entities Overview
  • save() : bool

    • Save the entity to Business Central
  • validate() : bool

    • Validate the entity against the rules set by Business Central (this method is also called during save()
  • getEntityType: EntityType

    • Get the entity's EntityType
  • query() : Builder

    • Get a query pointing to the entity
  • toArray() : array

    • Get the entity as an associative array

EntityCollection

Container class for Entities fetched from Business Central

EntityCollection Properties

None

EntityCollection Methods

  • find(string|array $id, $default = null) : Entity | null

    • Finds and returns an entity from the collection with the given id or $default on failure
  • create(array $attributes) : Entity

    • Creates and returns a new Entity with the given attributes
  • update(string|array $id, array $attributes) : Entity

    • Updates and returns an existing Entity with the given attributes
  • delete(string|array $id) : bool

    • Deletes en entity from the collection with the given id - Returns true/false on success/failure
  • first($default = null) : Entity | null | mixed

    • Returns the first index of the collection or $default is empty
  • count() : int

    • Returns the amount of entities in the collection
  • all() : array

    • Get all Entities in the collection as an array
    • This is a heavy method call on large collections - Use wisely!
    • Note: If the EntityCollection isn't fully loaded then the remaining Entities will be fetched!
  • getEntitySet: EntitySet

    • Get the collections EntitySet
  • query() : Builder

    • Get a query pointing to the collection (includes extentions)
  • toArray() : array

    • Get the collection as an array (converts all entities within also)

Builder

Query builder used to fetch and update entities on Business Central

Note: All EntityCollection method calls can be performed on the Builder instance itself,
due to an internal call to $collection->fetch() before the method call.

Builder Properties

None

Builder Methods

Builder Navigation
  • navigateTo(string $component, string $id = null) | to(string $component, string $id = null) : self

    • Point the Builder towards a component
  • fetch() : EntityCollection

    • Fetch all entities at the pointer
Builder Meta
  • count() : int

    • Get the total count matching the Builder
  • exists() : bool

    • Check if anything matches the builder
Builder Pagination
  • limit(int $limit = null) : self|int

    • Set the limit if $limit is set, else returns the current limit
  • page(int $page = null) : self|int

    • Set the page if $page is set, else returns the current page
  • nextPage() : self

    • Flip to the next page
  • prevPage() : self

    • Flip to the previous page
Builder Sorting
  • orderBy($property, string $direction = 'asc') : self

    • Sort the Builder by a specified property and $direction
    • The $field property can be an array containing multiple conditions ( ['property' => 'direction'] )
  • orderByAsc(string $property) : self

    • Sort the Builder by a specified property ascending
  • orderByDesc(string $property) : self

    • Sort the Builder by a specified property descending
Builder Expansion

OData Reference: Reference

  • expand(array $relations) : self
    • Expand selection Expansion allows us to fetch multiple levels of entities in a single request.
      This allows us to minimize the amount of requests needed to get the entities needed.
Basic Usage

Example:

$company->customers()->expand([
    'paymentMethod',
    'picture',
])->fetch();

The above will fetch a collection with all customers from a company with their paymentMethod relation in one request.

Multilevel Expansion / Filtered Expansion

Utilizing closures it is possible to nest expansions and apply filters to the expansions at any level.

$company->customers()->expand([
    'paymentMethod' => function(Builder $query) {
    	$query->where('code', '=', 'CR3D17C4RD')
	      ->expand(['nested_relation']);
    },
    'picture',
]);

// Query: companies(...)/customers?$expand=picture,paymentMethod($filter=code eq 'CR3D17C4RD';$expand=nested_relation;$count=true)&$top=40&$count=true

See Filtering

Note: The nesting can be done indefinitely and as complex as you want, but keep in mind there still is a character limit to URLs.

Builder Filtering

OData reference: Reference

Filtering allows us to more carefully select which entities we fetch from Business Central.
This allows us to improve performance and exclude irrelevant models from our processing from the start.

A number of different filtering methods exists.
For every filter method an "OR" version exists (eg. whereDate(...) -> orWhereDate(...)
The $before argument is the boolean operator prepended to the query before every clause.

  • where(string $property, $operator = null, $value = null, string $before = 'and') : self

    • Basic where clause
    • Shorthand: where('displayName', 'John Doe'); is the same as where('displayName', '=', 'John Doe')
  • whereIn(string $property, array $values, string $before = 'and') : self

    • Where property in a group of values
  • whereDateTime(string $property, $operator, DateTime $value = null, string $before = 'and') : self

    • Where property is a datetime (Y-m-d\TH:i:s.v\Z)
  • whereContains(string $property, $value, string $before = 'and') : self

    • Where property contains the value - Same as SQL ´column´ like '%value%'
  • whereStartsWith(string $property, $value, string $before = 'and') : self

    • Where property starts with the value - Same as SQL ´column´ like 'value%'
  • whereEndsWith(string $property, $value, string $before = 'and') : self

    • Where property ends with the value - Same as SQL ´column´ like '%value'
  • whereGroup(Closure $callback, string $before = 'and') : self

    • Grouped where clause - Example:
      whereGroup(function(Builder $query) { $query->where('property', 'Foo')->orWhere('property', 'Bar'))
    • This functionality can be shorthanded as where(function(Builder $query) { ... })

Operators:

Logical OData equiv
= eq
!= ne
> gt
>= ge
< lt
<= le
Basic Usage
Builder Advanced
  • clone() : self

    • Clone the current Builder instance with extentions (filters, expands, sorting etc.)
  • cloneWithoutExtentions() : self

    • Clone the current Builder instance without extentions (filters, expands, sorting etc.)

Extending Entity Models

The SDK has a range of pre-generated Models it uses to contain and assist the user while using the SDK.
You can replace the class used as the container if you want to - Only requirement is that the model must extend \BusinessCentral\Entity.

Example:

\BusinessCentral\ClassMap::extend('nativeInvoicingSalesCustomers', MyTotallyNewAwesomeCustomerModelReplacementOfAbsoluteDoom::class);

This overrides the model class used for all entities of type customer in the entire application.

Debugging

The SDK logs all requests to and from your application to Business Central for debugging and monitoring purposes.

You can get all log entries from the SDK at any time from $sdk->request_log, which returns an array of RequestLog objects.

RequestLog Properties

Name Type
method string
code int
uri string
time float
options array
response mixed

Contribution

This SDK is not a finished product.
Input, additions and changes are very much encouraged - Fork the repo, make the changes/additions/fixes, and create a pull request.

What's needed?

Schema Overrides

A lot of entities on Business Central has read-only fields which are disguised as actual properties but are virtual (like currencyCode on customers is the value of the customer's currency's code property and cannot be changed on the customer itself).

These properties needs to be found and flagged.
Take a look in schema_overrides.json and follow the syntax for flagging a property as read-only.

business-central-php's People

Contributors

dependabot[bot] avatar exeque avatar fichtme avatar pazion avatar vassard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

business-central-php's Issues

api call upon initiating

I noticed that when instantiating a Model/Entity it does a call to the API , this is triggered from
EntityCollection::__construct.

When setting the limit high, this fetches a lot of data.

I had a look, but the propagate is really dependent in the construct functionality.

I also thought about limiting the construct to 1, but believe this has consequences for the pagination?

Question

Hey, first of all thanks for the great work done here!

I have a quick question, does it also work with On-premise deployments?

create

What is the structure of code to create an entry ? have you got a example ?

deep insert

When creating a SalesDocument I wanted to directly do a deep insert ( add SalesDocumentLines in the same call ), but noticed the library doesn't yet support this, are there any plans for this?

Call to a member function getEntityType() on null in vendor\coolrunner\business-central-sdk\src\EntityCollection.php:59

Hi,
I'm getting the error Call to a member function getEntityType() on null when I try to do any query.
It seems the problem starts in `EntityCollection.ph , line 77:

$this->type        = $this->query->getEntitySet($response['@odata.context']);

$response['@odata.context'] has a valid content:
https://api.businesscentral.dynamics.com/v2.0/myTenant/sandbox/ODataV4/$metadata#Company

$response looks good as well (array of value is there; 1 element in it, with Name, Display_Name, Id, ...

But $this->query->getEntitySet returns null

Complete usage of SDK:

$sdk = \BusinessCentral\SDK::instance($myTenant, [
    // OAuth2 [Required]
    'client_id' => $myClientId,

    // Basic auth token [Required]
    'client_secret'    => $mySecret,

    // Default collection size [Optional]
    // Amount of entities to load initially and per page per collection
    // Can be changed on the fly on any collection instance
    'default_collection_size' => 20,

    // Default environment [Optional - 'production' by default]
    'environment' => 'sandbox',
]);

$company = $sdk->company('company');

At creating the instance some warnings will come - maybe this is the reason?

Warning: Undefined array key "@attributes" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Undefined array key "@attributes" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Undefined array key "Namespace" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Undefined array key "@attributes" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Undefined array key "Namespace" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Undefined array key "@attributes" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 40

Warning: Undefined array key "NAV.ComplexTypes" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 55

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 55

Warning: foreach() argument must be of type array|object, null given in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 55

Warning: Undefined array key "NAV" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 59

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 59

Warning: foreach() argument must be of type array|object, null given in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 59

Warning: Undefined array key "NAV" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 63

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 63

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 63

Warning: foreach() argument must be of type array|object, null given in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 63

Warning: Undefined array key "NAV" in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 67

Warning: Trying to access array offset on value of type null in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 67

Warning: foreach() argument must be of type array|object, null given in \vendor\coolrunner\business-central-sdk\src\Schema.php on line 67

PHP Version: 8.1.6

Guzzle ^7 support

Hi Morten.

Thanks for your work!!

Is it possible to update the guzzle requirements, so it is possible to require it with Laravel 8?

Thanks!

Error: An item with the same key has already been added. when requesting the ODataV4/$metadata

Hi

I'm sorry for asking this here as I'm sure this is a problem with my Business Central set-up and not with this project. I don't know much about Business Central but I've followed some instructions on the web and got Azure AD OAuth configured and working with a trial Business Central set-up in the cloud. I've done some searching but I've not been able to find a solution to my problem.

The SDK appeared to work fine for the fist request but subsequent requests have generated this error:

Server error: `GET https://api.businesscentral.dynamics.com/v2.0/afcd4d86-327b-4160-baad-c45589c7eddb/production/ODataV4/$metadata` resulted in a `500 Internal Server Error` response:
{"error":{"code":"Unknown","message":"An item with the same key has already been added.  CorrelationId:  40da9e46-37e9-4 (truncated...)

Using the same request in Postman generates the same result.

{
    "error": {
        "code": "Unknown",
        "message": "An item with the same key has already been added.  CorrelationId:  d04c747b-44b2-41cc-82b2-57db29f88414."
    }
}

Do you know what I need to do in Business Central to fix this?

On premise issue building models

Hi.

I finally got to testing the SDK on on-premise test environment.
I was able to get connection working after modifying SDK.php file line 80 from
'base_uri' => "https://api.businesscentral.dynamics.com/v2.0/$this->tenant/$this->environment/ODataV4/",
to
'base_uri' => "$this->tenant/ODataV4/",
and passing on-prem service url while starting instance and removing cloud prefix alltogether.

But I'm having issues building models and populating entities.md and was hoping you could help me out a bit.

First I tried post-autoload-dump script. I failed passing credentials from composer.json. Composer simply did not validate json. Then I tried defining default variable values at Constructor.php buildModels function. Then the script ran but ended up with error:
Script BusinessCentral\Constructor::buildModels handling the post-autoload-dump event terminated with an exception [ErrorException] Object of class Composer\Script\Event could not be converted to string

Finnaly tried building models directly from php script by calling
BusinessCentral\Constructor::buildModels( 'server:port/instance', 'username', 'password' );
And got buntch of undefined index errors and failing:
Undefined index: NAV in C:\wamp64\www\ekodumaja\vendor\coolrunner\business-central-sdk\src\Schema.php on line 59 Undefined index: @attributes in C:\wamp64\www\ekodumaja\vendor\coolrunner\business-central-sdk\src\Schema.php on line 40 Undefined index: Namespace in C:\wamp64\www\ekodumaja\vendor\coolrunner\business-central-sdk\src\Schema.php on line 40 Undefined index: NAV.ComplexTypes in C:\wamp64\www\ekodumaja\vendor\coolrunner\business-central-sdk\src\Schema.php on line 55 Invalid argument supplied for foreach() in C:\wamp64\www\ekodumaja\vendor\coolrunner\business-central-sdk\src\Schema.php on line 55

Am I missing something obvious here?
Maybe you can provide me sample of your working composer.json in case i'm not including something important there.

If I get some time I will make a fork and make pull request regarding on-premise tenant URL fix.

Undefined array key "@attributes"

Hi.
Im trying to connect to my BC Environment and im getting an ErrorException
Undefined array key "@attributes" on line 'client_id' => .....

` public function test(){

    $sdk = SDK::instance('######', [

        // OAuth2 [Required]

        'client_id' => '######',



        // Basic auth token [Required]

        'client_secret'    => '######',



        // Default collection size [Optional]

        // Amount of entities to load initially and per page per collection

        // Can be changed on the fly on any collection instance

        'default_collection_size' => 20,



        // Default environment [Optional - 'production' by default]

        'environment' => 'TESTES'

    ]);

}`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.