Declarative Schema Trong Magento2 (Phần 1)

Table of Contents

  1. Lời mở đầu
  2. Tại sao lại sử dụng Declarative Schema
  3. Cấu trúc db_schema
  4. Whitelist
  5. Chế độ DRY RUN
  6. Tổng Kết Phần 1

1.Lời mở đầu

Declarative Schema

  • Trong bài viết trước Tạo Và Chỉnh Sửa Bảng với Setup/Upgrade Script Trong Magento2, chúng ta đã tìm hiểu cách để tạo bảng, cập nhật bảng với các PHP script, như:
    • InstallDataInstallSchema: được chạy vào lần đầu tiên mà module được cài đặt.
    • UpgradeDataUpgradeSchema: được chạy vào mỗi lần upgrade module, dựa vào version của module, để cập nhật bảng và dữ liệu cho bảng.
  • Trước phiên bản Magento 2.3, đây là cách duy nhất để chúng ta làm việc với database. Tuy nhiên, với phiên bản 2.3 được phát hành năm 2018, một trong những thay đổi lớn được giới thiệu đó là Declarative Schema, một cách mới mà chúng ta có thể làm việc với database mà không cần phải viết nhiều script nữa.

2.Tại sao lại sử dụng Declarative Schema

  • Lợi ích của việc sử dụng Declarative Schema là gì? Trước tiên việc chúng ta phải viết những PHP database script như trước đây không phải là một giải pháp hoàn hảo.
  • Bởi vì, những script sẽ apply những thay đổi dựa vào version của module, khi bạn upgrade một module lên vài version so với hiện tại, vì tất cả các script giữa 2 version này sẽ đều được thực thi. Ví dụ bạn có một module với version 1.0.0 và phiên bản mới nhất của module là 1.0.4. Khi bạn upgrade module, những script cho version 1.0.1, 1.0.2, 1.0.3 cũng sẽ được apply!
  • Nhược điểm của cách này là Magento sẽ thực apply những thay đổi một cách mù quáng, chẳng hạn như trong một version, bạn tạo mới một cột trong bảng CSDL, đến version tiếp theo bạn lại xóa nó đi. Declarative Schema sẽ giúp loại bỏ những bước thừa, không cần thiết này.
  • Declarative Schema sẽ khai báo cấu trúc của database, Magento so sánh phiên bản database cuối cùng này với phiên bản hiện tại và chỉ apply những thay đổi giữa phiên bản cuối cùng và phiên bản hiện tại.
  • Đây là một ví dụ lấy từ Catalog/etc/db_schema.xml, định nghiã bảng catalog_product_entity_datetime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<table name="catalog_product_entity_datetime" resource="default" engine="innodb"
comment="Catalog Product Datetime Attribute Backend Table">
<column xsi:type="int" name="value_id" padding="11" unsigned="false" nullable="false" identity="true" comment="Value ID"/>
<column xsi:type="smallint" name="attribute_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Attribute ID"/>
<column xsi:type="smallint" name="store_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Store ID"/>
<column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Entity ID"/>
<column xsi:type="datetime" name="value" on_update="false" nullable="true" comment="Value"/>
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="value_id"/>
</constraint>
<constraint xsi:type="foreign" referenceId="CAT_PRD_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID" table="catalog_product_entity_datetime" column="attribute_id" referenceTable="eav_attribute" referenceColumn="attribute_id" onDelete="CASCADE"/>
<constraint xsi:type="foreign" referenceId="CAT_PRD_ENTT_DTIME_ENTT_ID_CAT_PRD_ENTT_ENTT_ID" table="catalog_product_entity_datetime" column="entity_id" referenceTable="catalog_product_entity" referenceColumn="entity_id" onDelete="CASCADE"/>
<constraint xsi:type="foreign" referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID" table="catalog_product_entity_datetime" column="store_id" referenceTable="store" referenceColumn="store_id" onDelete="CASCADE"/>
<constraint xsi:type="unique" referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID">
<column name="entity_id"/>
<column name="attribute_id"/>
<column name="store_id"/>
</constraint>
<index referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_ATTRIBUTE_ID" indexType="btree">
<column name="attribute_id"/>
</index>
<index referenceId="CATALOG_PRODUCT_ENTITY_DATETIME_STORE_ID" indexType="btree">
<column name="store_id"/>
</index>
</table>

