11/08/2018, 20:38

Bóc mẽ cách Magento tổ chức, thiết kế sản phẩm

Trong bài này ta sẽ tìm hiểu về kỹ thuật thiết kế dữ liệu để quản lý sản phẩm trong Magento Đối với lập trình viên Magento , việc nắm rõ bản chất của từng sản phẩm, hiểu tại sao Varien lại thiết kế Magento như thế? Sẽ giúp các bạn dễ dàng hơn trong quá trình xây dựng và phát triển, nhất là ...

Trong bài này ta sẽ tìm hiểu về kỹ thuật thiết kế dữ liệu để quản lý sản phẩm trong Magento

  • Đối với lập trình viên Magento, việc nắm rõ bản chất của từng sản phẩm, hiểu tại sao Varien lại thiết kế Magento như thế? Sẽ giúp các bạn dễ dàng hơn trong quá trình xây dựng và phát triển, nhất là các dự án khó, đòi hỏi sự tùy biến cao.
  • Đối với các bạn sử dụng hệ thống Ecommerce khác, bạn sẽ có cơ hội được so sánh, đánh giá giữa hai hệ thống, tìm ra những điểm tối ưu và khiếm khuyết, biết đâu kết hợp ý tưởng giữa hai nền tảng lại đem lại cho bạn nhiều sáng kiến mới: “Woocommerce trong WordPress có hỗ trợ 7 loại sản phẩm” hay là “Tốc độ của Shopify – linh hoạt như Magento” chẳng hạn.
  • Đối với các bạn sử dụng ngôn ngữ, nền tảng, công nghệ hoàn toàn khác, nhất là các nền tảng không mạnh về Ecommerce, việc tự thiết kế một hệ thống quản lý sản phẩm linh hoạt, ổn định, đáp ứng nhiều nhu cầu doanh nghiệp vô cùng gian nan. Hiểu rõ bản chất của hệ thống Magento giúp các bạn dễ dàng học hỏi và áp dụng vào công nghệ mình đang sử dụng, đặc biệt khi xây dựng hệ thống quản lý sản phẩm tầm trung trở lên. (mình đoán đa phần kipalogers là dân Ruby, .NET nên rơi vào case 3 này)

Trước tiên, ta sẽ cùng tiếp cận các kiểu thiết kế khi làm 1 trang quản lý sản phẩm bán hàng.

Thiết kế dữ liệu:
alt text

  • Catalog: danh mục sản phẩm. Một danh mục chứa nhiều sản phẩm.
  • Product: sản phẩm. Chức toàn bộ các thuộc tính sản phẩm và doanh nghiệp cần

Đây là cách thiết kế đơn giản nhất, và cũng yếu kém nhất, vì nhu cầu doanh nghiệp muốn add thêm 1 số thuộc tính vào sản phẩm thì làm thế nào? Cùng theo dõi đoạn chat sau:

– Công ty may mặc Miếng Vá Thế Kỷ: anh kỹ thuật ơi, tôi muốn nhập màu sắc, kích cỡ vào sản phẩm thì sao?
– Lập trình Nguyễn Văn Tèo: thì cô cứ nhập hết vào phần “content”, sản phẩm có gì đặc trưng cứ cho hết vào đấy.
– Công ty may: OK, nhưng phải có khung tìm kiếm, filter theo màu sắc, kích thước đấy nhé!
– Tèo: thôi xong, vậy chị chờ em 3 ngày để phát triển chức năng nhé!

Tèo suy nghĩ: nếu muốn filter theo thuộc tính như vậy thì ta phải tách thuộc tính ra làm field riêng, không thể để chung với field “content” được, chẵng lẽ mỗi lần search, ta lại lặp qua toàn bộ sản phẩm trong hệ thống, tìm tới field “content” và search trong đó?
Vậy là Tèo thêm 2 field mới vào table sản phẩm: color, size.
Nhờ vậy chức năng search, filter của Tèo hoạt động vì câu truy vấn Tèo dùng:
select * from product where color = "red" and size = "S"

