PHPackages                             oscar-team/odoo-json2 - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. oscar-team/odoo-json2

ActiveLibrary[API Development](/categories/api)

oscar-team/odoo-json2
=====================

Modern PHP client library for Odoo JSON-2 API (Odoo 19+)

v1.0.1(4mo ago)0243↓50%MITPHPPHP ^8.2

Since Dec 10Pushed 4mo agoCompare

[ Source](https://github.com/oscar-team/odoo-json2)[ Packagist](https://packagist.org/packages/oscar-team/odoo-json2)[ RSS](/packages/oscar-team-odoo-json2/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (3)Versions (3)Used By (0)

Odoo JSON-2 PHP Client
======================

[](#odoo-json-2-php-client)

A modern PHP client library for interacting with Odoo's JSON-2 API (Odoo 19+). This library provides a clean, type-safe interface for performing CRUD operations and calling custom methods on Odoo models.

Features
--------

[](#features)

- ✅ Modern JSON-2 API support (Odoo 19+)
- ✅ API key-based authentication
- ✅ Full CRUD operations (Create, Read, Update, Delete)
- ✅ Type-safe with PHP 8.2+
- ✅ Comprehensive error handling
- ✅ Built on Guzzle HTTP client
- ✅ Clean, Laravel-style code structure

Requirements
------------

[](#requirements)

- PHP 8.2 or higher
- Composer
- Odoo 19 or higher with JSON-2 API enabled

Installation
------------

[](#installation)

```
composer require oscar-team/odoo-json2
```

Laravel Integration
-------------------

[](#laravel-integration)

This package is designed to work seamlessly with Laravel. The service provider is automatically discovered, so no manual registration is required.

### Publishing Configuration

[](#publishing-configuration)

After installing the package, publish the configuration file:

```
php artisan vendor:publish --provider="OdooJson2\OdooServiceProvider" --tag="config"
```

This will create a `config/odoo.php` file in your Laravel application.

### Environment Configuration

[](#environment-configuration)

Add the following environment variables to your `.env` file:

```
ODOO_HOST=https://your-odoo-instance.com
ODOO_DATABASE=your-database-name
ODOO_API_KEY=your-api-key-here
ODOO_SSL_VERIFY=true

# Optional context settings
ODOO_LANG=en_US
ODOO_TIMEZONE=UTC
ODOO_COMPANY_ID=1
```

### Using the Odoo Client in Laravel

[](#using-the-odoo-client-in-laravel)

The package automatically registers the `Odoo` class as a singleton, so you can use it via dependency injection:

```
use OdooJson2\Odoo;

class YourController extends Controller
{
    public function __construct(
        private Odoo $odoo
    ) {}

    public function index()
    {
        $partners = $this->odoo->search('res.partner', []);
        return response()->json($partners);
    }
}
```

Or resolve it from the service container:

```
use OdooJson2\Odoo;

$odoo = app(Odoo::class);
$partners = $odoo->search('res.partner', []);
```

### Using Models in Laravel

[](#using-models-in-laravel)

Models are automatically booted when the service provider loads. You can use them directly:

```
use App\Models\Partner; // Your Odoo model

class PartnerController extends Controller
{
    public function index()
    {
        $partners = Partner::query()
            ->where('is_company', '=', true)
            ->get();

        return response()->json($partners);
    }
}
```

### Configuration File

[](#configuration-file)

The published configuration file (`config/odoo.php`) contains the following options:

- `host` - Your Odoo instance URL (required)
- `database` - Database name (optional, can be determined from API key)
- `api_key` - Your Odoo API key (required)
- `ssl_verify` - Whether to verify SSL certificates (default: true)
- `context` - Additional context settings (lang, timezone, companyId)

All values can be overridden via environment variables.

Configuration
-------------

[](#configuration)

### Getting Your API Key

[](#getting-your-api-key)

1. Log in to your Odoo instance
2. Go to **Settings** → **Users &amp; Companies** → **API Keys**
3. Create a new API key
4. Copy the generated API key

Usage
-----

[](#usage)

### Basic Setup

[](#basic-setup)

```
use OdooJson2\OdooClient;

$client = new OdooClient(
    baseUrl: 'https://your-odoo-instance.com',
    apiKey: 'your-api-key-here',
    database: 'your-database-name'
);
```

### Search Records

[](#search-records)

```
// Search with domain
$partners = $client->search('res.partner', [
    ['name', '=', 'John Doe']
]);

// Search with options
$partners = $client->search('res.partner', [
    ['is_company', '=', true]
], [
    'limit' => 10,
    'offset' => 0,
    'order' => 'name asc'
]);
```

### Read Records

[](#read-records)

```
// Read specific records
$partners = $client->read('res.partner', [1, 2, 3]);

// Read with specific fields
$partners = $client->read('res.partner', [1, 2, 3], [
    'name',
    'email',
    'phone'
]);
```

### Search and Read

[](#search-and-read)

```
// Search and read in one call
$partners = $client->searchRead('res.partner', [
    ['is_company', '=', true]
], [
    'fields' => ['name', 'email', 'phone'],
    'limit' => 10
]);
```

### Create Records

[](#create-records)

```
// Create a single record
$partnerId = $client->create('res.partner', [
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'phone' => '+1234567890',
    'is_company' => false
]);

// Create multiple records
$partnerIds = $client->create('res.partner', [
    [
        'name' => 'Company A',
        'is_company' => true,
    ],
    [
        'name' => 'Company B',
        'is_company' => true,
    ],
]);
```

### Update Records

[](#update-records)

```
// Update records
$success = $client->write('res.partner', [1, 2], [
    'email' => 'newemail@example.com',
    'phone' => '+9876543210'
]);
```

### Delete Records

[](#delete-records)

```
// Delete records
$success = $client->unlink('res.partner', [1, 2, 3]);
```

### Count Records

[](#count-records)

```
// Count records matching a domain
$count = $client->count('res.partner', [
    ['is_company', '=', true]
]);
```

### Get Model Fields

[](#get-model-fields)

```
// Get all fields of a model
$fields = $client->fieldsGet('res.partner');

// Get specific field attributes
$fields = $client->fieldsGet('res.partner', ['type', 'required', 'string']);
```

### Custom Method Calls

[](#custom-method-calls)

```
// Call any custom method on a model
$result = $client->call('sale.order', 'action_confirm', [
    'ids' => [123],
    'context' => []
]);
```

### Error Handling

[](#error-handling)

```
use OdooJson2\Exceptions\OdooException;
use OdooJson2\Exceptions\AuthenticationException;
use OdooJson2\Exceptions\ConnectionException;

try {
    $partners = $client->search('res.partner', []);
} catch (AuthenticationException $e) {
    // Handle authentication errors
    echo "Authentication failed: " . $e->getMessage();
} catch (ConnectionException $e) {
    // Handle connection errors
    echo "Connection failed: " . $e->getMessage();
} catch (OdooException $e) {
    // Handle other Odoo errors
    echo "Odoo error: " . $e->getMessage();
}
```

### Advanced Configuration

[](#advanced-configuration)

```
// Custom HTTP client options
$client = new OdooClient(
    baseUrl: 'https://your-odoo-instance.com',
    apiKey: 'your-api-key-here',
    database: 'your-database-name',
    options: [
        'timeout' => 60,
        'verify' => false, // Only for development
        'proxy' => 'http://proxy.example.com:8080',
    ]
);

// Use custom HTTP client
$customClient = new \GuzzleHttp\Client([
    'timeout' => 120,
]);
$client->setHttpClient($customClient);
```

Domain Syntax
-------------

[](#domain-syntax)

The domain syntax follows Odoo's standard format:

```
// Simple condition
[['field', '=', 'value']]

// Multiple conditions (AND)
[
    ['field1', '=', 'value1'],
    ['field2', '!=', 'value2']
]

// OR conditions
['|', ['field1', '=', 'value1'], ['field2', '=', 'value2']]

// Operators: =, !=, , =, like, ilike, in, not in, child_of, parent_of
```

Examples
--------

[](#examples)

### Working with Sales Orders

[](#working-with-sales-orders)

```
// Create a sales order
$orderId = $client->create('sale.order', [
    'partner_id' => 1,
    'order_line' => [
        [
            'product_id' => 5,
            'product_uom_qty' => 10,
            'price_unit' => 100.00,
        ],
    ],
]);

// Confirm the order
$client->call('sale.order', 'action_confirm', [
    'ids' => [$orderId]
]);

// Search for confirmed orders
$orders = $client->searchRead('sale.order', [
    ['state', '=', 'sale']
], [
    'fields' => ['name', 'partner_id', 'amount_total'],
    'limit' => 20
]);
```

### Working with Products

[](#working-with-products)

```
// Create a product
$productId = $client->create('product.product', [
    'name' => 'New Product',
    'type' => 'product',
    'categ_id' => 1,
    'list_price' => 99.99,
    'standard_price' => 50.00,
]);

// Update product price
$client->write('product.product', [$productId], [
    'list_price' => 89.99
]);

// Search products by category
$products = $client->searchRead('product.product', [
    ['categ_id', '=', 1],
    ['sale_ok', '=', true]
], [
    'fields' => ['name', 'list_price', 'qty_available'],
    'order' => 'list_price desc'
]);
```

Eloquent-like Models
--------------------

[](#eloquent-like-models)

This library provides an Eloquent-like ORM for working with Odoo models, making it easy to define models with relationships and perform type-safe operations.

### Basic Model Definition

[](#basic-model-definition)

```
use OdooJson2\Attributes\Field;
use OdooJson2\Attributes\Model;
use OdooJson2\Odoo\OdooModel;

#[Model('res.partner')]
class Partner extends OdooModel
{
    #[Field]
    public string $name;

    #[Field('email')]
    public ?string $email;

    #[Field('phone')]
    public ?string $phone;

    #[Field('is_company')]
    public bool $isCompany = false;
}
```

### Field Attributes

[](#field-attributes)

Use the `#[Field]` attribute to map Odoo fields to PHP properties:

```
#[Model('product.product')]
class Product extends OdooModel
{
    // Field name matches property name
    #[Field]
    public string $name;

    // Custom field name mapping
    #[Field('default_code')]
    public ?string $defaultCode;

    #[Field('list_price')]
    public ?float $listPrice;

    #[Field('standard_price')]
    public ?float $standardPrice;
}
```

### Many-to-One Relationships (BelongsTo)

[](#many-to-one-relationships-belongsto)

Use `#[Key]` to extract the ID and `#[BelongsTo]` to define the relationship:

```
#[Model('product.product')]
class Product extends OdooModel
{
    #[Field]
    public string $name;

    // Extract the ID from the many-to-one field
    #[Field('categ_id'), Key]
    public ?int $categoryId;

    // Extract the name from the many-to-one field
    #[Field('categ_id'), KeyName]
    public ?string $categoryName;

    // Define the relationship
    #[Field('categ_id'), BelongsTo(name: 'categ_id', class: ProductCategory::class)]
    public ?ProductCategory $category;
}
```

### One-to-Many Relationships (HasMany)

[](#one-to-many-relationships-hasmany)

```
#[Model('account.move')]
class AccountMove extends OdooModel
{
    #[Field('name')]
    public string $name;

    #[Field('partner_id'), Key]
    public ?int $partnerId;

    // One-to-many relationship
    #[Field('invoice_line_ids'), HasMany(class: AccountMoveLine::class, name: 'invoice_line_ids')]
    public ?array $invoiceLines;
}
```

### Complete Model Example

[](#complete-model-example)

```
use OdooJson2\Attributes\BelongsTo;
use OdooJson2\Attributes\Field;
use OdooJson2\Attributes\HasMany;
use OdooJson2\Attributes\Key;
use OdooJson2\Attributes\KeyName;
use OdooJson2\Attributes\Model;
use OdooJson2\Odoo\OdooModel;

#[Model('res.partner')]
class Partner extends OdooModel
{
    #[Field]
    public string $name;

    #[Field('display_name')]
    public ?string $displayName;

    #[Field('email')]
    public ?string $email;

    #[Field('phone')]
    public ?string $phone;

    #[Field('street')]
    public ?string $street;

    #[Field('city')]
    public ?string $city;

    #[Field('zip')]
    public ?string $zip;

    #[Field('country_id'), Key]
    public ?int $countryId;

    #[Field('country_id'), KeyName]
    public ?string $countryName;

    #[Field('is_company')]
    public bool $isCompany = false;

    #[Field('active')]
    public bool $active = true;

    // Self-referential relationship
    #[Field('parent_id'), Key]
    public ?int $parentId;

    #[Field('parent_id'), BelongsTo(name: 'parent_id', class: Partner::class)]
    public ?Partner $parent;

    #[Field('child_ids'), HasMany(class: Partner::class, name: 'child_ids')]
    public ?array $children;
}
```

### Initializing Models

[](#initializing-models)

Before using models, you need to boot the Odoo instance:

```
use OdooJson2\Odoo;
use OdooJson2\Odoo\Config;
use OdooJson2\Odoo\Context;
use OdooJson2\Odoo\OdooModel;

// Create Odoo instance
$config = new Config(
    database: 'your-database',
    host: 'https://your-odoo-instance.com',
    apiKey: 'your-api-key'
);
$context = new Context();
$odoo = new Odoo($config, $context);

// Boot models
OdooModel::boot($odoo);
```

### Querying with Models

[](#querying-with-models)

```
// Find a record by ID
$partner = Partner::find(1);

// Get all records
$partners = Partner::all();

// Query with conditions
$partners = Partner::query()
    ->where('is_company', '=', true)
    ->where('active', '=', true)
    ->limit(10)
    ->orderBy('name')
    ->get();

// Get first matching record
$partner = Partner::query()
    ->where('email', '=', 'john@example.com')
    ->first();

// Count records
$count = Partner::query()
    ->where('is_company', '=', true)
    ->count();

// Get only IDs
$ids = Partner::query()
    ->where('active', '=', true)
    ->ids();
```

### Creating Records

[](#creating-records)

```
// Create a new partner
$partner = new Partner();
$partner->name = 'John Doe';
$partner->email = 'john@example.com';
$partner->phone = '+1234567890';
$partner->isCompany = false;
$partner->save();

// Or use fill method
$partner = new Partner();
$partner->fill([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'phone' => '+1234567890',
]);
$partner->save();
```

### Updating Records

[](#updating-records)

```
// Update an existing record
$partner = Partner::find(1);
$partner->email = 'newemail@example.com';
$partner->phone = '+9876543210';
$partner->save();

// Or update multiple records via query
Partner::query()
    ->where('is_company', '=', true)
    ->update(['active' => false]);
```

### Deleting Records

[](#deleting-records)

```
// Delete a single record
$partner = Partner::find(1);
// Note: OdooModel doesn't have a delete() method by default
// Use the Odoo client directly:
$odoo->unlink('res.partner', [$partner->id]);

// Or delete via query
Partner::query()
    ->where('active', '=', false)
    ->delete();
```

### Working with Relationships

[](#working-with-relationships)

```
// Access belongs-to relationship
$product = Product::find(1);
$category = $product->category; // Automatically loaded

// Access has-many relationship (lazy loaded)
$invoice = AccountMove::find(1);
$lines = $invoice->invoiceLines; // Lazy loaded when accessed

// Working with self-referential relationships
$partner = Partner::find(1);
$parent = $partner->parent; // Parent partner
$children = $partner->children; // Child partners
```

### Advanced Queries

[](#advanced-queries)

```
// Search with multiple conditions
$partners = Partner::query()
    ->where('is_company', '=', true)
    ->orWhere('email', '!=', null)
    ->limit(50)
    ->offset(10)
    ->orderBy('name', 'desc')
    ->get();

// Read specific records
$partners = Partner::read([1, 2, 3, 4, 5]);

// Get model fields
$fields = Partner::listFields();
```

### Real-World Examples

[](#real-world-examples)

#### Example 1: Create a Partner with Relationships

[](#example-1-create-a-partner-with-relationships)

```
$partner = new Partner();
$partner->name = 'Acme Corporation';
$partner->email = 'contact@acme.com';
$partner->phone = '+1234567890';
$partner->isCompany = true;
$partner->street = '123 Main St';
$partner->city = 'New York';
$partner->zip = '10001';
$partner->countryId = 233; // USA
$partner->save();

echo "Created partner with ID: {$partner->id}\n";
```

#### Example 2: Find and Update Products

[](#example-2-find-and-update-products)

```
// Find products by category
$products = Product::query()
    ->where('categ_id', '=', 1)
    ->where('sale_ok', '=', true)
    ->where('active', '=', true)
    ->get();

foreach ($products as $product) {
    echo "Product: {$product->name}, Price: {$product->listPrice}\n";

    // Update price
    $product->listPrice = $product->listPrice * 1.1; // 10% increase
    $product->save();
}
```

#### Example 3: Working with Invoices

[](#example-3-working-with-invoices)

```
// Find invoices for a partner
$invoices = AccountMove::query()
    ->where('partner_id', '=', 1)
    ->where('move_type', '=', 'out_invoice')
    ->where('state', '=', 'posted')
    ->get();

foreach ($invoices as $invoice) {
    echo "Invoice: {$invoice->name}, Total: {$invoice->amountTotal}\n";

    // Access invoice lines (lazy loaded)
    if ($invoice->invoiceLines) {
        foreach ($invoice->invoiceLines as $line) {
            echo "  - Line: {$line->name}, Amount: {$line->priceTotal}\n";
        }
    }
}
```

#### Example 4: Hierarchical Partners

[](#example-4-hierarchical-partners)

```
// Find company partners
$companies = Partner::query()
    ->where('is_company', '=', true)
    ->get();

foreach ($companies as $company) {
    echo "Company: {$company->name}\n";

    // Access children (lazy loaded)
    if ($company->children) {
        foreach ($company->children as $child) {
            echo "  - Contact: {$child->name} ({$child->email})\n";
        }
    }
}
```

Testing
-------

[](#testing)

```
# Run tests
composer test

# Run with coverage
composer test-coverage
```

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

License
-------

[](#license)

This library is open-sourced software licensed under the [MIT license](LICENSE).

Support
-------

[](#support)

For issues, questions, or contributions, please open an issue on GitHub.

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance74

Regular maintenance activity

Popularity15

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~11 days

Total

2

Last Release

148d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/db2675a9674fa6c8a957211599bdc1af7597506f161b47eb64351c462c2a22eb?d=identicon)[moiz2050](/maintainers/moiz2050)

---

Top Contributors

[![moiz2050](https://avatars.githubusercontent.com/u/820400?v=4)](https://github.com/moiz2050 "moiz2050 (1 commits)")

---

Tags

apiclientlaravelodooERPjson-2

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/oscar-team-odoo-json2/health.svg)

```
[![Health](https://phpackages.com/badges/oscar-team-odoo-json2/health.svg)](https://phpackages.com/packages/oscar-team-odoo-json2)
```

###  Alternatives

[openai-php/laravel

OpenAI PHP for Laravel is a supercharged PHP API client that allows you to interact with the Open AI API

3.7k7.6M74](/packages/openai-php-laravel)[devio/pipedrive

Complete Pipedrive API client for PHP and/or Laravel

1691.8M](/packages/devio-pipedrive)[mozex/anthropic-laravel

Anthropic PHP for Laravel is a supercharged PHP API client that allows you to interact with the Anthropic API

71226.4k1](/packages/mozex-anthropic-laravel)[jubaer/zoom-laravel

A comprehensive Zoom integration package for Laravel, providing easy-to-use API functionality to interact with the Zoom platform using PHP.

58107.8k](/packages/jubaer-zoom-laravel)[barryvanveen/lastfm

Last.fm API client for PHP 7+

254.5k](/packages/barryvanveen-lastfm)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
