語言和開發規則
以下是我們在Dolibarr項目中使用的開發語言、語法和開發標準的一些規則:
版本管理
- Dolibarr 必須適用於:
- 所有作業系統(Windows, Linux, MACOS...)
- PHP 7.1.0+(需要DateTimeZone.getOffset、php-intl等函數)(除了數據庫訪問模塊之外,必須可以在沒有任何PHP擴展模塊的情況下運行)。
- Mysql 5.1+
規範版權聲明
- 所有 PHP 文件必須以如下所示的標題開頭。
<?php
/* Copyright (C) YYYY John Doe <email@email.com>
*
* Licence information
*/
...
當編輯項目的現有文件時,必須在他人的版權行下添加自己的版權行。
規範 PHP 代碼
PHP
- Dolibarr 是用PHP編寫的,支持所有高於 7.1.0+的 PHP 版本(需要 DateTimeZone.getOffset、 php-intl等函數)。 所有文件的擴展名必須為 .php
- 對PHP超全局變量的調用必須通過專用運算符,如:$_COOKIES、$_SERVER、$_ENV。但是要獲取$_GET或$_POST的內容,必須使用 Dolibarr 函數 GETPOST() 或 GETPOSTINT() 。
在PHP中被棄用的其他運算符($HTTP-SERVER-GET,…)不能再使用。因此,即使選項 register-long-arrays 設置為off,代碼也必須能正常工作。此外,當PHP選項 register-globals 設置為off(PHP推薦)或 register-globals 設置為on(許多安裝的默認設置)時,代碼也必須能正常工作。
- 請勿使用 PHP_SELF,請改用 $_SERVER["PHP_SELF"]。此外,Dolibarr框架調用了一個例程,以使$_SERVER["PHP_SELF"]的內容更安全(在main.inc.php文件中,因此是在任何業務處理之前)
- 當多個變量必須初始化為相同的值時,必須使用多個單獨的聲明(由;分隔)語句:
$var1=1;$var2=1;$var3=1;
而不是
$var1=$var2=$var3=1;
後一種情況下更慢。
其效率較低。
- 字符串中調用變量時,變量必須在引號之外。
print 'My text show my '.$variable.' !';
- 注釋必須遵循C語法,即對單行注釋使用雙斜槓,對代碼塊注釋使用斜槓星號。
/* 块注释
*
* 块注释结束
*/
$monobjet = new MonObjet($db);
$result=$monobjet->fetch($idobject);
for ($i = 1 , $i < 2 ; $i++)
{
// 单行注释
print $i;
}
- 函數必須在成功時返回大於等於0的數字,在錯誤時返回小於0的數字。
- Dolibarr核心代碼中沒有死代碼(不被使用)(僅由外部模塊使用的代碼必須包含在外部模塊中)。
- 使用"include_once"加載任何包含函數或類定義的文件(如 *.class.php 和 *.lib.php),使用 "include"加載任何模板文件或包含HTML和PHP混合代碼的文件(如 *.inc.php 和 *.tpl.php)。
- 要使用的編碼風格是PSR-12 (https://www.php-fig.org/psr/psr-12/)。僅強制執行「必須」的規則。另請注意以下要點/例外:
- 行的長度:PSR-12規定行長度的「軟」限制為120個字符。事實上,最好是用長行聲明數據而不是長頁面,因為該代碼不包含任何有趣或有用的邏輯,但「硬」限制為1000個字符。超出此範圍的代碼將導致集成平台的持續報錯。
- 允許使用制表符:另一個例外是我們沒有系統地用空格替換制表符。使用制表符對大多數編輯器/開發人員來說更為方便。此外,使用空格也會破壞某些自動格式化功能(如某些版本的Eclipse的自動格式化功能)。目前,最好的設置是「保持空格/制表符不變」,但是,您可以啟用「刪除行尾的空格」選項。
- 注意 1:遵循以下規則尤為重要:
- 文件必須用Unix格式(LF)保存,而不是Windows(CR/LF)格式。Unix格式兼容所有作業系統,如Unix、Windows、Mac,但是Windows文本文件格式在Unix下的某些PHP中會出現問題。
- 不要使用PHP智能標籤。PHP代碼部分必須以 <?php 開頭
- 注意 2:您可以使用文件 dev/setup/codesniffer/ruleset.xml 作為規則文件,使用 PHPCodeSniffer 檢查代碼規範。
- 注意 3:您可以使用文件 dev/setup/eclipse/PSR-12 [built-in].xml 作為規則文件來配置 Eclipse 的語法格式化。
- 注意 4:您會注意到,當前代碼不一定遵循所有這些規則。原因是歷史性的。這條規則是在這個過程中制定的,所以如果你發現任何代碼不符合這個標準,不要猶豫,糾正它。(請記住2個例外)。
類和屬性結構
在不同的類中有一些相同的屬性。為了避免使用不同的名稱,我們將使用以下PHP屬性名稱:
- entity that is id for the multicompany feature - date_creation: date of creation - date_modification: date of last modification (often field tms in database) - date_validation: date validation - fk_user_creation: id of User of creation - fk_user_modification: id of User of last modification - fk_user_validation: id of User of validation - import_key contains the import code YYYYMMDDHHMMSS, if record was loaded with a mass import.
- entity 多公司功能的ID
- date_creation: 創建日期
- date_modification: 上次修改日期(通常是數據庫中的tms)
- date_validation: 確認日期/驗證日期
- fk_user_creation: 創建者的ID
- fk_user_modification: 上次修改者的ID
- fk_user_validation: 確認者/驗證者的ID
- import_key 如果記錄是用批量導入加載的,則存放導入編號 YYYYMMDDHHMMSS 。
規範 SQL
DDL 文件格式
每張表必須有兩個包含數據庫結構定義(DDL)的文件。
- 第一個文件定義表及其字段。文件名包含表名,例如:llx_mytable.sql
還將為每個字段添加注釋以解釋其含義。
- 第二個文件定義所有外鍵、性能索引或其他唯一性約束,文件名類似於:llx_mytable.key.sql。
對於所有標準模塊,這些文件必須存儲在目錄 install/mysql/tables 中;對於外部模塊,這些文件必須存儲在目錄 mymodule/tables 中。
示例:用於創建表 llx_mytable 的文件 llx_mytable.sql
-- ===========================================================================
-- Copyright (C) 2013 Author <email@author.com>
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-- ===========================================================================
create table llx_mytable
(
rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
ref varchar(30) NOT NULL, -- object reference number
entity integer DEFAULT 1 NOT NULL, -- multi company id
ref_ext varchar(255),-- reference into an external system (not used by dolibarr)
field_one integer,
field_two integer NOT NULL,
fk_field integer,
field_date datetime,
datec timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,, -- creation datetime
tms timestamp, -- update time stamp
fk_user_author integer NOT NULL, -- Author, foreign key of llx_user
fk_user_mod integer NOT NULL, -- Last updater, foreign key of llx_user
import_key varchar(14) -- Use by import process
)type=innodb;
示例:用於為表 llx_mytable 創建主鍵/索引的文件 llx_mytable.key.sql
-- ===========================================================================
-- Copyright (C) 2013 Author <email@author.com>
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
-- ===========================================================================
ALTER TABLE llx_mytable ADD UNIQUE uk_mytable_field (field_one, field_two);
ALTER TABLE llx_mytable ADD CONSTRAINT fk_mytable_fk_field FOREIGN KEY (fk_field) REFERENCES llx_matablepere (rowid);
表和字段的結構
- 所有表都有前綴,用以避免與其它項目的命名衝突。目前,前綴可在安裝時更改。其默認值為 llx_ 。
- 表的結構
當您創建一個新表時,建議使用與其他Dolibarr表相同的命名約定。必須要有以下字段:
- rowid INTEGER AUTO_INCREMENT PRIMARY KEY 技术ID - entity INTEGER default 1 多公司功能的ID - date_creation datetime 创建日期 - tms timestamp 上次修改日期(数据库自行管理,无需通过代码管理) - import_key 如果数据是批量导入的,则保存导入编号YYYYMMDDHHMMSS - status smallint 存储对象的状态
可能字段:
- fk_user_creat integer 创建者的ID(或者是 fk_user_author) - fk_user_modif integer 修改者的ID - fk_user_valid integer 确认者/验证者的ID(如果适用) - fk_soc integer 合伙方的ID(如果适用)
備註字段:
- note_private text 对象的私有备注 - note_pubic text 对象的公开备注
或者
- note text 备注(如果不需要区分私有或公开)
- 字段類型:
在實踐中,為了與所有國家/地區對金額要求的任何準確性兼容、與任何數據庫語法兼容、以及與Dolibarr升級框架兼容,我們將僅使用以下類型:
- integer 用于任何整数、ID或外键(bigint 可用于非常大的表ID)
- smallint 用于任何小尺寸的整数(如存储状态代码)或布尔值 - double(24,8) 用于任何金额 - double(6,3) 用于增值税税率 - real 用于数量 - varchar 用于字符串(包括长度为1的字符串),char类型已被逐渐弃用 - timestamp 用于要自动更新的日期+时间字段 - datetime 用于日期+时间字段 - date 用于日期字段 - text or medium text 用于大字段 (此类字段上不允许有索引)
所有的業務規則必須位於同一個位置,是在PHP代碼中,而不是在客戶端或數據庫中,這就是為什麼 enum 類型也不允許使用。
由於兼容性的原因,其他類型是不允許使用的。
主鍵
表的主鍵必須是 rowid 。
目前有一些舊錶不符合此規則(如 表llx_c_actioncomm 的主鍵是id),這是歷史原因,這種情況不應該再發生。
外鍵
外鍵的名稱必須以前綴 fk_ 開頭,後跟表名(即引用表的名稱)(這是必需的,以避免項目中的名稱衝突。因為在某些DBMS中,如Postgresql中,即使是不同的表中也不允許名稱重複),再後跟字段名(即引用字段的名稱)(這是為了允許在同一表中有多個外鍵)。
示例:fk_facture_fourn_fk_soc 是表 llx_facture_fourn 中的外鍵,建在字段 fk_soc 上(引用另一個表中的 rowid 字段)
注意:如果您正在開發自己的外部模塊,則您的表中不應該有指向 Dolibarr 標準模塊表的外鍵。因為這將破壞 Dolibarr 的升級,修復,備份和還原工具,也可能破壞標準模塊的功能。
替代鍵
有時,我們需要主鍵以外的其它唯一索引。因此,還可以創建一個替代主鍵的唯一索引。這樣的索引名稱以前綴 uk_ 開頭,後跟表名(這是必需的,以避免項目中的名稱衝突。因為在某些DBMS中,如Postgresql中,即使是不同的表中也不允許名稱重複),再後跟描述鍵的後綴(這是為了允許在同一表中有多個唯一索引)。
示例:uk_societe_code_client 是表 llx_societe 在 code_client 字段上的一個唯一索引
性能索引
某些字段通常用作搜索、排序或連接條件。在這種情況下,應定義性能索引用以提高性能。此類索引將以前綴 idx_ 命名,後跟表名、再後跟索引所在字段的名稱。
示例:idx_societe_user_creat 是表 llx_societe 在字段 user_creat 上的性能索引。
SQL編碼規則
- 使用別名/字段命名
在進行 select 時,可以使用別名來簡化查詢的編寫和閱讀:
select chp1, chpxxx2 as chp2 from table2 as t1, table2 as t2 where t1.chpx = t2.chpy
但是,不應在 update 請求中使用別名,因為它們與 MySQL 3.1 不兼容。
- 禁止使用 SELECT * !每個 SELECT 都必須指定要檢索的字段的完整列表。這有助於避免混淆。最重要的是,這使代碼的重新生成更加容易,並使變更對字段的影響分析成為可能。示例:
SELECT field_a, field_b, field_c FROM table_1 WHERE field_d = '$id'
- 在 SQL insert 時,必需給字段加引號,但不包括要存儲在 double 或 real 類型字段中的金額數字。浮點數字上的引號有時會導致其存儲了不同的值。例如,如果字段類型為 double(24,8),則插入412.62時實際上是將值412.61999512(由於隱式的字符串 - 數字轉換)存儲到數據庫中。只有PHP可以看到412.61999512。其他工具將看到412.62,這使其看上去沒有問題。但PHP是正確的,數據庫中確實存在錯誤的值。通過刪除數字上的引號,就不會出現此問題。
示例:
Good: INSERT INTO table_1 (field_txt, field_num) VALUES ('txt', 412.62)
Bad: INSERT INTO table_1 (field_txt, field_num) VALUES ('txt', '412.62')
請注意,浮點數的問題是普遍性的,不僅是在數據庫訪問上,而且在處理實數時,所有語言都存在浮點數問題。因此,一旦受到影響,就必須使用 price2num 函數清除浮點問題。根據需要,第二個參數設置為:「MU」(如果是單價)、「MT」(如果是總價)或「MS」(其他情況),具體取決於數字的使用情況。另請參閱下面的「實數、金額和計算」一章。
- SQL命令中不允許使用SQL日期函數NOW、SYSDATE、DATEDIFF。如果可能的話,還必須避免使用MONTH、YEAR。如果必須在字段中使用當前日期,則該值必須來自PHP,例如通過 dol_now() 函數,而不是來自數據庫引擎。原因如下:
- 為了更好的代碼可移植性
- 正確管理TimeZone,並且不依賴於數據庫的TimeZone(參考時區是PHP時區,數據庫時區可能與此不同,因此日期轉換必須在PHP端進行)。
- 更好的性能(請參閱下面的示例和評論)
示例,不要:
$sql="SELECT rowid FROM table where datefield = NOW()";
要:
$sql="SELECT rowid FROM table where datefield = '".$this->db->idate(dol_now())."'";
示例,不要:
$sql="SELECT rowid FROM table where DATEDIFF(table.datefield, NOW()) > 7";
要:
$sql="SELECT rowid FROM table where datefield < '".$this->db->idate(dol_now() - (7 * 24 * 3600))."'";
此規則的另一個優點是,查詢可以從索引使用中獲益,因為它將日期字段與固定值進行比較。當使用 datediff 時,因在比較之前對字段進行了處理,使得數據庫無法使用該字段的索引。與不使用 datediff 的方案相比,其性能非常差。
- 禁止使用 WITH ROLLUP
不應使用此語句:不同數據庫的處理方式不同。此外,使用 WITH ROLLUP 會破壞返回數據的純度。通過該指令生成小計的中間聚合可以在PHP的幫助下輕鬆完成,並有助於保持返回的數據表乾淨(不會被人工插入的數據損壞)。除了標準化程度不高之外,通過在數據中插入計算結果,給出的結果是數據和聚合業務計算的混合結果,對代碼的理解變得複雜,甚至在這些聚合上的代碼演變變得複雜時更可能難以理解。
- 使用 $db->ifsql 替代 IF SQL
不要在SQL查詢中使用IF。請改用 $db->ifsql() 方法,您將創建一個與所有SQL數據庫兼容的SQL IF。
- 不使用 DELETE CASCADE 和 UPDATE CASCADE
禁止使用此類SQL語句,因為它們繞過了應用程式的業務規則,給開發人員帶來了很多失控的麻煩。例如,如果表A和B之間存在 DELETE CASCADE,當應用程式運行代碼刪除A中的記錄時,表B中的子項也將被刪除。如果一個PHP Dolibarr觸發器(例如,由外部模塊提供)存在於B表記錄的刪除操作上(例如,驗證刪除或執行附加操作),則 DELETE CASCADE 將在不執行PHP Dolibarr觸發器的情況下僅執行刪除B表記錄的操作,跳過了由外部模塊的PHP Dolibarr觸發器執行的驗證或附帶操作。因此,所有業務規則都必須在同一端(PHP伺服器端)實現,這就是為什麼不允許在數據庫端實現業務規則的原因(與下一節關於數據庫觸發器的情況相同)。
請注意,外部模塊可以將它們(DELETE CASCADE 和 UPDATE CASCADE)用於Dolibarr表及其模塊表之間的連結,但最好的推薦解決方案是在刪除父表的記錄時使用 Dolibarr PHP 觸發器來實現其子表記錄的刪除。
使用數據庫觸發器
Dolibarr的代碼中不允許使用數據庫觸發器,因此您應該不會遇到數據庫觸發器的問題。
數據庫觸發器不可移植。在升級數據庫版本時,兼容性可能會被破壞,數據庫觸發器通常不包含在備份工具中,數據庫觸發器需要您可能沒有(也不應該有)的操作公共安全的數據庫權限,數據庫觸發器還意味着將應用程式的一些「業務規則」放置在了數據庫中,其中大多數其他規則由代碼本身管理(這可能會產生衝突或無法使用步進調試程序調試代碼),數據庫觸發器使用數據庫的時區執行代碼,而不是使用可能不同的應用程式的參考時區(生成數據庫偏移量)...
我們有很多理由不建議在您自己的開發中使用數據庫觸發器。
使用數據庫觸發器可以在進行開發時節省您的時間,但這是一種非常糟糕的做法,因此被禁止,因為它與Dolibarr團隊所期望的高質量代碼不兼容。使用數據庫觸發器的外部模塊在市場上也可能被拒絕,因為它不能為超過50%的用戶正常工作。
基本遷移規則
結構遷移腳本是 install/mysql/migration/a.b.c-x.y.z.sql 類型的文件。在從n到n+1的遷移文件中,只有在從n+1版本遷移到n+2版本時,才會銷毀在n+1中不再使用的表(drop table)。這是為了在版本升級期間始終保留過時的表,以便在發生升級錯誤的情況下能回退到原版本。
Mysql/MariaDB 特性
- 表必須使用 InnoDB 格式聲明
實際上,此格式支持外鍵、附加到外鍵的任何約束以及使數據在表之間具有一致性所需的事務完整性概念。
這保證了 Dolibarr 操作在表之間保存了正確的數據,即使事務修改了不同的表也是如此。
- 即使 Mysql 的 strict 選項被激活,Dolibarr也必須能正常工作
要啟用它(強烈建議在Dolibarr開發中使用),請在MySQL伺服器配置文件(my.cnf 或 my.ini)中添加以下行:
sql-mode="STRICT_ALL_TABLES,ONLY_FULL_GROUP_BY,NO_ZERO_DATE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
PostgreSQL 特性
只需要維護 MySQL 的文件。其他數據庫的文件由數據庫的Dolibarr驅動程序「動態」轉換。
有一個例外,就是 SQL "UPDATE FROM":
MySQL 語法:
UPDATE table_taget as target, table_source as source SET fieldtarget=source.fieldsource
WHERE source.rowid=target.rowid;
PgSQL 語法:
UPDATE table_taget as target SET fieldtarget=source.fieldsource
FROM table_source as source WHERE source.rowid=target.rowid;
在dolibarr內核中沒有本地化「update from」。但對於您的模塊,您應該這樣做:
if ($this->db->type=='pgsql') {
$sql="UPDATE table_taget as target SET fieldtarget=source.fieldsource
FROM table_source as source WHERE source.rowid=target.rowid";
} else {
$sql= "UPDATE table_taget as target, table_source as source SET fieldtarget=source.fieldsource
WHERE source.rowid=target.rowid";
}
規範 HTML
語法規則
- 使用的 HTML 必須符合 HTML 標準,而不是 XHTML。HTML標記中的所有屬性都必須 小寫 ,並使用 雙引號 括起來。
- href 連結必須是絕對路徑,請使用函數 dol_buildpath() 從htdocs目錄的相對路徑(對於外部模塊)或使用 DOL_URL_ROOT(對於核心代碼)獲取絕對路徑。對於img標記,必須調用 img_picto() 。
示例:
print '<a href="'.dol_buildpath('/mydir/mypage.php').'">'.img_picto('Texte alt','namepictopng','').'</a>';
- HTML tables 必須具有沒有強制寬度的列,但包含非可變長度信息的列除外。例如,帶有 picto 的列可以強制為with=「20px」。
在其他情況下,應避免強制列的寬度。原因是,在大多數情況下,瀏覽器在自動設置正確的列寬度方面比手動強制寬度做得更好。並且無論屏幕解像度如何,它都可以正常工作。
- 應避免在PHP頁面中使用Javascript/ajax和Java腳本。但是,如果需要包含 javascript代碼,則必須先對 "$conf->use_javascript_ajax" 進行條件判斷:
if ($conf->use_javascript_ajax) {
... // php code generating javascript here
}
- 不應使用 Windows 彈出窗口,但工具提示除外(並且仍受上述條件的限制)。
- 外部腳本是用Perl編寫的,如果它們不能用PHP編寫的話,則不禁止使用其他語言,但必須事先在開發人員的郵件列表中討論。
外部模板框架?
有大量的框架可以提供HTML頁面的模板語言(smarty,twig,...)。我們所知道的最好和更快的只有被稱為「PHP」的,並且由於Dolibarr已經依賴於PHP,因此沒有必要引入對第三種語言的依賴來製作模板。
所有模板框架都只是預處理器,BEFORE PHP。它不可能比單用PHP更快。當我們說「速度提高是因為模板框架使用了緩存」時,你必須了解一個事實「框架花費的超時時間是由緩存減少的,而不是處理 PHP 文件的速度」。我們應該說「任何模板框架在 PHP 上都有重載,模板框架的緩存只是減少了使用這個框架的減慢效果」,而不是「速度提高了,與純 PHP 相比總是降低的」。
最重要的是,通過做一個「.tpl.php」文件,完全可以擁有一個模板系統,該文件不包含任何邏輯代碼,而只包含HTML和"echo"。我們得到相同的結果(但更快、更容易開發,因為它不需要在調用每個模板之前完成所有的 setXXX)。所有變量(由包含模板的代碼所知的)都會自動被 PHP 模板頁面所知。沒有忘記集合的風險,減少了大量代碼行,也節省了大量時間和開發錯誤。
此外,在以下一種情況下,保證代碼和 HTML 輸出之間 100% 的隔離是有趣的:當團隊構建設計與團隊構建邏輯代碼完全不同時,如果您構建不太複雜的頁面,並且不需要太多的 Ajax 功能(這需要知道代碼是如何工作的)。
而且這種情況肯定不是Dolibarr的未來(團隊經常會是一樣的,Ajax會越來越多,事件,如果我希望不是太多,屏幕越來越依賴於動態或上下文事件,很難用一個簡單的模板來做到這一點,而不將模板轉換為具有高級代碼的頁面)。還要設計現在在 CSS 端而不是 PHP 端進行更多管理。
過去,Dolibarr已經嘗試過像smarty這樣的模板系統。如果在紙上這個想法是好的,我們很快意識到發現錯誤變得令人頭疼,編碼成為尋寶,維護和開發做得太慢(與以前輸出模板進入每個 PHP 文件的「視圖」部分的情況相比),我們忘記了這個想法。實用主義造就了規則。
不使用外部模板系統還有很多其他原因,使用它們的所有參數也是使用 HTML/PHP 作為我們的模板系統的最佳參數。我們只是要求在控制器(PHP 頁面的第一部分就在 /* Action */ 行之後)和視圖(/* View */ 注釋之後的第二部分)之間保持分離。
規範 Dolibarr 和代碼框架
代碼框架
為了使代碼標準化並加快Dolibarr中新組件的開發,可以在 htdocs/modulebuilder/templates 目錄中找到準備好的框架文件。
例如:
- 模塊描述示例 myModule.class.php
- 創建新類的代碼示例 skeleton_class.class.php
- 創建新頁面的代碼示例 skeleton_page.php
- 創建要在命令行上運行的腳本的代碼示例 skeleton_script.php
- ...
請注意,PHP代碼生成器(ModuleBuilder模塊)也使用這些框架,該模塊在Dolibarr模塊開發章節中有描述,用以提高您的開發速度。
日期和時區
Dolibarr是一個多用戶和多地域應用程式。因此,應以正確的格式存儲日期。為避免轉換出現問題,應遵循以下規則:
- 內存中的日期必須採用GMT時間戳格式。
- 存儲在數據庫中的日期包含來自PHP伺服器本地時間請求中提交的日期的時間戳。這不適用於數據庫自動更新的日期(數據庫中的 tms 字段)。
例如:1970年1月1日巴黎時間的3點鐘(TZ=+1)= 1970年1月1日格林威治時間的2點鐘(TZ=0),其存儲在內存中的值都是7200,並將以字符串「19700101030000」的形式提交給數據庫(PHP將其轉換為TZ的時間值,而數據庫再將其轉換回PHP伺服器時區的時間)。
因此,所有 select 方法都必須通過調用 db->jdate 函數來轉換日期字段,將其從數據庫讀取的TZ格式日期字段('19700101030000')轉換成GMT時間戳格式,以便以GMT時間戳格式檢索內存中的信息。而在生成SQL語句時,所有 insert 方法都必須通過調用 db->idate 函數將已知內存格式日期轉換為字符串格式日期(參見框架生成的示例)。
- 數據庫自動更新的日期字段(數據庫中的 tms 字段)存放根據數據庫時區進行轉換的時間戳。select 方法通過調用 db->jdate 以GMT時間戳格式檢索內存中的數據。如果數據庫時區與PHP時區不同(其中一個設置不正確),則創建日期和修改日期之間可能會存在差異。
- PHP中的日期操作必須使用 dolibarr 中的日期函數:dol_now()、dol_mktime()、dol_stringtotime()、dol_getdate()、dol_time_plus_duree()。您還可以在文件 date.lib.php 中找到其他現成的可用函數。
UTF8/ISO 編碼
Dolibarr 按以下方式存儲數據:
- 在數據庫中,數據為 UTF8 或 ISO 格式。這取決於數據庫的pagecode(頁面代碼),本選項是在創建數據庫時設置的。無論如何,Dolibarr的數據庫訪問驅動程序(在 lib/database 中)在讀取和插入時處理UTF8的轉換。
- 內存數據存儲為 UTF8(PHP 對象的實例)格式。
- 屏幕上顯示的網頁採用 UTF8 格式。
浮點數,金額和計算
在PHP中,就像在其他語言(如Java)中一樣,非整數數據(float、real、double)對於計算來說是不可靠的。 例如:
print 239.2 - 229.3 - 9.9;
你不會得到零,而是一個非常小的負10的冪。如果你得到零,你可以找到其他不起作用的例子。
浮點數的問題是一般性的,計算實數的結果變量必須由 price2num() 函數系統地清理,根據需要將第二個參數設置為「MU」、「MT」或「MS」(參見函數文檔)。
print price2num(239.2 - 229.3 - 9.9, 'MT');
如果計算的數據不是一個價格,那麼使用 MU、MT、MS 就沒有意義了,您必須使用 round() 函數。
表的創建
不要在使用時創建表,即首次使用模塊時。如果您正在創建的模塊使用的表不是 Dolibarr 代碼中的標準表,請確保遵循 模塊開發 教程,該教程解釋了如何在模塊激活時創建表,而不是在模塊使用期間創建表(見上文)。
比較版本
如果您的代碼需要根據 Dolibarr 版本的不同而執行不同的操作,您可以使用以下技巧來判斷和比較版本:
$version=preg_split('/[\.-]/',DOL_VERSION);
if (versioncompare($version,array(5,0,-4)) >= 0) { //mycode for 5.0 only; } // For dolibarr 5.0.* (the -4 means we include also alpha, beta, rc and rcX)
但是這個解決方案需要 include 函數
$version=preg_split('/[\.-]/',DOL_VERSION);
if (versioncompare($version,array(5,0,-4)) >= 0) { //mycode for 5.0 only; } // For dolibarr 5.0.* (the -4 means we include also alpha, beta, rc and rcX)
。另一種替代解決方案是執行以下比較操作:
if ((float) DOL_VERSION >= 5.0) { //mycode for 5.0 only; } // For dolibarr 5.0.*
日誌
使用函數在代碼中添加日誌:
dol_syslog($yourmessage, LOG_INFO|LOG_DEBUG|LOG_WARNING|LOG_ERR);
工作目錄
如果需要在代碼中創建工作目錄,請將該目錄引用為 DOL_DATA_ROOT.'/mymodule' 。
可以使用以下代碼在運行時使用代碼創建目錄:
$mymoduledir=DOL_DATA_ROOT.'/mymodule';
dol_mkdir($mymoduledir);
如果需要包含臨時數據的目錄,則該目錄應為 DOL_DATA_ROOT.'/mymodule/temp' 。
設計模式與面向對象編程
GoF 設計模式
由四人幫(Gang Of Four)定義的設計模式(參見維基百科上的 設計模式 )。
創建型模式 (GoF)
在Dolibarr中,沒有實現特定的創建型設計模式。有一些對象在設計思想上接近 Singletons 和 Fabriques,但在語法上不一致,以便儘可能與不是純對象語言的PHP兼容。
結構型模式 (GoF)
在Dolibarr中,沒有實現特定的結構型設計模式。
行為型模式 (GoF)
在Dolibarr中,沒有實現特定的行為型設計模式。
企業設計模式 (Martin Fowler)
組織代碼的方法
Martin Fowler 確定了三種組織代碼的方法,稱為模式:
- 事務腳本(每個用戶操作的原始碼都是線性的)
這是過程語言中使用的老式模式。
缺點:代碼冗餘。需要了解物理模型以開發業務部件。
- 域模型
這個概念可用於對象語言。業務流程(必須在此之前確定)是對象類的基礎。
缺點:模型維護起來非常複雜。
- 表模塊
介於前兩種方法之間的中間方法,其中每個數據庫表都只有一個類實例。
-> 如代碼框架所示(見前面章節),Dolibarr基於 表模塊 的原理設計。
業務邏輯 - 數據之間的交互 (ORM)
有三種交互模式:
- 表和行 Data Gateway
這是最簡單的方法。我們為每個表創建一個類,每個類都是到相應表的橋樑,並在該表上提供CRUD方法(Ceate, Read, Update, Delete)。其次,類的實例是表的記錄。類僅包含表行或列的訪問代碼。最後,必須將業務代碼添加到其他類中。這些表類和業務類之間的連結關係必須是已知的,或可從開發人員的知識中推斷出來的。
示例:此模式由某些ORM框架使用,如 iBatis(http://ibatis.apache.org/)。
- Active Record
與前一個相同,但允許在類上添加一些業務函數,前提是這些函數非常特定於表或記錄所承載的業務概念。這是提供最佳 功能/複雜性 比的模式。
示例:這是Dolibarr和許多其他PHP應用程式開發的首選模式,這些應用程式有自己的框架和開發實踐。
- 數據 Mapper
類表示問題的實體,而不是數據。所以您必須兩次,三次 ... 地調用 Mapper 類訪問數據。這種模式在理論上更「純粹」,因為它更接近業務,但也有非常複雜和難以在現實中維護的缺點(觀察到的實踐經驗不符合理論的承諾)。
示例:這是 ORM 框架 Propel 的選擇(http://propel.phpdb.org/trac/)。因此,它可以在基於此ORM的大量的應用程式中找到。
-> 對於Dolibarr開發,建議使用 Active Record 連結模式,該模式提供了接近業務模型的優點,而不具有複雜性,也不過度隱藏技術。正是在這種模式下,開發、代碼理解以及技術和/或功能維護似乎最有成效(然而,這是純粹主義者和實用主義者之間的一場永恆的辯論,沒有人真正正確,因為一切都取決於要實現的目標)。
圖形界面設計模式(MVC)
用戶界面有多種設計模式(MVC、MVP、MVVM)。許多人將此設計模式與使用模板框架的設計模式相混淆。這無關緊要。模板框架(見前面章節)僅與這些設計模式的「V」部分有關。
-> Dolibarr使用MVC設計模式。M部分由包含用於訪問表的CRUD(創建-讀取-更新-刪除)方法的DAO類實現。
許多框架選擇將C(控制器)代碼部分和V(視圖)代碼部分放在單獨的文件甚至目錄中。從理論上看,似乎更容易維護,但實際上,我們必須不斷地在n個文件之間切換,因C與V緊密相連。同樣,在Dolibarr中,隔離是通過一個簡單的注釋塊完成的。因此,C(控制器)代碼位於下述標記處:
/* * Actions * /
它後面是V (Vue)(視圖)代碼,通過如下標記與C代碼隔離開:
/* * View * /
這種隔離模式對新增需求的開發產生了中性影響,但大大減少了維護和修復錯誤所需的時間,因此不打算切換到另一種類型的隔離(確實,現實生活中的觀察結果並不總是學術理論所期望的)。重要的是,C和V之間總是存在隔離,無論這種隔離是如何實現的,只要它存在(提醒:不要將C和V的隔離概念與為視圖提供數據的代碼和格式化視圖的代碼的分離概念混淆,這是視圖設計模式的分離原則,請參閱下一節)。
視圖 - 展現的設計模式
有專門用於視圖設計模式的框架,這部分涉及格式化用戶看到的屏幕。
舊版本的Dolibarr使用Smarty。這個框架是一個已經非常強大的屏幕格式化框架的覆蓋層,名為...PHP。由於優點遠小於缺點,它已被放棄,取而代之的是PHP模板(.tpl.php文件),PHP模板仍然是最簡單、最通用、最可擴展和最高效的展現框架。
它唯一的缺陷是,它不能100%保證開發人員不會在視圖中集成與展現無關的邏輯(除了用於生成、格式化、HTML輸出、判斷邏輯或循環的其它代碼)。然而,這一點應該由項目經理代碼驗證機制控制。
另一方面,正是經驗和「在現場」觀察到的更高生產率,促使人們放棄了外部展現框架,轉而使用PHP框架。