– Công ty may: anh Tèo ơi, mấy sản phẩm quần áo em muốn thêm dữ liệu: hãng thời trang, chất liệu… Mà sao hệ thống anh không cho em tự add thêm thuộc tính vào sản phẩm, cứ gọi hỗ trợ hoài mệt quá!
– Tèo: …
– Công ty may: mà công ty em năm nay bán thêm sản phẩm bánh trung thu, anh bỏ mấy thuộc tính: kích thước và màu sắc giùm em.
– Tèo: (té ghế)

Bạn thấy đó, với cách thiết kế này, không thể nào thiết kế được 1 hệ thống quản lý sản phẩm bán hàng linh động, không thể nào tái sử dụng cho nhiều dự án khác nhau. Đã từ lâu, các lập trình viên trên thế giới đã tìm tòi ra được những kỹ thuật giải quyết được bài toán trên. Nào, ta cùng ngược dòng thời gian về năm 2008 để gặp 2 lập trình viên Cristian Darie và Emilian Balanescu, những chuyên gia trong lĩnh vực Ecommerce người Romanian.

alt text

– Cristian: anh Emilian này, chúng ta phải thiết kế database sao để cho phép người dùng chọn màu sắc và kích thước khi mua quần áo?
– Emilian: hãy tách màu sắc và kích thước ra khỏi bảng product trong cơ sở dữ liệu. Với mối quan hệ 1-n, 1 product có nhiều color, ta dễ dàng thêm xóa color ra khỏi product, thông qua 1 giao diện trong admin để quản lý bảng product_color.
– Cristian: nói vậy là mỗi thuộc tính color, size lại có 1 bảng riêng à?
– Emilian: Hmmm… Tôi nghĩ chúng ta cần thiết kế lại chỗ này.

Sau đó họ đã thiết kế ra một mô hình tuyệt vời với 3 bảng như sau:

  • attribute: chứa tên của thuộc tính, ví dụ: Size, Color…
  • attribute_value: chứa giá trị của tât cả các thuộc tính, mối quan hệ giữa bảng attribute và attribute_value là 1-n, ví dụ: S, M, L, Red, Blue, Yellow
  • product_attribute: chứa mối quan hệ giữa product và attribute_value n-n

Cả ba bảng trên đều giúp quản trị viên Admin, thêm xóa sửa thuộc tính, và gán vào những product tương ứng mà không cần sửa lại cấu trúc database.
Ví dụ: muốn thêm thuộc tính “Chất Liệu”:

  1. Thêm 1 dòng vào bảng attribute.
    INSERT INTOattribute(attribute_id,name) VALUES (3, 'Chất Liệu');

  2. Thêm các giá trị của “Chất Liệu” vào bảng attribute_value.
    INSERT INTOattribute_value(attribute_value_id,attribute_id,value)
    VALUES (6, 3, 'Cotton'), (7, 3, 'Thun'), (8, 3, 'Lụa');

  3. Gán sản phẩm vào giá trị thuộc tính chất liệu:
    INSERT INTOproduct_attribute(product_id,attribute_value_id)
    VALUES (100, 6), (100, 7), (101, 6), (101, 8);

– product id 100 có chất liệu là Cotton và Thun
– product id 101 có chất liệu là Cotton và Lụa.

Đây là mô hình quan hệ sau khi thiết kế:
alt text

Với thiết kế trên Cristian và Emilian còn cung cấp một số câu truy vấn để làm việc như sau:
Lấy thông tin thuộc tính theo product ID

SELECT a.name AS attribute_name,
       av.attribute_value_id, av.value AS attribute_value
FROM attribute_value av
INNER JOIN attribute a
           ON av.attribute_id = a.attribute_id
WHERE av.attribute_value_id IN
      (SELECT attribute_value_id
       FROM product_attribute
       WHERE product_id = :productId)
ORDER BY a.name;

Cristian Darie và Emilian Balanescu đã cùng nhau xuất bản cuốn sách Beginning PHP and MySQL E-Commerce, 2nd Edition. Chứa các kỹ thuật thiết kế và phát triển một website bán hàng khá hay, các bạn theo hướng PHP nên tìm đọc.

