wdelfuego/php-odoo-api-client

Odoo API客户端

安装: 11

依赖项: 0

建议者: 0

安全性: 0

星标: 0

关注者: 0

分支: 22

类型:组件

v7.2.1 2024-06-04 21:23 UTC

This package is auto-updated.

Last update: 2024-09-04 21:55:12 UTC


README

Build Status Latest Stable Version Latest Unstable Version Total Downloads

使用XML-RPC Odoo ORM External API的Odoo API客户端。它允许您轻松调用您的Odoo实例并管理记录。

您正在阅读版本7.0的文档,如果您的版本较旧,请阅读此文档(6.1.3) 请参阅UPGRADE-7.0.md文件,以便轻松升级版本。

主要功能

  • 身份验证 <7.0
  • 基本的XML-RPC调用 <7.0
  • 表达式构建器 <7.0
  • 数据库抽象层(DBAL) >=7.0
    • 记录管理器
    • 仓库
    • 查询构建器

须知

如果您在Symfony应用程序中,您可能对ang3/odoo-bundle(客户端集成)感兴趣。

要求

  • 必须启用PHP扩展php-xmlrpc

安装

打开命令行,进入您的项目目录,并执行以下命令以下载客户端的最新稳定版本

$ composer require ang3/php-odoo-api-client

此命令要求您全局安装Composer,如Composer文档中的安装章节中所述。

基本用法

首先,您必须创建客户端实例

<?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([
    'url' => '<host>',
    'database' => '<database>',
    'username' => '<user>',
    'password' => '<password>',
], $logger = null);

异常

  • 当从静态方法createFromConfig()缺少必要参数时,抛出Ang3\Component\Odoo\Exception\MissingConfigParameterException

然后,进行调用

$result = $client->call($name, $method, $parameters = [], $options = []);

异常

  • 当认证失败时,抛出Ang3\Component\Odoo\Exception\AuthenticationException
  • 当请求失败时,抛出Ang3\Component\Odoo\Exception\RequestException

这些之前的异常可以由客户端的所有方法抛出。

DBAL(数据库抽象层)

首先,Odoo是一个数据库。每个"模型"都是一个表,并有自己的字段。

DBAL功能是在版本7.0中添加的 - 如果您的版本较旧,请使用客户端内置的ORM方法,如专用文档中所述:请注意,这些客户端ORM方法自版本7.0以来已弃用。

记录管理器

客户端提供了一个记录管理器来管理您的Odoo模型记录。

您可以通过以下方式获取客户端的相关管理器

$recordManager = $client->getRecordManager();

您也可以使用客户端实例创建自己的

use Ang3\Component\Odoo\DBAL\RecordManager;

/** @var \Ang3\Component\Odoo\Client $myClient */
$recordManager = new RecordManager($myClient);

内置ORM方法

以下是记录管理器提供的所有内置ORM方法

use Ang3\Component\Odoo\DBAL\Expression\DomainInterface;

/**
 * Create a new record.
 *
 * @return int the ID of the new record
 */public function create(string $modelName, array $data): int;

/**
 * Update record(s).
 *
 * NB: It is not currently possible to perform “computed” updates (by criteria).
 * To do it, you have to perform a search then an update with search result IDs.
 *
 * @param array|int $ids
 */
public function update(string $modelName, $ids, array $data = []): void;

/**
 * Delete record(s).
 *
 * NB: It is not currently possible to perform “computed” deletes (by criteria).
 * To do it, you have to perform a search then a delete with search result IDs.
 *
 * @param array|int $ids
 */
public function delete(string $modelName, $ids): void;

/**
 * Search one ID of record by criteria.
 */
public function searchOne(string $modelName, ?DomainInterface $criteria): ?int;

/**
 * Search all ID of record(s).
 *
 * @return int[]
 */
public function searchAll(string $modelName, array $orders = [], int $limit = null, int $offset = null): array;

/**
 * Search ID of record(s) by criteria.
 *
 * @return int[]
 */
public function search(string $modelName, ?DomainInterface $criteria = null, array $orders = [], int $limit = null, int $offset = null): array;

/**
 * Find ONE record by ID.
 *
 * @throws RecordNotFoundException when the record was not found
 */
public function read(string $modelName, int $id, array $fields = []): array;

