天天看點

雲客Drupal源碼分析之資料庫Schema及建立資料表

本主題是《雲客Drupal源碼分析之資料庫系統及其使用》的補充,便于查詢,是以獨立成一個主題

講解資料庫系統如何操作Schema(建立修改資料庫、資料表、字段;判斷它們的存在性等等),以及子產品如何通過一個結構化數組去建立自己用到的資料表

官方的Schema文檔位址是:https://www.drupal.org/node/146843

官方API文檔:https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%21database.api.php/group/schemaapi/8.2.x

此外在\core\lib\Drupal\Core\Database\database.api.php檔案中也有詳盡的注釋。

資料表定義:

程式要去建立資料表,那麼需要先得到一個關于資料表的定義,才能據此建立,在drupal中這個定義是一個嵌套數組,結構如下:

$schema = array(
  'description' => 'The base table for nodes.',
  'fields' => array(
    'nid'       => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
    'vid'       => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE,'default' => 0),
    'type'      => array('type' => 'varchar','length' => 32,'not null' => TRUE, 'default' => ''),
    'language'  => array('type' => 'varchar','length' => 12,'not null' => TRUE,'default' => ''),
    'title'     => array('type' => 'varchar','length' => 255,'not null' => TRUE, 'default' => ''),
    'uid'       => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
    'status'    => array('type' => 'int', 'not null' => TRUE, 'default' => 1),
    'created'   => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
    'changed'   => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
    'comment'   => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
    'promote'   => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
    'moderate'  => array('type' => 'int', 'not null' => TRUE,'default' => 0),
    'sticky'    => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
    'translate' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
  ),
  'indexes' => array(
    'node_changed'        => array('changed'),
    'node_created'        => array('created'),
    'node_moderate'       => array('moderate'),
    'node_frontpage'      => array('promote', 'status', 'sticky', 'created'),
    'node_status_type'    => array('status', 'type', 'nid'),
    'node_title_type'     => array('title', array('type', 4)),
    'node_type'           => array(array('type', 4)),
    'uid'                 => array('uid'),
    'translate'           => array('translate'),
  ),
  'unique keys' => array(
    'vid' => array('vid'),
  ),
  // For documentation purposes only; foreign keys are not created in the
  // database.
  'foreign keys' => array(
    'node_revision' => array(
      'table' => 'node_field_revision',
      'columns' => array('vid' => 'vid'),
     ),
    'node_author' => array(
      'table' => 'users',
      'columns' => array('uid' => 'uid'),
     ),
   ),
  'primary key' => array('nid'),
);
           