Vậy là hệ thống có từ năm 2008 này đã giải quyết được bài toán mở rộng thuộc tính sản phẩm khá linh hoạt. Tuy nhiên, lĩnh vực thương mại điện tử đầy biến động chưa bao giờ hết thử thách. Giờ đã đến lúc quay về với hiện tại 2017 để gặp lại anh Tèo của chúng ta.

alt text

Sau khi tạo thêm 3 bảng attribute, attribute_value, product_attribute, website của Tèo nay có thể dễ dàng quản lý thuộc tính sản phẩm mà không cần chỉnh sửa database. Thêm nữa, không những bán mặt hàng quần áo, mà còn bán được nhiều loại sản phẩm với các thuộc tính khác nhau. Tuy nhiên khó khăn chưa dừng lại ở đó:
– Công ty may: hello anh Tèo, lại là em đây. Loại sản phẩm bánh trung thu của tụi em kích thước khác thì giá tiền khác nhau anh ạ, cái size lớn thì bán giá cao hơn…

Tèo suy nghĩ: mình sẽ thêm 1 cột nữa vào bảng product_attribute, cái này sẽ lưu +(số tiền) hoặc -(số tiền). Khi người dùng chọn sản phẩm với thuộc tính có giá như vậy mình sẽ cộng hoặc trừ tiền vào giá gốc đã lưu trong bảng product. Nhưng như thế phải sửa lại code mấy chỗ lấy giá, Tèo vò đầu bức tai, không biết trong các hệ thống Thương Mại Điện Tử lớn người ta làm cách nào nhỉ?
Nếu bạn có cùng câu hỏi như Tèo, cùng gặp những bài toán khó khi thiết kế quản lý sản phẩm, thì bạn đã đọc đúng chỗ rồi, tiếp theo ta sẽ cùng tìm hiểu xem Magento – một trong những hệ thống ecommerce lớn –
đã làm như thế nào.

EAV

Entity-Attribute-Value viết tắt là EAV, là 1 mô hình dữ liệu, làm việc với các thực thể (entity) có số lượng các thuộc tính (attribute) có thể mở rộng. Trong toán học, mô hình này gọi là Ma trận thưa thớt (sparse matrix)
wikipedia
Mô hình này là 1 mô hình nổi tiếng và có từ lâu, phần Product Attribute level 2 bên trên thực chất cũng từ mô hình EAV này mà ra.
Cùng quan sát cách Magento xây dựng hệ thống EAV:

alt text

phân tích theo mô hình trên:
catalog_product_entity chính là thực thể (entity) chứa dữ liệu của sản phẩm – tương đương bảng product trong phần 2
eav_attribute là bảng chứa thông tin chính của thuộc tính (attribute)
– Còn cái đám catalog_product_entity_attribute_varchar, -int… Chính là chứa giá trị của thuộc tính (tương đương attribute_value), tuy nhiên trong Magento, mỗi kiễu dữ liệu của attribute lại có bảng riêng (varchar, int, decimal, text, datetime …)

Tại sao lại tách attribute_value ra theo kiễu dữ liệu?
Đó là vì Magento dễ dàng kiểm soát các attribute của mình hơn. Khi tạo mới 1 attribute, Magento sẽ hỏi attribute input type là gì? Text, Date, Yes/No hay Dropdown, Price… Với mỗi loại, sẽ hiển thị tiếp chỗ nhập giá trị tương ứng với các validate thích hợp.
Ví dụ: attribute dạng text như: name, description… thì validate khác do với attribute dạng decimal như price, weight…, attribute dạng dropdown thì lại hiện danh sách các field option bên trong…
Đó là 1 cải tiến khá hay của Magento so với EAV truyền thống, tuy nhiên hạn chế của nó là khi truy xuất dữ liệu, tốc độ câu truy vấn bị giảm đáng kể vì phải thu thập dữ liệu từ nhiều bảng khác nhau.

Ví dụ trường hợp sau: tôi muốn đổi tên 1 sản phẩm bằng cách sửa database

  1. Tìm product id trong bảng: catalog_product_entity.entity_id = 1
  2. Tìm attribute id trong bảng: eav_attribute.attribute_code = name, attribute_id = 73, (entity_type_id = 4 là sản phẩm). Đồng thời xem luôn backend_type của attribute này là varchar
  3. Tìm giá trị attribute trong bảng: catalog_product_entity_varchar (dựa vào backend_type bước 2) attribute_id = 73, entity_id = 1 => value = Joust Duffle Bag – đây chính là tên sản phẩm cần tìm.

