Laravel9通过XMLRPC对接Odoo:认证成功但无法获取工单任务数据
Laravel 9对接Odoo XML-RPC获取模型数据失败排查
使用Laravel 9.46版本,通过phpxmlrpc/phpxmlrpc ^4.10包对接Odoo XML-RPC接口,已成功完成认证,但调用project.task、helpdesk.ticket等模型的search_read方法时始终报错。以下是原始实现代码,需排查并修复问题:
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use PhpXmlRpc\Client; use PhpXmlRpc\Request; use PhpXmlRpc\Value; use DB; class FetchOdooData extends Command { protected $signature = 'fetch:odoo-data'; protected $description = 'Fetch data from Odoo and update local database'; public function __construct() { parent::__construct(); } public function handle() { // Odoo credentials $url = 'http://urltoodoo::8049'; $db = 'DBNAME'; $username = 'username'; $password = 'APIKEY'; // Define XML-RPC endpoints $common_url = "{$url}/xmlrpc/2/common"; $object_url = "{$url}/xmlrpc/2/object"; // Create a client instance for common services $client = new Client($common_url); // Prepare the authentication request $request = new Request('authenticate', array( new Value($db, 'string'), new Value($username, 'string'), new Value($password, 'string'), new Value(array(), 'struct') )); // Send the authentication request $response = $client->send($request); if ($response->faultCode()) { $this->error('Error: ' . $response->faultString()); return; } // Retrieve the user ID from the response $user_id = $response->value()->scalarval(); if ($user_id) { $this->info("Authenticated successfully. User ID: $user_id"); } else { $this->error("Authentication failed."); return; } // Function to call Odoo methods function callOdooMethod($client, $method, $params) { var_dump($method); var_dump($params); var_dump($client); $msg = new Request($method, array(new Value($params, 'array'))); var_dump($msg); $resp = $client->send($msg); var_dump($resp); if ($resp->faultCode()) { return 'Error: ' . $resp->faultString(); } return $resp->value(); } // Fetch last sync time for a model function getLastSyncTime($model_name) { return DB::table('last_sync')->where('model_name', $model_name)->value('last_update'); } // Update last sync time for a model function updateLastSyncTime($model_name) { DB::table('last_sync')->updateOrInsert( ['model_name' => $model_name], ['last_update' => now()] ); } // Fetch data from Odoo function fetchData($models, $db, $user_id, $password, $model_name, $fields, $last_sync_time=0) { //$domain = $last_sync_time ? [['write_date', '>', $last_sync_time]] : []; $params = array( new Value($db, 'string'), new Value($user_id, 'int'), new Value($password, 'string'), new Value($model_name, 'string'), new Value('search_read', 'string'), new Value(array( //new Value($domain, 'array'), // Domain filter new Value($fields, 'array'), // Fields to retrieve ), 'array') ); return callOdooMethod($models, 'execute_kw', $params); } // Update local database with fetched data function updateLocalDatabase($model_name, $data) { foreach ($data as $record) { } } // Create a client instance for object services $models = new Client($object_url); var_dump($models); // Define fields to retrieve for each model $project_fields = array('name', 'user_id', 'date_start', 'date_end', 'tasks'); $task_fields = array('name', 'user_id', 'date_deadline', 'stage_id', 'description'); $ticket_fields = array('name', 'stage_id', 'user_id', 'priority', 'description'); $incident_fields = array('name', 'stage_id', 'user_id', 'priority', 'description'); // Replace with actual fields // Define models $models_to_fetch = array( 'project.project' => $project_fields, 'project.task' => $task_fields, 'helpdesk.ticket' => $ticket_fields ); foreach ($models_to_fetch as $model_name => $fields) { //$last_sync_time = getLastSyncTime($model_name); $data = fetchData($models, $db, $user_id, $password, $model_name, $fields); if (is_string($data)) { $this->error("Error fetching $model_name: " . $data); } else { $this->info(ucfirst(str_replace('.', ' ', $model_name)) . " Data:\n"); print_r($data); updateLocalDatabase($model_name, $data); updateLastSyncTime($model_name); } } } }
核心问题分析与修复方案
1. URL格式错误
原始代码中$url = 'http://urltoodoo::8049';包含多余的冒号,导致请求地址无效,修正为:
$url = 'http://urltoodoo:8049';
2. Odoo execute_kw参数结构错误
Odoo的execute_kw要求参数格式为:[db, uid, password, model_name, method_name, [method_args...]],原始代码错误嵌套了多层Value对象,导致Odoo无法解析参数。
修复fetchData方法:
private function fetchData($models, $db, $user_id, $password, $model_name, $fields, $last_sync_time=0) { $domain = $last_sync_time ? [['write_date', '>', $last_sync_time]] : []; // 将字段数组转换为XmlRpc Value数组 $xmlrpc_fields = array_map(function($field) { return new Value($field, 'string'); }, $fields); // 将domain转换为XmlRpc Value结构 $xmlrpc_domain = []; foreach ($domain as $condition) { $xmlrpc_condition = array_map(function($val) { return is_string($val) ? new Value($val, 'string') : new Value($val, 'int'); }, $condition); $xmlrpc_domain[] = new Value($xmlrpc_condition, 'array'); } // 构造execute_kw的正确参数 $params = array( new Value($db, 'string'), new Value($user_id, 'int'), new Value($password, 'string'), new Value($model_name, 'string'), new Value('search_read', 'string'), // search_read的参数:domain, fields new Value( array( new Value($xmlrpc_domain, 'array'), new Value($xmlrpc_fields, 'array') ), 'array' ) ); return $this->callOdooMethod($models, 'execute_kw', $params); }
3. callOdooMethod参数包装错误
原始代码将参数数组额外包装了一层Value对象,导致结构完全错误,修正后直接传入参数数组:
private function callOdooMethod($client, $method, $params) { $msg = new Request($method, $params); $resp = $client->send($msg); if ($resp->faultCode()) { return 'Error: ' . $resp->faultString(); } return $resp->value()->scalarval(); }
4. 内部函数作用域问题
在handle方法内定义的内部函数无法直接访问Laravel门面,需将函数改为类的私有方法:
// 将所有内部函数改为类私有方法,调用时使用$this private function getLastSyncTime($model_name) { return DB::table('last_sync')->where('model_name', $model_name)->value('last_update'); } private function updateLastSyncTime($model_name) { DB::table('last_sync')->updateOrInsert( ['model_name' => $model_name], ['last_update' => now()] ); } private function updateLocalDatabase($model_name, $data) { foreach ($data as $record) { // 实现本地数据库更新逻辑 } }
5. 权限验证
确保Odoo中使用的API账号拥有project.task、helpdesk.ticket等模型的读取权限,可在Odoo后台用户权限设置中检查调整。
内容的提问来源于stack exchange,提问作者Osama Ahmed Momin
相关产品推荐
相关产品推荐

