Everything you need to know about using the Tectalic Apacta PHP API Client.
The Tectalic Apacta REST API Client is a package that provides a convenient and straightforward way to interact with the Apacta API from your PHP application.
You can purchase this package from https://tectalic.com/apis/apacta.
To install this package into your PHP project, we recommend using Composer.
Please see the detailed instructions on configuring your project to access the Tectalic Composer repository.
You will need to log into the Tectalic account that purchased the Tectalic Apacta REST API Client package to access these instructions.
Once you have followed the above instructions, install the package into your project:
composer require tectalic/apacta
If you aren’t using Composer in your PHP project, you can choose to Download the latest release and install it into your PHP project manually.
If doing this, you will need to ensure that all dependencies listed in the package’s composer.json
file are also installed.
After installing the Tectalic Apacta REST API Client package into your project, ensure you also have a compatible PSR-18 HTTP client such as ‘Guzzle’ or the Symfony ‘HTTP Client’.
You can use the following code sample and customize it to suit your application.
// Load your project's composer autoloader (if you aren't already doing so).require_once(__DIR__ . '/vendor/autoload.php');
use Symfony\Component\HttpClient\Psr18Client;use Tectalic\Apacta\Authentication;use Tectalic\Apacta\Client;use Tectalic\Apacta\Manager; // Build a Tectalic Apacta REST API Client globally.$auth = new Authentication('key');$httpClient = new Psr18Client();Manager::build($httpClient, $auth); // or // Build a Tectalic Apacta REST API Client manually.$auth = new Authentication('key');$httpClient = new Psr18Client();$client = new Client($httpClient, $auth, Manager::BASE_URI);
To authenticate your API requests, you will need to provide an Authentication
($auth
) object when calling Manager::build()
.
Authentication to the Apacta API is by API Key Header authentication.
Please see the Apacta API documentation for more details on obtaining your authentication credentials.
In the Usage code above, customize the Authentication
constructor to your needs. For example, you may wish to define your credentials in an environment variable and pass it to the constructor.
The primary class you will interact with is the Client
class (Tectalic\Apacta\Client
).
This Client
class also contains the helper methods that let you quickly access the 43 API Handlers.
Please see below for a complete list of supported handlers and methods.
This package supports 138 API Methods, which are grouped into 43 API Handlers.
See the table below for a full list of API Handlers and Methods.
API Handler Class and Method Name | Description | API Verb and URL |
---|---|---|
Cities::get() |
Get list of cities supported in Apacta | GET /cities |
Cities::cityIdGet() |
Get details about one city | GET /cities/{city_id} |
ClockingRecords::get() |
Get a list of clocking records | GET /clocking_records |
ClockingRecords::post() |
Create clocking record for authenticated user | POST /clocking_records |
ClockingRecords::clockingRecordIdGet() |
Details of 1 clocking_record | GET /clocking_records/{clocking_record_id} |
ClockingRecords::clockingRecordIdPut() |
Edit a clocking record | PUT /clocking_records/{clocking_record_id} |
ClockingRecords::clockingRecordIdDelete() |
Delete a clocking record | DELETE /clocking_records/{clocking_record_id} |
ClockingRecordsCheckout::post() |
Checkout active clocking record for authenticated user | POST /clocking_records/checkout |
Companies::get() |
Get a list of companies | GET /companies |
Companies::companyIdGet() |
Details of 1 company | GET /companies/{company_id} |
CompaniesIntegrationFeatureSettings::companyIdIntegrationFeatureSettingsGet() |
Get a list of integration feature settings | GET /companies/{company_id}/integration_feature_settings |
CompaniesIntegrationFeatureSettings::companyIdIntegrationFeatureSettingsIntegrationFeatureSettingIdGet() |
Show details of 1 integration feature setting | GET /companies/{company_id}/integration_feature_settings/{integration_feature_setting_id} |
ContactTypes::get() |
Get list of contact types supported in Apacta | GET /contact_types |
ContactTypes::contactTypeIdGet() |
Get details about one contact type | GET /contact_types/{contact_type_id} |
Contacts::get() |
Get a list of contacts | GET /contacts |
Contacts::post() |
Add a new contact | POST /contacts |
Contacts::contactIdGet() |
Details of 1 contact | GET /contacts/{contact_id} |
Contacts::contactIdPut() |
Edit a contact | PUT /contacts/{contact_id} |
Contacts::contactIdDelete() |
Delete a contact | DELETE /contacts/{contact_id} |
Currencies::get() |
Get list of currencies supported in Apacta | GET /currencies |
Currencies::currencyIdGet() |
Get details about one currency | GET /currencies/{currency_id} |
EmployeeHours::get() |
Used to retrieve details about the logged in user’s hours | GET /employee_hours |
ExpenseFiles::get() |
Show list of expense files | GET /expense_files |
ExpenseFiles::post() |
Add file to expense | POST /expense_files |
ExpenseFiles::expenseFileIdGet() |
Show file | GET /expense_files/{expense_file_id} |
ExpenseFiles::expenseFileIdPut() |
Edit file | PUT /expense_files/{expense_file_id} |
ExpenseFiles::expenseFileIdDelete() |
Delete file | DELETE /expense_files/{expense_file_id} |
ExpenseLines::get() |
Show list of expense lines | GET /expense_lines |
ExpenseLines::post() |
Add line to expense | POST /expense_lines |
ExpenseLines::expenseLineIdGet() |
Show expense line | GET /expense_lines/{expense_line_id} |
ExpenseLines::expenseLineIdPut() |
Edit expense line | PUT /expense_lines/{expense_line_id} |
ExpenseLines::expenseLineIdDelete() |
Delete expense line | DELETE /expense_lines/{expense_line_id} |
Expenses::get() |
Show list of expenses | GET /expenses |
Expenses::post() |
Add line to expense | POST /expenses |
Expenses::expenseIdGet() |
Show expense | GET /expenses/{expense_id} |
Expenses::expenseIdPut() |
Edit expense | PUT /expenses/{expense_id} |
Expenses::expenseIdDelete() |
Delete expense | DELETE /expenses/{expense_id} |
ExpensesOriginalFiles::expenseIdOriginalFilesGet() |
Show list of all OIOUBL files for the expense | GET /expenses/{expense_id}/original_files |
ExpensesOriginalFiles::expenseIdOriginalFilesFileIdGet() |
Show OIOUBL file | GET /expenses/{expense_id}/original_files/{file_id} |
FormFieldTypes::get() |
Get list of form field types | GET /form_field_types |
FormFieldTypes::formFieldTypeIdGet() |
Get details about single FormField |
GET /form_field_types/{form_field_type_id} |
FormFields::post() |
Add a new field to a Form |
POST /form_fields |
FormFields::formFieldIdGet() |
Get details about single FormField |
GET /form_fields/{form_field_id} |
FormTemplates::get() |
Get array of form_templates for your company | GET /form_templates |
FormTemplates::formTemplateIdGet() |
View one form template | GET /form_templates/{form_template_id} |
Forms::get() |
Retrieve array of forms | GET /forms |
Forms::post() |
Add new form | POST /forms |
Forms::formIdGet() |
View form | GET /forms/{form_id} |
Forms::formIdPut() |
Edit a form | PUT /forms/{form_id} |
Forms::formIdDelete() |
Delete a form | DELETE /forms/{form_id} |
InvoiceLines::get() |
View list of invoice lines | GET /invoice_lines |
InvoiceLines::post() |
Add invoice | POST /invoice_lines |
InvoiceLines::invoiceLineIdGet() |
View invoice line | GET /invoice_lines/{invoice_line_id} |
InvoiceLines::invoiceLineIdPut() |
Edit invoice line | PUT /invoice_lines/{invoice_line_id} |
InvoiceLines::invoiceLineIdDelete() |
Delete invoice line | DELETE /invoice_lines/{invoice_line_id} |
Invoices::get() |
View list of invoices | GET /invoices |
Invoices::post() |
Add invoice | POST /invoices |
Invoices::invoiceIdGet() |
View invoice | GET /invoices/{invoice_id} |
Invoices::invoiceIdPut() |
Edit invoice | PUT /invoices/{invoice_id} |
Invoices::invoiceIdDelete() |
Delete invoice | DELETE /invoices/{invoice_id} |
MassMessagesUsers::get() |
View list of mass messages for specific user | GET /mass_messages_users |
MassMessagesUsers::massMessagesUserIdGet() |
View mass message | GET /mass_messages_users/{mass_messages_user_id} |
MassMessagesUsers::massMessagesUserIdPut() |
Edit mass message | PUT /mass_messages_users/{mass_messages_user_id} |
Materials::get() |
View list of all materials | GET /materials |
Materials::materialIdGet() |
View material | GET /materials/{material_id} |
Materials::materialIdPut() |
Edit material | PUT /materials/{material_id} |
Materials::materialIdDelete() |
Delete material | DELETE /materials/{material_id} |
MaterialsRentals::materialIdRentalsGet() |
Show list of rentals for specific material | GET /materials/{material_id}/rentals/ |
MaterialsRentals::materialIdRentalsPost() |
Add material rental | POST /materials/{material_id}/rentals/ |
MaterialsRentals::materialIdRentalsMaterialRentalIdGet() |
Show rental foor materi | GET /materials/{material_id}/rentals/{material_rental_id}/ |
MaterialsRentals::materialIdRentalsMaterialRentalIdPut() |
Edit material rental | PUT /materials/{material_id}/rentals/{material_rental_id}/ |
MaterialsRentals::materialIdRentalsMaterialRentalIdPost() |
Add material | POST /materials/{material_id}/rentals/{material_rental_id}/ |
MaterialsRentals::materialIdRentalsMaterialRentalIdDelete() |
Delete material rental | DELETE /materials/{material_id}/rentals/{material_rental_id}/ |
MaterialsRentalsCheckout::materialIdRentalsCheckoutPost() |
Checkout material rental | POST /materials/{material_id}/rentals/checkout/ |
PaymentTermTypes::get() |
Get a list of payment term types | GET /payment_term_types |
PaymentTermTypes::paymentTermTypeIdGet() |
Details of 1 payment term type | GET /payment_term_types/{payment_term_type_id} |
PaymentTerms::get() |
Get a list of payment terms | GET /payment_terms |
PaymentTerms::paymentTermIdGet() |
Details of 1 payment term | GET /payment_terms/{payment_term_id} |
Ping::get() |
Check if API is up and API key works | GET /ping |
Products::get() |
List products | GET /products |
Products::post() |
Add new product | POST /products |
Products::productIdGet() |
View single product | GET /products/{product_id} |
Products::productIdPut() |
Edit a product | PUT /products/{product_id} |
Products::productIdDelete() |
Delete a product | DELETE /products/{product_id} |
ProjectStatuses::get() |
Get list of project statuses | GET /project_statuses |
ProjectStatuses::projectStatusIdGet() |
Get details about one contact type | GET /project_statuses/{project_status_id} |
Projects::get() |
View list of projects | GET /projects |
Projects::post() |
Add a project | POST /projects |
Projects::projectIdGet() |
View specific project | GET /projects/{project_id} |
Projects::projectIdPut() |
Edit a project | PUT /projects/{project_id} |
Projects::projectIdDelete() |
Delete a project | DELETE /projects/{project_id} |
ProjectsFiles::projectIdFilesGet() |
Show list of files uploaded to project | GET /projects/{project_id}/files |
ProjectsFiles::projectIdFilesFileIdGet() |
Show file | GET /projects/{project_id}/files/{file_id}/ |
ProjectsFiles::projectIdFilesFileIdPut() |
Edit file | PUT /projects/{project_id}/files/{file_id}/ |
ProjectsFiles::projectIdFilesFileIdDelete() |
Delete file | DELETE /projects/{project_id}/files/{file_id}/ |
ProjectsProjectFiles::projectIdProjectFilesGet() |
Show list of project files uploaded to project | GET /projects/{project_id}/project_files |
ProjectsProjectFiles::projectIdProjectFilesPost() |
Add project file to projects | POST /projects/{project_id}/project_files |
ProjectsProjectFiles::projectIdProjectFilesProjectFileIdGet() |
Show project file | GET /projects/{project_id}/project_files/{project_file_id}/ |
ProjectsProjectFiles::projectIdProjectFilesProjectFileIdPut() |
Edit project file | PUT /projects/{project_id}/project_files/{project_file_id}/ |
ProjectsProjectFiles::projectIdProjectFilesProjectFileIdDelete() |
Delete project file | DELETE /projects/{project_id}/project_files/{project_file_id}/ |
ProjectsUsers::projectIdUsersGet() |
Show list of users added to project | GET /projects/{project_id}/users/ |
ProjectsUsers::projectIdUsersPost() |
Add user to project | POST /projects/{project_id}/users/ |
ProjectsUsers::projectIdUsersUserIdGet() |
View specific user assigned to project | GET /projects/{project_id}/users/{user_id} |
ProjectsUsers::projectIdUsersUserIdDelete() |
Delete user from project | DELETE /projects/{project_id}/users/{user_id} |
StockLocations::get() |
List stock_locations | GET /stock_locations |
StockLocations::post() |
Add new stock_locations | POST /stock_locations |
StockLocations::locationIdGet() |
View single location | GET /stock_locations/{location_id} |
StockLocations::locationIdPut() |
Edit location | PUT /stock_locations/{location_id} |
StockLocations::locationIdDelete() |
Delete location | DELETE /stock_locations/{location_id} |
TimeEntries::get() |
List time entries | GET /time_entries |
TimeEntries::post() |
Add new time entry | POST /time_entries |
TimeEntries::timeEntryIdGet() |
View time entry | GET /time_entries/{time_entry_id} |
TimeEntries::timeEntryIdPut() |
Edit time entry | PUT /time_entries/{time_entry_id} |
TimeEntries::timeEntryIdDelete() |
Delete time entry | DELETE /time_entries/{time_entry_id} |
TimeEntryIntervals::get() |
List possible time entry intervals | GET /time_entry_intervals |
TimeEntryIntervals::timeEntryIntervalIdGet() |
View time entry interval | GET /time_entry_intervals/{time_entry_interval_id} |
TimeEntryTypes::get() |
List time entries types | GET /time_entry_types |
TimeEntryTypes::post() |
Add new time entry type | POST /time_entry_types |
TimeEntryTypes::timeEntryTypeIdGet() |
View time entry type | GET /time_entry_types/{time_entry_type_id} |
TimeEntryTypes::timeEntryTypeIdPut() |
Edit time entry type | PUT /time_entry_types/{time_entry_type_id} |
TimeEntryTypes::timeEntryTypeIdDelete() |
Delete time entry type | DELETE /time_entry_types/{time_entry_type_id} |
TimeEntryUnitTypes::get() |
List possible time entry unit types | GET /time_entry_unit_types |
TimeEntryUnitTypes::timeEntryUnitTypeIdGet() |
View time entry unit type | GET /time_entry_unit_types/{time_entry_unit_type_id} |
TimeEntryValueTypes::get() |
List possible time entry value types | GET /time_entry_value_types |
TimeEntryValueTypes::timeEntryValueTypeIdGet() |
View time entry value type | GET /time_entry_value_types/{time_entry_value_type_id} |
Users::get() |
Get list of users in company | GET /users |
Users::post() |
Add user to company | POST /users |
Users::userIdGet() |
View user | GET /users/{user_id} |
Users::userIdPut() |
Edit user | PUT /users/{user_id} |
Users::userIdDelete() |
Delete user | DELETE /users/{user_id} |
VendorProducts::get() |
List vendor products | GET /vendor_products |
VendorProducts::vendorProductIdGet() |
View single vendor product | GET /vendor_products/{vendor_product_id} |
WallComments::post() |
Add wall comment | POST /wall_comments |
WallComments::wallCommentIdGet() |
View wall comment | GET /wall_comments/{wall_comment_id} |
WallPosts::get() |
View list of wall posts | GET /wall_posts |
WallPosts::post() |
Add a wall post | POST /wall_posts |
WallPosts::wallPostIdGet() |
View wall post | GET /wall_posts/{wall_post_id} |
WallPostsWallComments::wallPostIdWallCommentsGet() |
See wall comments to a wall post | GET /wall_posts/{wall_post_id}/wall_comments |
There are two ways to make a request to the nominated API Handler and API Method:
If you built the client to be accessible globally, you can use the relevant API Handler Class directly:
use Tectalic\Apacta\Handlers\Cities; (new Cities())->get();
Alternatively, you can access all API Handlers from the client class using the Client class:
$client->cities()->get();
Once you have made a request using one of the two methods outlined above, the next step is to access the response.
You can access the response in different ways. Please choose your preferred one.
Model responses are Data Transfer Object (DTO) style PHP classes, with public properties for each API property.
They offer a structured way of retrieving the response from an API request.
All Response Models are an instance of \Spatie\DataTransferObject\DataTransferObject
or \Spatie\DataTransferObject\DataTransferObjectCollection
.
After performing the request, use the ->toModel()
fluent method to the API Method:
use Tectalic\Apacta\Handlers\Cities; $model = (new Cities())->get()->toModel();
Each API Method’s toModel()
call will return the appropriate Model class type for the API Method you have just called.
After performing the request, use the ->toArray()
fluent method to the API Method:
use Tectalic\Apacta\Handlers\Cities; $array = (new Cities())->get()->toArray();
In the resulting associative array, the array keys will match the names of the public properties in the relevant Model class.
If you need to access the raw response or inspect the HTTP headers, use the ->getResponse()
fluent method to the API Method. It will return a Psr\Http\Message\ResponseInterface
:
use Tectalic\Apacta\Handlers\Cities; $response = (new Cities())->get()->getResponse();
When performing requests with Tectalic Apacta REST API Client, specific scenarios will cause a Tectalic\Apacta\Exception\ClientException
to be thrown. Please see below for details.
Manager
ClassA \LogicException
will be thrown if the Manager::build()
function is called multiple times, or if Manager::access()
is called before calling Manager::build()
.
The Tectalic Apacta REST API Client depends on a PSR-18 compatible HTTP client, and that HTTP client should not throw an exception for unsuccessful HTTP response codes.
An unsuccessful response code is classified as one that is not in the range 200
-299
(inclusive). Examples of unsuccessful response codes include:
100
-199
)300
-399
)400
-499
)500
-599
)If an unsuccessful response code does occur:
toModel()
method will throw a ClientException
.toArray()
method will return the response body and not throw a ClientException
.getResponse()
method will return the raw response and not throw a ClientException
.Below is an example of how you may wish to use a try
/catch
block when performing a request so that you can detect and handle unexpected errors.
use Tectalic\Apacta\Authentication;use Tectalic\Apacta\Client;use Tectalic\Apacta\ClientException;use Tectalic\Apacta\Manager; // Build a Tectalic Apacta REST API Client globally.$auth = new Authentication('key');Manager::build($httpClient, $auth);$handler = new Cities(); // Perform a requesttry { $model = $handler->get()->toModel(); // Do something with the response model...} catch (ClientException $e) { // Error response received. Retrieve the HTTP response code and response body. $responseBody = $handler->toArray(); $rawResponse = $handler->getResponse()->getResponse(); $responseCode = $handler->getResponse()->getStatusCode(); // Handle the error...}
If your HTTP client of choice throws an exception other than ClientException
, the Tectalic Apacta REST API Client Client
and its API Handler classes will let these exceptions bubble up.
Consult your HTTP client’s documentation for more details on exception handling.
The Tectalic Apacta REST API Client package includes several types of automated PHPUnit tests to verify the correct operation:
To run these tests, you will need to have installed the Tectalic Apacta REST API Client package with its dev dependencies (i.e. not using the --no-dev
flag when running composer).
These PHPUnit tests are designed to:
Client
and Manager
classes.The unit tests can be run using the following command, which needs to be run from this package’s root directory.
composer test:unit
Unit tests do not perform any real requests against the Apacta API.
Unit tests are located in the tests/Unit
directory.
Integration tests are located in the tests/Integration
directory.
These PHPUnit tests are designed to confirm that each API Method parses a valid response, according to the Apacta API OpenAPI specification. Out of the box the integration tests are designed to work with the Prism Mock Server.
Make sure Prism is installed. Please see the Prism documentation for details on how to install Prism.
Once Prism is installed, you can run prism and the integration tests side by side in separate terminal windows, or using the following command, which need to be run from this package’s root directory.
echo "> Starting Prism server"prism mock tests/openapi.json >/dev/null 2>&1 &PRISM_PID=$!sleep 2echo " => Started"composer test:integrationkill $PRISM_PID
Those commands will start the Prism mock server, then run the integration tests, and then stop the Prism mock server when the tests are completed.
In this case the integration tests do not perform any real requests against the Apacta API.
By setting the APACTA_CLIENT_TEST_BASE_URI
environment variable, you can set a different API endpoint target for the integration tests.
For example, instead of using Prism, you can use a different mocking/staging/test server of your choice, or you can use the Apacta API’s live endpoints.
Do not forget to set the appropriate credentials in the APACTA_CLIENT_TEST_AUTH_KEY
environment variable.
After your setup is complete simply run the following command.
composer test:integration
We do not recommend running integration tests against the live Apacta API endpoints. This is because the tests will send example data to all endpoints, which can result in new data being created, or existing data being deleted.
If you have any questions or feedback, you can submit a support request to the Tectalic developers by going to https://tectalic.com/support/apacta.
This software is copyright (c) Tectalic.
For the full copyright and license information, please view the ‘LICENSE’ file distributed with the source code.
Last updated 27 Oct 2022