Vậy là ta đã nắm được khái niệm EAV trong Magento. Tiếp theo ta sẽ tìm hiểu Magento lưu trữ như thế nào đối với từng loại sản phẩm. Trong bài viết trước mình có giới thiệu về 7 loại sản phẩm trong Magento. Bạn cần phân biệt rõ đặc điểm của mỗi loại. Để tăng tính thực nghiệm, ta sẽ giúp Tèo tạo bảng và dữ liệu giả định.

Simple product

Đây là loại product đơn giản nhất, là đơn vị nhỏ nhất để cấu tạo thành các loại product khác.
Table: catalog_product

catalog_product (
id, sku, type_id, is_active, sort_order, visibility, created_at, updated_at
)
  • Id, sku là thuộc tính không đổi, tồn lại mãi theo product và là duy nhất.
  • type_id là loại sản phẩm: simple, configurable…
  • is_active, sort_order, visibility, created_at, updated_at là thuộc tính đi theo sản phẩm cố định luôn.

Còn các thuộc tính: name, description, image, price… Vâng, chúng sẽ được tách ra và bỏ vào bảng attribute, tác dụng ra sao hạ hồi phân giải nhé.
Ở đây ta nhập liệu trước cho bảng product 2 records như sau:
(1, ‘sku001’, ‘simple’, 1, 0, 1),
(2, ‘sku002’, ‘simple’, 1, 0, 1)
Không nhập 2 cột created_at, updated_at

Table: eav_attribute

eav_attribute (
id, code, label, type, is_required, is_unique, note
)

Giớ mình sẽ tạo trước 4 thuộc tính như sau:
(1, ‘name’, ‘Name’, ‘varchar’, 1, 0),
(2, ‘description’, ‘Description’, ‘text’, 0, 0),
(3, ‘price’, ‘Price’, ‘decimal’, 1, 0),
(4, ‘image’, ‘Image’, ‘varchar’, 0, 0)
Không nhập cột note

Tiếp là bảng chứa giá trị của mỗi thuộc tính, đặc biệt Magento khác so với phần 2 đã đề cập bên trên 2 điểm:
– Chia attribute value ra bảng riêng theo kiểu dữ liệu.
– Chứa kết nối tới product (entity) ngay trong bảng attribute value mà không cần bảng con nữa.

Table: catalog_product_varchar

catalog_product_varchar (
id, attribute_id, store_id, entity_id, value
)

Yeah! Bạn có nhìn thấy store_id không? Tinh tế nằm ở đây nhé, cột này sẽ cho phép bạn tạo thuộc tính đa ngôn ngữ, hãy xem đòn của ta đây:
(1, 1, 1, 1, ‘Bánh Trung Thu nhân mì tôm’), (2, 1, 2, 1, ‘Noodle Mooncake’),
(3, 2, 1, 1, ‘Bột bánh mềm, bên trong chứa mì tôm sống ăn giòn tan…’),
(4, 3, 1, 1, 100),
(5, 4, 1, 1, ‘img01.jpg’)

Giả sử bạn có 2 store: Việt Nam id = 1 và English id = 2
Vậy là sản phẩm Bánh trung thu (id = 1) đã có đầy đủ 4 thuộc tính, hỗ trợ đa ngôn ngữ.
Khoan nhé, ta phải tách dữ liệu của thuộc tính description id=2 qua bảng catalog_product_text và price id=3 qua bảng catalog_product_decimal mới chính xác. Lúc này bạn vô tình khám phá ra nhờ biết chính xác kiểu giá trị của thuộc tính mà ta dễ dàng thiết kế kiểu dữ liệu của cột value cho phù hợp.

