Odoo API client using XML-RPC Odoo ORM External API.
Main features
- Authentication
- ORM methods
- Expression builder added in v5.0
If you are in Symfony application, I suggest you to install the bundle ang3/odoo-api-bundle. It provides a registry you can configure easily and deploys clients as services.
Coming:
The ORM (Object relational mapper) is in development: ang3/php-odoo-orm (need tests). Of course if you are in Symfony application you should be interested in the bundle: ang3/odoo-bundle (ORM integration).
- The PHP extension
php-xmlrpcmust be enabled.
| Odoo server | Compatibility | Comment |
|---|---|---|
| v13.0 | Yes | Some Odoo model names changed (e.g account.invoice > account.move) |
| v12.0 | Yes | First tested version |
| v11.0 | Unknown | |
| v10.0 | Unknown | |
| < v10 | Unknown |
Open a command console, enter your project directory and execute the following command to download the latest stable version of the client:
$ composer require ang3/php-odoo-api-clientThis command requires you to have Composer installed globally, as explained in the installation chapter of the Composer documentation.
- Usage
- Built-in ORM methods
- Write records - Create and update records
- Search records - Search and read records
- Delete records
- Expression builder - Oodo array expressions
- Get the expression builder - Create or get a builder
- Domains - Build Odoo domain expressions
- Collection operations - Build Odoo collection field operations
- Upgrades - Major changes
First, you have to create a client instance:
<?php
require_once 'vendor/autoload.php';
use Ang3\Component\Odoo\Client;
// Option 1: by calling the constructor...
$client = new Client('<host>', '<database>', '<username>', '<password>', $logger = null);
// Option 2 : by calling the static method ::createFromConfig() with configuration as array
$client = Client::createFromConfig([
'host' => '<host>',
'database' => '<database>',
'user' => '<user>',
'password' => '<password>',
], $logger = null);Exceptions:
Ang3\Component\Odoo\Exception\MissingConfigParameterExceptionwhen a required parameter is missing from the static methodcreateFromConfig().
Then, make your call:
$result = $client->call($name, $method, $parameters = [], $options = []);Exceptions:
Ang3\Component\Odoo\Exception\AuthenticationExceptionwhen authentication failed.Ang3\Component\Odoo\Exception\RequestExceptionwhen request failed.
These previous exception can be thrown by all methods of the client.
For all these methods, the parameter $data can contains collection field operations.
Please see the section Expression builder to manage collection fields easily.
Create a record
$data = [
'field_name' => 'value'
];
$recordId = $client->create('model_name', $data);The method returns the ID of the created record.
Update a record
$ids = [1,2,3]; // Can be a value of type int|array<int>
$data = [
'field_name' => 'value'
];
$client->update('model_name', $ids, $data); // voidThe method returns void.
For all these methods, the parameter $criteria must be an array. I suggest you to create tour criteria
with the Expression builder.
Read records
Get a list of records by ID.
$ids = [1,2,3]; // Can be a value of type int|array<int>
$records = $client->read('model_name', $ids);The method returns an array of records of type array<array>.
Find a record by ID
$id = 1; // Must be an integer
$record = $client->find('model_name', $id, $options = []);The method returns the record as array, or NULL is the record was not found.
For each method below, the value of the parameter $criteria can be NULL,
an array or a domain expression.
Please see the section Expression builder to build domain expressions.
Find ONE record by criteria and options
$record = $client->findOneBy('model_name', $criteria = null, $options = []);The method returns the record as array, or NULL is the record was not found.
Find records by criteria and options
$records = $client->findBy('model_name', $criteria = null, $options = []);The method returns an array of records of type array<array>.
Search ONE record ID by criteria and options
$recordIds = $client->searchOne('model_name', $criteria = null, $options = []);Search all record IDs by options
$recordIds = $client->searchAll('model_name', $options = []);Search record(s)
Get a list of ID for matched record(s).
$recordIds = $client->search('model_name', $criteria = null, $options = []);The method returns a list of ID of type array<int>.
Check if a record exists by ID
$id = 1; // Must be an integer
$recordExists = $client->exists('model_name', $id);Count records by criteria
$nbRecords = $client->count('model_name', $criteria = null);The method returns an integer.
You can delete many records at one time.
$ids = [1,2,3]; // Can be a value of type int|array<int>
$client->delete('model_name', $ids);The method returns void.
There are two kinds of expressions : domains for criteria
and collection operations in data writing.
Odoo has its own array format for those expressions.
The aim of the expression builder is to provide some
helper methods to simplify a programmer's life.
Here is an example of how to build a ExpressionBuilder object from a Client instance:
$expr = $client->getExpressionBuilder();
// Or $expr = $client->expr();You can still use the expression builder as standalone by creating an instance yourself.
use Ang3\Component\Odoo\Expression\ExpressionBuilder;
$expr = new ExpressionBuilder();For all search queries (search, findBy, findOneBy and count),
Odoo is waiting for an array of domains
with a polish notation for logical operations (AND, OR and NOT).
It could be quickly ugly to do a complex domain, but don't worry the builder makes all for you. :)
Each domain builder method creates an instance of Ang3\Component\Odoo\Expression\DomainInterface.
The only one method of this interface is toArray() in order to get a normalized array of the expression.
To illustrate how to work with it, here is an example using ExpressionBuilder helper methods:
// $client instanceof Client
// Get the expression builder
$expr = $client->expr();
$result = $client->findBy('model_name', $expr->andX( // Logical node "AND"
$expr->gte('id', 10), // id >= 10
$expr->lte('id', 100), // id <= 10
), $options = []);Of course, you can nest logical nodes:
$result = $client->findBy('model_name', $expr->andX(
$expr->orX(
$expr->eq('A', 1),
$expr->eq('B', 1)
),
$expr->orX(
$expr->eq('C', 1),
$expr->eq('D', 1),
$expr->eq('E', 1)
)
), $options = []);The client formats automatically all domains by calling the special builder
method normalizeDomains() internally.
Here is a complete list of helper methods available in ExpressionBuilder for domain expressions:
/**
* Create a logical operation "AND".
*/
public function andX(DomainInterface ...$domains): CompositeDomain;
/**
* Create a logical operation "OR".
*/
public function orX(DomainInterface ...$domains): CompositeDomain;
/**
* Create a logical operation "NOT".
*/
public function notX(DomainInterface ...$domains): CompositeDomain;
/**
* Check if the field is EQUAL TO the value.
*
* @param mixed $value
*/
public function eq(string $fieldName, $value): Comparison;
/**
* Check if the field is NOT EQUAL TO the value.
*
* @param mixed $value
*/
public function neq(string $fieldName, $value): Comparison;
/**
* Check if the field is UNSET OR EQUAL TO the value.
*
* @param mixed $value
*/
public function ueq(string $fieldName, $value): Comparison;
/**
* Check if the field is LESS THAN the value.
*
* @param mixed $value
*/
public function lt(string $fieldName, $value): Comparison;
/**
* Check if the field is LESS THAN OR EQUAL the value.
*
* @param mixed $value
*/
public function lte(string $fieldName, $value): Comparison;
/**
* Check if the field is GREATER THAN the value.
*
* @param mixed $value
*/
public function gt(string $fieldName, $value): Comparison;
/**
* Check if the field is GREATER THAN OR EQUAL the value.
*
* @param mixed $value
*/
public function gte(string $fieldName, $value): Comparison;
/**
* Check if the variable is LIKE the value.
*
* An underscore _ in the pattern stands for (matches) any single character
* A percent sign % matches any string of zero or more characters.
*
* If $strict is set to FALSE, the value pattern is "%value%" (automatically wrapped into signs %).
*
* @param mixed $value
*/
public function like(string $fieldName, $value, bool $strict = false, bool $caseSensitive = true): Comparison;
/**
* Check if the field is IS NOT LIKE the value.
*
* @param mixed $value
*/
public function notLike(string $fieldName, $value, bool $caseSensitive = true): Comparison;
/**
* Check if the field is IN values list.
*/
public function in(string $fieldName, array $values = []): Comparison;
/**
* Check if the field is NOT IN values list.
*/
public function notIn(string $fieldName, array $values = []): Comparison;In data writing context, Odoo allows you to manage *toMany collection fields with special commands. Please read the ORM documentation to known what we are talking about.
The expression builder provides helper methods to build a well-formed operation command: each operation method returns the operation as array.
To illustrate how to work with operations, here is an example using ExpressionBuilder helper methods:
// $client instanceof Client
// Get the expression builder
$expr = $client->expr();
// Prepare new record data
$data = [
'foo' => 'bar',
'bar_ids' => [ // Field of type "manytoMany"
$expr->addRecord(3), // Add the record of ID 3 to the set
$expr->createRecord([ // Create a new sub record and add it to the set
'bar' => 'baz'
// ...
])
]
];
$result = $client->create('model_name', $data);The client formats automatically the whole query parameters for all writing methods
(create and update) by calling the special builder
method normalizeData() internally.
Here is a complete list of helper methods available in ExpressionBuilder for operation expressions:
/**
* Adds a new record created from data.
*/
public function createRecord(array $data): array;
/**
* Updates an existing record of id $id with data.
* /!\ Can not be used in record insert query.
*/
public function updateRecord(int $id, array $data): array;
/**
* Adds an existing record of id $id to the collection.
*/
public function addRecord(int $id): array;
/**
* Removes the record of id $id from the collection, but does not delete it.
* /!\ Can not be used in record insert query.
*/
public function removeRecord(int $id): array;
/**
* Removes the record of id $id from the collection, then deletes it from the database.
* /!\ Can not be used in record insert query.
*/
public function deleteRecord(int $id): array;
/**
* Replaces all existing records in the collection by the $ids list,
* Equivalent to using the command "clear" followed by a command "add" for each id in $ids.
*/
public function replaceRecords(array $ids = []): array;
/**
* Removes all records from the collection, equivalent to using the command "remove" on every record explicitly.
* /!\ Can not be used in record insert query.
*/
public function clearRecords(): array;- Fixed logging.
- Deleted useless files and updated
.gitignore
- Replaced package darkaonline/ripcord by ang3/php-xmlrpc-client.
- Implemented interface
Ang3\Component\Odoo\Exception\ExceptionInterfacefor all client exceptions.
- Removed dependency of package ang3/php-dev-binaries.
- Added methods
searchOneandsearchAll. - Back to package darkaonline/ripcord.
- Removed XML-RPC client.
- Removed remote exception.
- Removed trace back feature.
- Fixed logging messages.
- Fixed method
findAll. - Updated endpoint logging level to
debugand added client logging with levelinfo
- Added method
findAll. - Fixed empty array result issue.
- Catched remote type exception when the method returns no value.
- Fixed composer dev dependencies
- Updated travis configuration
- Updated
README.md
- Fixed method
Client::create().
- Added method
Client::exists().
What you have to do:
- Create the client with the constructor
new Client(array $config)instead of calling static methodClient::createFromConfig(array $config). - Replace usages of the method
searchAndReadby the methodfindBy.
Logs:
- Replaced package darkaonline/ripcord to phpxmlrpc/phpxmlrpc.
- Created XML-RPC client.
- Deleted static method
Client::createFromConfig(array $config). - Renamed ORM method
searchAndRead(...)tofindBy(...). - Added ORM methods
find(...)tofindOneBy(...). - Added expression builder support.
- Updated namespace
Ang3\Component\Odoo\ClienttoAng3\Component\Odoo.
- Updated namespace
Ang3\Component\OdooApiClienttoAng3\Component\Odoo\Client.
That's it!