下面解釋各個鍵的意思:

  'description': 一個非标記字元串,用來描述表的目的.引用到其他表時,其他表名用{}包裹,如"儲存标題資料為表{node}."
    'fields': 一個關聯數組('字段名' => 規格描述) 它描述了表的列,規格描述也是一個數組. 以下規格參數被定義:

        'description': 一個非标記字元串,用來描述字段的目的.引用到其他表時,其他表名用{}包裹,如"儲存标題資料為表{node}."
        'type': 常用資料類型: 'char', 'varchar', 'text', 'blob', 'int', 'float', 'numeric', or 'serial'. 大多數類型映射到相應資料庫引擎指定的類型.  'serial' 代表自增字段. 對于MySQL來說是 'INT auto_increment'. 特殊類型'varchar_ascii'可用來限制字段機器名為US ASCII字元集.
        'mysql_type', 'pgsql_type', 'sqlite_type'等等:如果你要的類型在上面沒有,你可以為特殊資料庫指定類型,這時可以省略上面的類型參數,但這樣可能有風險,通常可以使用"text"來代替
        'serialize': 布爾值,指定字段是否應該存儲序列化資料
        'size': 資料大小: 'tiny', 'small', 'medium', 'normal', 'big'. 這是一個可儲存最大值的暗示,指導儲存引擎使用何種類型(比如在 MySQL中有 TINYINT、INT 、 BIGINT). 預設值是'normal'(在MySQL中是INT, VARCHAR, BLOB,). 對于各種資料類型而言,不是所有大小均可用,可能的組合見DatabaseSchema::getFieldTypeMap()
        'not null': 布爾值,true表示no NULL值.預設為false.
        'default': 字段的預設值,注意資料類型,在php中'', '0',  0 是不同的. 為'int'字段指定'0'将不工作(注意引号),因為'0'是字元串,不是整數0
        'length': 類型'char', 'varchar' or 'text' 字段的最大長度.對于其他字段類型将忽略此值
        'unsigned': 布爾值,指明'int', 'float' and 'numeric' 類型有無符号(正負).預設為FALSE.對于其他字段類型将忽略此值
        'precision', 'scale': 對于'numeric' 字段而言是必須的,precision指明精度(有效數字的總數),scale (小數位位數). 其他類型将忽略
        'binary': 布爾值,指明 MySQL應該強制 'char', 'varchar' or 'text' 字段類型使用大小寫敏感的二進制校對. 對于預設就是大敏感的類型,它沒有影響

    字段定義中除'type'以外的全部參數是可選的,但有些另外: 'numeric' 必須指定'precision' 和'scale', 'varchar' 必須指定'length'

    'primary key': 構成主鍵的多個列數組,一個表中隻能有一個主鍵,但一個主鍵可以包含多個列,稱為聯合主鍵。
    'unique keys': unique鍵的關聯數組 ('keyname' => specification). specification是一個構成unique的列數組
    'foreign keys': 一個關聯數組('my_relation' => specification). 每個定義是一個包含被引用表名及列映射的數組,列映射為('源列' => '引用列'),外鍵不在資料庫中建立,drupal不強制這個
    'indexes': 索引的關聯數組 ('indexname' => specification). 
           

上面的定義有些額外的說明:

primary key:主鍵,一個表中隻能有一個主鍵限制,你可以命名這個主鍵限制,并指定它包含有哪些列,比如一個學生表,可以指定主鍵限制包含班級和學号,那麼在行中班級可以相同,學号也可以相同,但班級和學号不能同時相同,這就是聯合主鍵的作用

unique keys:唯一性,對字段資料進行唯一性限制,可以同時指定多個字段,被指定的這些字段,它們各自的資料不可以重複,各行資料在這個字段必須是唯一的

foreign keys:外鍵,這指明本字段引用到其他表的某個字段,在本字段所儲存的值必須是被引用到的那個字段儲存的值之一,如果插入值時,不在被引用字段内容中,那麼将産生錯誤,比如權限表要儲存使用者ID,那麼這個字段就應該指定外鍵到使用者表的id字段

indexes:索引,主要用于加速資料查詢,提高性能,包含多個列的索引又叫複合索引或者多列索引,條件子句涉及到索引指定的列時,在資料庫内部這些索引被用到,當表很大時能大大提高搜尋速度,表較小則索引展現不出優勢,複合索引指定列時,順序不是随意的,關于這方面知識請看:http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html

在上面的定義中:索引處有個定義是'node_type' => array(array('type', 4)),為什麼不是'node_type' => array('type',),呢?這個參數4是什麼意思?這是指在type這個字段中儲存的資料隻有前4位元組被用于索引内容,加快速度,如果存儲引擎不支援這樣的功能,那參數4将被忽略,這樣的用法同樣适用于主鍵。

有了這樣的表定義就可以建立表了,方法如下:

$con=\Drupal\Core\Database\Database::getConnection($target, $key);
$con->schema()->createTable($table_name, $schema);
           

$con->schema()傳回的是一個schema對象,專門操作schema,裡面提供了許多方法

源碼在\core\lib\Drupal\Core\Database\Driver\中,它是\core\lib\Drupal\Core\Database\Schema.php的子類,

母類提供個資料庫通用功能,子類用以解決資料庫之間的不同(資料庫方言)

Schema對象的常用方法如下:

$con = \Drupal\Core\Database\Database::getConnection($target, $key);
$schema = $con->schema();
$schema->createTable($table_name, $schema); //建立表
$schema->tableExists($table); //判斷表是否存在
$schema->renameTable($table, $new_name);//重命名表
$schema->fieldExists($table, $column); //判斷字段是否存在
$schema->fieldNames($fields);//從一個column specifiers傳回字段名
$schema->changeField($table, $field, $field_new, $spec, $keys_new = array());//修改字段
$schema->addField($table, $field, $spec, $keys_new = array());//添加字段
$schema->dropField($table, $field);//删除字段
$schema->fieldSetDefault($table, $field, $default);//為字段設定預設值
$schema->fieldSetNoDefault($table, $field); //取消預設值
$schema->indexExists($table, $name); //判斷是否存在索引
$schema->addIndex($table, $name, $fields, $spec = array());//添加索引
$schema->dropIndex($table, $name);//删除索引
$schema->addPrimaryKey($table, $fields); //給聯合主鍵添加字段
$schema->dropPrimaryKey($table);//删除主鍵
$schema->addUniqueKey($table, $name, $fields);//添加唯一性限制
$schema->dropUniqueKey($table, $name);//銷毀唯一性限制
$schema->prepareComment($comment, $length = null); //預處理注釋
$schema->getComment($table, $column = null); //擷取字段注釋
           

這些方法的說明請看注釋:\core\lib\Drupal\Core\Database\Schema.php

常用資料庫Mysql資料庫的類型映射如下:

$map = array(
        'varchar_ascii:normal' => 'VARCHAR',

        'varchar:normal' => 'VARCHAR',
        'char:normal' => 'CHAR',

        'text:tiny' => 'TINYTEXT',
        'text:small' => 'TINYTEXT',
        'text:medium' => 'MEDIUMTEXT',
        'text:big' => 'LONGTEXT',
        'text:normal' => 'TEXT',

        'serial:tiny' => 'TINYINT',
        'serial:small' => 'SMALLINT',
        'serial:medium' => 'MEDIUMINT',
        'serial:big' => 'BIGINT',
        'serial:normal' => 'INT',

        'int:tiny' => 'TINYINT',
        'int:small' => 'SMALLINT',
        'int:medium' => 'MEDIUMINT',
        'int:big' => 'BIGINT',
        'int:normal' => 'INT',

        'float:tiny' => 'FLOAT',
        'float:small' => 'FLOAT',
        'float:medium' => 'FLOAT',
        'float:big' => 'DOUBLE',
        'float:normal' => 'FLOAT',

        'numeric:normal' => 'DECIMAL',

        'blob:big' => 'LONGBLOB',
        'blob:normal' => 'BLOB',
        );
           

在子產品安裝的時候怎麼建立資料表呢?

在子產品檔案夾下建立一個檔案,命名為:module_name.install

module_name是子產品名,把此檔案當做php檔案看待,在裡面定義函數hook_schema().

其中hook應該替換為子產品名,比如子產品名叫yunke,該函數名應該為yunke_schema()。

在函數中傳回一個關聯數組,鍵名表示資料庫表名,鍵值為資料表定義數組(見上),格式如下:

function yunke_schema() {
  $table['table_name1'] = $schema;
  $table['table_name2'] = $schema2;
  return $table;
}
           

table_name1和table_name2為要建立的表名,他們的值為上面定義的表定義數組

在子產品安裝的時候将搜尋module_name.install檔案,執行裡面的hook_schema()函數

通過傳回值自動建立這兩個資料表,結構為定義數組控制,在子產品解除安裝的時候自動銷毀這兩個表

子產品更新可能會涉及到資料表的更新、變更等更新操作,

這應該是一個獨立的主題(它包含表定義、配置、資料處理等等),此處不做介紹,在子產品相關主題中講解

我是雲客,【雲遊天下,做客四方】,聯系方式見首頁,歡迎轉載,但須注明出處

繼續閱讀