3. Cấu trúc db_schema

  • File db_schema.xml khai báo cấu trúc database của một module được định nghĩa ở
    [Module_Vendor]/[Module_Name]/etc/db_schema.xml

3.1 Thẻ <chema>

  • File db_chema.xml mở đầu với thẻ <schema>
1
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">

3.2 Thẻ <table>

  • Mỗi một file db_schema.xml sẽ chứa một hoặc nhiều node table. Mỗi một node table đại diện cho một bảng trong database.
  • Một node table có thể chứa những attribute sau:
    ATTRIBUTE DESCRIPTION
    name Tên của table
    engine SQL engine. Sẽ là innodb hoặc memory
    resource Phân đoạn CSDL nào mà chúng ta cài đặt bảng. Sẽ là default, checkout, hoặc false.
    comment Comment chúng ta viết cho bảng.
  • Một node table có thể bao gồm 3 loại node con sau:
    • column
    • constraint
    • index

3.3 Thẻ <column>

  • Node column định nghĩa column của một bảng.

  • Một column sẽ có các thuộc tính sau:

    ATTRIBUTE DESCRIPTION
    xsi:type Chỉ định kiểu dữ liệu của cột. Sẽ là những kiểu sau:
    - blob, boolean, date, datetime, decimal, float, int (bao gồm cả smallint, bigint, tinyint), json, real (bao gồm cả decimal, float, double, real), smallint, text (bao gồm cả text, mediumtext, longtext), timestamp, varbinary, varchar
    default Giá trị mặc định của cột khi được khởi tạo. Giá trị này phải cùng kiểu dữ liệu khai báo với xsi:type
    disabled Vô hiệu hóa hoặc xóa cột.
    identity Chỉ định cột là tự động tăng.
    length Chỉ định độ dài của dữ liệu cột. Dùng với các kiểu dữ liệu char, varchar, và varbinary.
    nullable Cột có thể null hay không.
    onCreate Đây là một DDL Trigger cho phép bạn chuyển dữ liệu từ một cột đang tồn tại sang một cột mới. Trigger này chỉ hoạt động khi một cột được tạo.
    padding Size của một cột kiểu integer.
    precision Độ chính xác của một dữ liệu kiểu real.
    scale Số chữ số đằng sau dấu phẩy của một dữ liệu kiểu real.
    unsigned Đối với một cột có dữ liệu kiểu numberic, chỉ định liệu dữ liệu đó có thể là số dương hoặc số âm không hay chỉ có thể là số dương.
  • Ví dụ sau tạo một cột entity_id tự động tăng

1
<column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="true" comment="Credit ID"/>

3.4 Thẻ <constrain>

  • Một node <constrain> luôn chứa các thuộc tính sau

    ATTRIBUTE DESCRIPTION
    type primary, unique, hoặc foreign
    referenceId Tên của ràng buộc. Bạn có thể tự đặt tên cho nó nhưng cách đơn giản nhất là bạn lấy luôn tên được generate trong file db_schema_whitelist.json khi chạy command generate-whitelist. Lưu ý tên ràng buộc này chỉ được sử dụng trong phạm vi các file db_schema.xml, còn trong database tên ràng buộc có thể khác và do hệ thống tự tạo
  • Ràng buộc primaryunique là ràng buộc nội bộ, chỉ liên quan đến 1 bảng, có thể định nghĩa nhiều cột cùng một lúc (nhiều column unique trong bảng hay 2 cột đồng thời là khóa chính)

    Ví dụ tạo một khóa chính

1
2
3
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="entity_id"/>
</constraint>
  • Khóa ngoại foreign kết nối 2 bảng với nhau. Một khóa ngoại có thể bao gồm các attribute sau:

    ATTRIBUTE DESCRIPTION
    table Tên của table hiện tại.
    column Tên cột trong bảng hiện tại mà tham chiếu đến một cột trong bảng khác.
    referenceTable Bảng được tham chiếu.
    referenceColumn Cột được tham chiếu trong bảng referenceTable
    onDelete Trigger khi delete bản ghi với khóa ngoại, có thể là CASCADE, SET NULL, hoặc NO ACTION

    Ví dụ sau khai báo một khóa ngoại, cột entity_id là khóa chính của bảng company nhưng là khóa ngoại của bảng company_credit