Tới đây, bạn đã hình dung ra giải pháp giải được bài toán của Tèo chưa?
Ta sẽ tạo thêm 1 thuộc tính size và gán vào product id=1 (size = S) vậy là chiếc bánh này có giá là 100. Sau đó ta tạo thêm 1 sản phẩm nữa id=2 với size = L, giá là 200, vậy là xong.
Ta có 2 sản phẩm tách biệt với 2 kích thước, 2 giá khác nhau, nếu ta muốn chỉ hiện lên 1 sản phẩm Bánh trung thu, nhưng có select cho người ta chọn size, mỗi khi chọn thì tự động nhảy giá thích hợp, thì ta phải tạo Product configurable.

Configurable product

Bản chất của loại sản phẩm này là 1 vỏ bọc bên ngoài để chứa các sản phẩm simple product bên trong.
Bây giờ ta sẽ chỉnh sửa lại database để phù hợp với sản phẩm bánh trung thu.
Table: catalog_product

catalog_product (
id, sku, type_id, is_active, sort_order, visibility, created_at, updated_at
)

(1, ‘sku001’, ‘simple’, 1, 0, 0),
(2, ‘sku002’, ‘simple’, 1, 0, 0),
(3, ‘sku003’, ‘configurable’, 1, 0, 1)

Thêm sản phẩm configurable.
Đặc biệt, cột visibility của 2 cái simple sẽ = 0, ý nghĩa là không hiển thị ngoài giao diện, người dùng chỉ thấy cái sản phẩm số 3

Table: eav_attribute

eav_attribute (
id, code, label, type, is_required, is_unique, note
)

(5, ‘size’, ‘Size’, ‘varchar’, 1, 0)
Giữ lại 4 thuộc tính cũ, ta chỉ thêm thuộc tính size

Table: catalog_product_varchar

catalog_product_varchar (
id, attribute_id, store_id, entity_id, value
)

(6, 5, 1, 1, ‘S’),
(7, 5, 1, 2, ‘L’)

Liên kết sản phẩm vào thuộc tính Size và thiết lập giá trị của thuộc tính tương ứng
Product configurable id=3 không cần thiết có size vì nó sẽ hiện size của 2 simple bên trong.
Tới đây ta đã hoàn tất dữ liệu cho configurable, tuy nhiên ta phải tạo thêm 1 bảng nữa, để chứa liên kết giữa sản phẩm configurable và 2 sản phẩm simple:

Table: catalog_product_link

eav_attribute (
id, product_id, linked_product_id
)

(1, 3, 1),
(2, 3, 2)

Vậy là, ta đã hiểu bên dưới, Magento lưu trữ sản phẩm configurable và simple ra sao. Về bản chất, simple nào nằm bên trong configurable thì sẽ ẩn ra ngoài giao diện, khi mua configurable, chọn vào thuộc tính đặc biệt như size, color, sản phẩm sẽ load lên giá và hình ảnh của simple product tương ứng.
Đây chính là những khái niệm cơ bản nhất về Magento.
Cùng đón chờ thử thách tiếp theo của Tèo và các loại sản phẩm khác, sẽ dần được hé mở trong các bài viết tiếp theo.

Vấn đề thiết kế dữ liệu trong 1 hệ thống thương mại điện tử là rất quan trọng. Khi chúng ta sử dụng lại các nền tảng CMS, Framework, Platform có sẵn, chúng ta chẳng cần quan tâm đến vấn đề này. Tuy nhiên, việc hiểu rõ bản chất cũng như có khả năng học hỏi, áp dụng lại những kỹ thuật đó sẽ giúp chúng ta tăng kỹ năng lập trình lên ở trình độ “developer” đúng nghĩa chứ không chỉ là “end-programmer-user”

Thêm nữa, chúng ta cũng sẽ rút được nhiều bài học cùng với Tèo. Từ nay, khi tự tay viết 1 trang bán hàng, chúng ta sẽ không thiết kế theo kiểu level 1 nữa, vì kiểu này hoàn toàn là 1 cách tiếp cận sai lầm.
Toàn bộ database trong ví dụ trên tôi xin cung cấp bằng MySQL – 2017-09-22_eav_tables
Viết bài này thèm bánh trung thu quá
alt text

Bài viết được copy từ blog của mình link
Chào đón các bạn quan tâm về Magento ghé thăm.

0