/**
 * Find ONE record by ID.
 */
public function find(string $modelName, int $id, array $fields = []): ?array;

/**
 * Find ONE record by criteria.
 */
public function findOneBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $offset = null): ?array;

/**
 * Find all records.
 *
 * @return array[]
 */
public function findAll(string $modelName, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array;

/**
 * Find record(s) by criteria.
 *
 * @return array[]
 */
public function findBy(string $modelName, ?DomainInterface $criteria = null, array $fields = [], array $orders = [], int $limit = null, int $offset = null): array;

/**
 * Check if a record exists.
 */
public function exists(string $modelName, int $id): bool;

/**
 * Count number of all records for the model.
 */
public function countAll(string $modelName): int;

/**
 * Count number of records for a model and criteria.
 */
public function count(string $modelName, ?DomainInterface $criteria = null): int;

对于选择/搜索查询中的$criteria和用于数据写入上下文的$data,请阅读表达式构建器部分。

模式

您可以通过调用获取器方法RecordManager::getSchema()来获取您的Odoo数据库的模式

/** @var \Ang3\Component\Odoo\DBAL\Schema\Schema $schema */
$schema = $recordManager->getSchema();

模式可以帮助您获取所有模型名称或获取模型的元数据。

获取所有模型名称

/** @var string[] $modelNames */
$modelNames = $schema->getModelNames();

获取模型元数据

/** @var \Ang3\Component\Odoo\DBAL\Schema\Model $model */
$model = $schema->getModel('res.company');

如果模型不存在,则抛出类型为Ang3\Component\Odoo\DBAL\Schema\SchemaException的异常。

查询构建器

它可以帮助您通过链式调用帮助方法(如SQL数据库中的Doctrine)轻松创建查询。

创建查询构建器

/** @var string|null $modelName */
$queryBuilder = $recordManager->createQueryBuilder($modelName);

变量$modelName表示查询的目标模型(from子句)。

构建您的查询

以下是QueryBuilder中可用的所有帮助方法的完整列表

/**
 * Defines the query of type "SELECT" with selected fields.
 * No fields selected = all fields returned.
 *
 * @param array|string|null $fields
 */
public function select($fields = null): self;

/**
 * Defines the query of type "SEARCH".
 */
public function search(): self;

/**
 * Defines the query of type "INSERT".
 */
public function insert(): self;

/**
 * Defines the query of type "UPDATE" with ids of records to update.
 *
 * @param int[] $ids
 */
public function update(array $ids): self;

/**
 * Defines the query of type "DELETE" with ids of records to delete.
 */
public function delete(array $ids): self;

/**
 * Adds a field to select.
 *
 * @throws LogicException when the type of the query is not "SELECT".
 */
public function addSelect(string $fieldName): self;

/**
 * Gets selected fields.
 */
public function getSelect(): array;

/**
 * Sets the target model name.
 */
public function from(string $modelName): self;

/**
 * Gets the target model name of the query.
 */
public function getFrom(): ?string;

/**
 * Sets target IDs in case of query of type "UPDATE" or "DELETE".
 *
 * @throws LogicException when the type of the query is not "UPDATE" nor "DELETE".
 */
public function setIds(array $ids): self;

/**
 * Adds target ID in case of query of type "UPDATE" or "DELETE".
 *
 * @throws LogicException when the type of the query is not "UPDATE" nor "DELETE".
 */
public function addId(int $id): self;

/**
 * Sets field values in case of query of type "INSERT" or "UPDATE".
 *
 * @throws LogicException when the type of the query is not "INSERT" nor "UPDATE".
 */
public function setValues(array $values = []): self;

/**
 * Set a field value in case of query of type "INSERT" or "UPDATE".
 *
 * @param mixed $value
 *
 * @throws LogicException when the type of the query is not "INSERT" nor "UPDATE".
 */
public function set(string $fieldName, $value): self;

/**
 * Gets field values set in case of query of type "INSERT" or "UPDATE".
 */
public function getValues(): array;

/**
 * Sets criteria for queries of type "SELECT" and "SEARCH".
 *
 * @throws LogicException when the type of the query is not "SELECT" not "SEARCH".
 */
public function where(?DomainInterface $domain = null): self;

/**
 * Takes the WHERE clause and adds a node with logical operator AND.
 *
 * @throws LogicException when the type of the query is not "SELECT" nor "SEARCH".
 */
public function andWhere(DomainInterface $domain): self;

/**
 * Takes the WHERE clause and adds a node with logical operator OR.
 *
 * @throws LogicException when the type of the query is not "SELECT" nor "SEARCH".
 */
public function orWhere(DomainInterface $domain): self;

/**
 * Gets the WHERE clause.
 */
public function getWhere(): ?DomainInterface;

/**
 * Sets orders.
 */
public function setOrders(array $orders = []): self;

/**
 * Clears orders and adds one.
 */
public function orderBy(string $fieldName, bool $isAsc = true): self;

/**
 * Adds order.
 *
 * @throws LogicException when the query type is not valid.
 */
public function addOrderBy(string $fieldName, bool $isAsc = true): self;

/**
 * Gets ordered fields.
 */
public function getOrders(): array;

/**
 * Sets the max results of the query (limit).
 */
public function setMaxResults(?int $maxResults): self;

/**
 * Gets the max results of the query.
 */
public function getMaxResults(): ?int;

/**
 * Sets the first results of the query (offset).
 */
public function setFirstResult(?int $firstResult): self;

/**
 * Gets the first results of the query.
 */
public function getFirstResult(): ?int;

然后,像下面这样构建您的查询

$query = $queryBuilder->getQuery();

您的查询是Ang3\Component\Odoo\Query\OrmQuery的一个实例。

执行您的查询

您可以根据查询类型以不同的方式获取/计数结果或执行插入/更新/删除操作。

/**
 * Counts the number of records from parameters.
 * Allowed methods: SEARCH, SEARCH_READ.
 *
 * @throws QueryException on invalid query method.
 */
public function count(): int;

/**
 * Gets just ONE scalar result.
 * Allowed methods: SEARCH, SEARCH_READ.
 *
 * @return bool|int|float|string
 *
 * @throws NoUniqueResultException on no unique result
 * @throws NoResultException       on no result
 * @throws QueryException          on invalid query method.
 */
public function getSingleScalarResult();

/**
 * Gets one or NULL scalar result.
 * Allowed methods: SEARCH, SEARCH_READ.
 *
 * @return bool|int|float|string|null
 *
 * @throws NoUniqueResultException on no unique result
 * @throws QueryException          on invalid query method.
 */
public function getOneOrNullScalarResult();

/**
 * Gets a list of scalar result.
 * Allowed methods: SEARCH, SEARCH_READ.
 *
 * @throws QueryException on invalid query method.
 *
 * @return array<bool|int|float|string>
 */
public function getScalarResult(): array;

/**
 * Gets one row.
 * Allowed methods: SEARCH, SEARCH_READ.
 *
 * @throws NoUniqueResultException on no unique result
 * @throws NoResultException       on no result
 * @throws QueryException          on invalid query method.
 */
public function getSingleResult(): array;

/**
 * Gets one or NULL row.
 * Allowed methods: SEARCH, SEARCH_READ.
 *
 * @throws NoUniqueResultException on no unique result
 * @throws QueryException          on invalid query method.
 */
public function getOneOrNullResult(): ?array;

/**
 * Gets all result rows.
 * Allowed methods: SEARCH, SEARCH_READ.
 *
 * @throws QueryException on invalid query method.
 */
public function getResult(): array;

/**
 * Execute the query.
 * Allowed methods: all.
 *
 * @return mixed
 */
public function execute();

仓库

有时,您可能希望将查询保留在内存中以在代码中重用它。为此,您应使用仓库。仓库是一个类,它帮助您隔离特定模型的查询。

例如,让我们为您的公司创建一个仓库并定义一个查询以获取所有法国公司

namespace App\Odoo\Repository;

use Ang3\Component\Odoo\DBAL\RecordManager;
use Ang3\Component\Odoo\DBAL\Repository\RecordRepository;

class CompanyRepository extends RecordRepository
{
    public function __construct(RecordManager $recordManager)
    {
        parent::__construct($recordManager, 'res.company');
    }

    public function findFrenchCompanies(): array
    {
        return $this
            ->createQueryBuilder()
            ->select('name')
            ->where($this->expr()->eq('country_id.code', 'FR'))
            ->getQuery()
            ->getResult();
    }
}

请注意,即使您没有明确选择,Odoo也总是会在结果中返回记录ID。

每个仓库在构造时都会注册到记录管理器中。这就是为什么您可以直接从记录管理器检索您的仓库的原因。

/** @var \App\Odoo\Repository\CompanyRepository $companyRepository */
$companyRepository = $recordManager->getRepository('res.company');

如果没有为模型创建仓库,则使用默认仓库Ang3\Component\Odoo\DBAL\Repository\RecordRepository。最后但同样重要的是,所有仓库都存储在相关的记录管理器中,以避免创建相同仓库的多个实例。

表达式构建器

有两种类型的表达式:用于标准的domains和数据写入上下文中的collection operations。Odoo为这些表达式提供了自己的数组格式。表达式构建器的目的是提供一些帮助方法,以简化程序员的编程生活。

以下是如何从客户端或记录管理器获取构建器的示例

$expr = $clientOrRecordManager->expr();
// or $expr = $clientOrRecordManager->getExpressionBuilder();

您仍然可以通过创建一个新实例来独立使用表达式构建器

use Ang3\Component\Odoo\DBAL\Expression\ExpressionBuilder;

$expr = new ExpressionBuilder();

对于所有select/search/count查询,Odoo都等待一个包含polish notation逻辑运算(ANDORNOT)的数组。

创建复杂的域可能会很快变得丑陋,但不用担心,构建器为您处理所有这些。

每个域构建器方法都会创建一个Ang3\Component\Odoo\Expression\DomainInterface的实例。该接口的唯一方法是将表达式转换为规范的数组。

以下是如何使用ExpressionBuilder辅助方法的示例

// Get the expression builder
$expr = $recordManager->expr();

$result = $recordManager->findBy('model_name', $expr->andX( // Logical node "AND"
	$expr->gte('id', 10), // id >= 10
	$expr->lte('id', 100), // id <= 10
));

当然,您可以嵌套逻辑节点

$result = $recordManager->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)
    )
));

