Connection.php 8.16 KB
<?php
    
    namespace artbox\odoo\components;
    
    use Ripcord\Ripcord;
    use yii\base\Component;
    use yii\base\Configurable;
    use yii\base\InvalidConfigException;
    
    /**
     * Odoo connection class
     *
     * @property string                 $dsn         Connection string
     * @property null|integer           $uid         Authenticated user ID
     * @property \Ripcord\Client\Client $fetchClient Fetch client
     */
    class Connection extends Component implements Configurable
    {
        /**
         * @event Event an event that is triggered after a DB connection is established
         */
        const EVENT_AFTER_OPEN = 'afterOpen';
        
        /**
         * @var string $url Odoo server URL
         */
        public $url;
        
        /**
         * @var string $db Odoo db name
         */
        public $db;
        
        /**
         * @var string $username Odoo username
         */
        public $username;
        
        /**
         * @var string $password Odoo password
         */
        public $password;
        
        /**
         * @var bool whether to log command and query executions.
         * When enabled this option may reduce performance. Odoo commands may contain large data,
         * consuming both CPU and memory.
         * It makes sense to disable this option in the production environment.
         */
        public $enableLogging = true;
        
        /**
         * @var bool whether to enable profiling the commands and queries being executed.
         * This option will have no effect in case [[enableLogging]] is disabled.
         */
        public $enableProfiling = true;
        
        /**
         * @var integer $uid Authenticated User ID
         */
        protected $uid;
        
        /**
         * @var \Ripcord\Client\Client $client Odoo client
         */
        protected $client;
        
        /**
         * @var \Ripcord\Client\Client $client Odoo fetch client
         */
        protected $fetchClient;
        
        /**
         * @var QueryBuilder|array|string the query builder for this connection
         */
        private $_queryBuilder = 'artbox\odoo\components\QueryBuilder';
        /**
         * @var LogBuilder|array|string log entries builder used for this connecton.
         */
        private $_logBuilder = 'artbox\odoo\components\LogBuilder';
        
        public function __construct(array $config = [])
        {
            parent::__construct($config);
        }
        
        /**
         * Returns the query builder for the this Odoo connection.
         *
         * @return QueryBuilder the query builder for the this Odoo connection.
         */
        public function getQueryBuilder(): QueryBuilder
        {
            if (!is_object($this->_queryBuilder)) {
                $this->_queryBuilder = \Yii::createObject($this->_queryBuilder, [ $this ]);
            }
            return $this->_queryBuilder;
        }
        /**
         * Sets the query builder for the this Odoo connection.
         *
         * @param QueryBuilder|array|string|null $queryBuilder the query builder for this Odoo connection.
         */
        public function setQueryBuilder($queryBuilder)
        {
            $this->_queryBuilder = $queryBuilder;
        }
        /**
         * Returns log builder for this connection.
         *
         * @return LogBuilder the log builder for this connection.
         */
        public function getLogBuilder(): LogBuilder
        {
            if (!is_object($this->_logBuilder)) {
                $this->_logBuilder = \Yii::createObject($this->_logBuilder);
            }
            return $this->_logBuilder;
        }
        /**
         * Sets log builder used for this connection.
         *
         * @param array|string|LogBuilder $logBuilder the log builder for this connection.
         */
        public function setLogBuilder($logBuilder)
        {
            $this->_logBuilder = $logBuilder;
        }
        
        /**
         * Establishes a Odoo connection.
         * It does nothing if a Odoo connection has already been established.
         *
         * @throws Exception if connection fails
         */
        public function open()
        {
            if ($this->client === null) {
                if (empty($this->username) || empty($this->db) || empty($this->password) || empty($this->url)) {
                    throw new InvalidConfigException(
                        '$username, $db, $password, $url must be set for ' . $this->className()
                    );
                }
                $token = "Opening Odoo connection: " . $this->getDsn();
                try {
                    \Yii::trace($token, __METHOD__);
                    \Yii::beginProfile($token, __METHOD__);
                    $this->client = Ripcord::client("$this->url/xmlrpc/2/common");
                    $this->uid = call_user_func_array(
                        [
                            $this->client,
                            'authenticate',
                        ],
                        [
                            $this->db,
                            $this->username,
                            $this->password,
                            [],
                        ]
                    );
                    if (!$this->uid) {
                        throw new InvalidConfigException("Incorrect authentication data.");
                    }
                    $this->fetchClient = Ripcord::client("$this->url/xmlrpc/2/object");
                    $this->initConnection();
                    \Yii::endProfile($token, __METHOD__);
                } catch (\Exception $e) {
                    \Yii::endProfile($token, __METHOD__);
                    throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
                }
            }
        }
        
        /**
         * Closes the currently active DB connection.
         * It does nothing if the connection is already closed.
         */
        public function close()
        {
            if ($this->client !== null) {
                \Yii::trace('Closing Odoo connection: ' . $this->getDsn(), __METHOD__);
                $this->client = null;
                $this->uid = null;
            }
        }
        
        /**
         * Initializes the DB connection.
         * This method is invoked right after the DB connection is established.
         * The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
         */
        protected function initConnection()
        {
            $this->trigger(self::EVENT_AFTER_OPEN);
        }
        
        /**
         * Creates Odoo command.
         *
         * @param string $model      Model name
         * @param string $method     Method name
         * @param array  $parameters parameters passed by position
         * @param array  $mapping    mapping of parameters to pass by keyword
         *
         * @return \artbox\odoo\components\Command command instance.
         */
        public function createCommand(string $model, string $method, array $parameters, array $mapping = []): Command
        {
            return new Command(
                [
                    'db'         => $this,
                    'model'      => $model,
                    'method'     => $method,
                    'parameters' => $parameters,
                    'mapping'    => $mapping,
                ]
            );
        }
        
        /**
         * Get dsn string
         *
         * @return string
         */
        public function getDsn()
        {
            return $this->url . ', ' . $this->db . ', ' . $this->username . ', ' . $this->password;
        }
        
        /**
         * Get authenticated user ID
         */
        public function getUid()
        {
            return $this->uid;
        }
        
        /**
         * Get fetch client
         *
         * @return \Ripcord\Client\Client
         */
        public function getFetchClient()
        {
            return $this->fetchClient;
        }
        
        /**
         * Get common client
         *
         * @return \Ripcord\Client\Client
         */
        public function getClient()
        {
            return $this->client;
        }
    }