1
2
<constraint xsi:type="foreign" referenceId="COMPANY_CREDIT_COMPANY_ID_COMPANY_ENTITY_ID" table="company_credit" column="company_id" referenceTable="company" referenceColumn="entity_id" onDelete="CASCADE"/>

3.5 Thẻ <index>

  • Định nghĩa chỉ mục cho bảng với thẻ <index>, giúp tăng tốc khi thực hiện các hành động DQL (Doctrine Query Language)

  • Một thẻ <index> có các attribute sau:

    ATTRIBUTE DESCRIPTION
    indexType Có thể là btree, fulltext, hoặc hash
    referenceId Tên của chỉ mục. Bạn có thể tự đặt tên cho nó nhưng cách đơn giản nhất là bạn lấy luôn tên được generate trong file db_schema_whitelist.json khi chạy command generate-whitelist

    Ví dụ:

1
2
3
<index referenceId="NEWSLETTER_SUBSCRIBER_CUSTOMER_ID" indexType="btree">
<column name="customer_id"/>
</index>

4. Whitelist

  • Declarative Schema sẽ không thể xóa các bảng, các cột và các khóa ràng buộc nếu nó chưa được định nghĩa trong db_schema.xml
  • Vì lý do trên chúng ta phải cần đến file db_schema_whitelist.json, file này sẽ cho thấy lịch sử của các bảng, các cột và ràng buộc nào đã được thêm vào db_schema.xml. Nói một cách dễ hình dung, giả sử như bạn muốn mang thiết bị vào một tòa nhà, bạn phải đăng kí thông tin ở quầy lễ tân(file db_schema_whitelist.json), như vậy bạn mới được phép khi vận chuyện thiết bị ra khỏi tòa nhà.
  • Như vậy bạn chỉ có thể xóa bảng, xóa cột, xóa ràng buộc khi nó đã được định nghĩa trong file db_schema_whitelist.json
  • Để generate file db_schema_whitelist.json, chạy command sau
1
$ bin/magento setup:db-declaration:generate-whitelist [options]

[options] có thể là --module-name[=MODULE-NAME] để chỉ định tên module bạn muốn generate file whitelist. Nếu không có module nào được chỉ định, mặc định sẽ là generate cho tất cả các module

5. Chế độ DRY RUN

  • Một điều rất cool mà Declarative Schema mang lại cho chúng ta đó là chế độ Dry Run. Chúng ta có thể kiểm tra trước tất cả các câu lệnh DDL SQL (Data Definition Language) mà được generate trong quá trình chạy declarative schema.
  • Ở trong chế độ Dry Run, database sẽ KHÔNG bị thay đổi.
  • Để chạy trong chế độ DRY RUN, chạy 1 trong các command sau:
1
$ bin/magento setup:install --dry-run=1

hoặc

1
$ bin/magento setup:upgrade --dry-run=1
  • Với flag --dry-run=1, Magento sẽ tạo ra một file log ở <Magento_Root>/var/log/dry-run-installation.log.
  • File này sẽ chứa tất cả các câu lệnh DDL SQL được generate trong quá trình cài đặt. Bạn có thể sử dụng chúng để debug và xử lý tối ưu performance

6. Tổng Kết Phần 1

  • Bài viết này mình trình bày cách sử dụng Declarative Schema, cấu trúc của một file db_schema.xml. Mong rằng qua bài viết này các bạn biết thêm một cách hữu ích có thể thay thế các tập lệnh Install/Upgrade Script để tạo và update cấu trúc của bảng trong CSDL.

  • Bài viết này còn tiếp. Trong phần 2 mính sẽ trình bày cụ thể các trường hợp mà bạn hay gặp như: Tạo bảng như nào, đổi tên bảng như nào, đổi tên cột, xóa cột…
    See ya!
    Thankyou