客户端会自动格式化所有域,方法是调用特殊的构建器方法normalizeDomains()

以下是用于域表达式的ExpressionBuilder中可用的所有帮助方法的完整列表

/**
 * 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;

集合操作

在数据写入上下文中,类型为insert/update的查询允许您使用特殊命令管理toMany集合字段。

请阅读ORM文档以了解我们所说的内容。

表达式构建器提供辅助方法来构建一个良好的操作命令:每个操作方法都会返回一个Ang3\Component\Odoo\DBAL\Expression\CollectionOperation的实例。与域类似,该接口的唯一方法是将表达式转换为规范的数组。

以下是如何使用操作方法的示例

// Get the expression builder
$expr = $recordManager->expr();

// Prepare data for a new record
$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 = $recordManager->create('model_name', $data);

客户端会自动格式化所有写入方法(createupdate)的所有查询参数,方法是调用特殊的构建器方法normalizeData()

这里是ExpressionBuilder中用于操作表达式可用的完整辅助方法列表

/**
 * Adds a new record created from data.
 */
public function createRecord(array $data): CollectionOperation;

/**
 * Updates an existing record of id $id with data.
 * /!\ Can not be used in record CREATE query.
 */
public function updateRecord(int $id, array $data): CollectionOperation;

/**
 * Adds an existing record of id $id to the collection.
 */
public function addRecord(int $id): CollectionOperation;

/**
 * Removes the record of id $id from the collection, but does not delete it.
 * /!\ Can not be used in record CREATE query.
 */
public function removeRecord(int $id): CollectionOperation;

/**
 * Removes the record of id $id from the collection, then deletes it from the database.
 * /!\ Can not be used in record CREATE query.
 */
public function deleteRecord(int $id): CollectionOperation;

/**
 * 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 = []): CollectionOperation;

/**
 * Removes all records from the collection, equivalent to using the command "remove" on every record explicitly.
 * /!\ Can not be used in record CREATE query.
 */
public function clearRecords(): CollectionOperation;

数据支持

  • 标量值保持不变
  • 数组递归转换
  • 类型为\DateTimeInterface的对象会自动按照UTC时区格式化为字符串
  • 可迭代的/生成器对象会被转换为一个数组
  • 非可迭代值会自动转换为字符串(因此任何非支持的对象必须定义__toString()方法)

资源