Tái cấu trúc controller và actions trong thực tế

pdf 7 trang Gia Huy 17/05/2022 2180
Bạn đang xem tài liệu "Tái cấu trúc controller và actions trong thực tế", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdftai_cau_truc_controller_va_actions_trong_thuc_te.pdf

Nội dung text: Tái cấu trúc controller và actions trong thực tế

  1. TÁI CẤU TRÚC CONTROLLER VÀ ACTIONS TRONG THỰC TẾ Nguyễn Hữu Cầm Trường Đại học Hà Nội Abstract: Khi phát triển một ứng dụng web application theo mô hình MVC, các nhà phát triển trong một tổ chức thường tự mình đặt tên cho controller và actions. Điều này sẽ dẫn đến khó có thể hiểu được action trong controller đó làm việc gì nếu như đặt tên không chính xác. Thực chất, việc đặt tên các controllers và các actions trong controllers đó là một bài toán không đơn giản do làm thế nào để đặt tên actions ngắn gọn, dễ nhớ và dễ tìm kiếm khi ứng dụng có xu hướng mở rộng. Trong ứng dụng nhỏ có thể không cần theo cách áp dụng này; tuy nhiên khi dự án có xu hướng mở rộng và phát triển lâu dài thì vấn đề đặt tên controller và actions theo chuẩn sẽ giúp quá trình phát triển nhanh hơn và dễ bảo trì hơn. Từ khoá: Action, Controller, Laravel, MVC architecture, Naming convention. A. Giới thiệu Trong một ứng dụng MVC, việc đặt tên cho controller và action là một bài toán đau đầu đối với các nhà lập trình viên. Sẽ có những lập trình viên khi lập trình một ứng dụng Laravel thường tạo controller rồi viết rất nhiều actions trong đó với chiều dài có thể lên tới hàng trăm dòng code. Điều này sẽ gây khó k [23]hăn không chỉ với bản thân người lập trình mà với những người duy trì dự án sau này khi đọc lại chính code của mình viết ra mà không hiểu tại sao mình lại viết như thế này cũng như mất thời gian để tìm hiểu lại xem phương thức này có ý nghĩa là gì. Vì thế, việc tách controller cũng như actions trong controller đó thành các actions nhỏ hơn sẽ giúp quản lí hiệu quả khi dự án ngày một phức tạp hơn. B. Lý thuyết và ứng dụng Bản chất chính của method này là hãy tách nhỏ controller thành các method quy chuẩn sau Tên function Method type Chức năng Index() GET Xem danh sách resources tồn tại trong hệ thống: Ví dụ như xem danh sách users và danh sách roles, etc. Create() GET Xem trang create resource. Ví dụ như xem trang create user, create role Store() POST Tiến hành thêm mới resource vào hệ thống Edit() GET Xem trang edit resource Update() PUT|PATCH Tiến hành update resource vào hệ thống Delete() DELETE Tiến hành xoá resource trong hệ thống Show() GET Xem thông tin chi tiết resource nhất định trong hệ thống 200
  2. Bài toán được ứng dụng như sau. Người dùng có xem danh sách podcasts, trong mỗi podcasts có nhiều episodes. Người dùng có thể xem danh sách episodes trong hệ thống, subscribe/unsubscribe podcast nhất định, publish/unpublish podcast và thay đổi cover image của podcasts trong hệ thống. Quy tắc 1: Tách nested resource thành controller riêng Ví dụ với vấn đề người dùng xem danh sách episodes trong podcast với URI theo chuẩn RESTful như sau Figure 9: URI trước khi refactor route cho nested resource - Lựa chọn thứ nhất: Nếu để action ví dụ như listEpisodes() trong PodcastController thì sẽ bị vi phạm nguyên tắc 7 methods như đã đề cập ở trên sai nguyên tắc - Lựa chọn thứ 2: Nếu để method index() trong EpisodeController thì bản thân function này có ý nghĩa xem tất cả các episodes trong hệ thống không chính xác. - Lựa chọn thứ 3: Tách thành 1 controller riêng có tên là PodcastEpisodesController với method là index(). Route sẽ được biểu diễn như sau Figure 10: URI sau khi đã refactor nested resource trong route/web.php Trong code Figure 11: Code behind trong nested resource với $id là id của podcasts. 201
  3. Việc làm này còn có một ưu điểm khác, trong trường hợp nếu muốn tạo mới 1 episode trong 1 podcasts nhất định thì URI sẽ như sau Figure 12: Different routing trong route/web.php Với create() là xem trang thêm mới episode trong podcasts nhất định và store() dùng để tiến hành thêm mới episode trong podcasts. Với các lí do trên, lựa chọn 3 là lựa chọn tốt nhất để quản lí episodes trong podcasts. Quy tắc 2: Tách edited property thành 1 controller riêng Bài toán tiếp theo cho phép người dùng thay đổi cover image của podcast nhất định Figure 13: Trước khi refactor route “Update cover image” trong route/web.php Code Figure 14: Code behind edited property Bản thân hàm updateCoverImage($id) là một hàm cập nhật resource, nếu dể hàm update() này vào trong PodcastController thì không đúng vì hàm đó là hàm cập nhật thông tin podcast, đặt vào EpisodeController cũng không đúng do hàm này không cập nhật thông tin episode Vì thế hãy tách ra thành 1 controller riêng có tên là PodcastCoverImageController với method update(),với method là PUT. URI sẽ được hình thành như sau Figure 15: Sau khi refactor URI cho edited property 202
  4. Tuy nhiên có vấn đề cần lưu ý: Trong trường hợp nếu cover image cần update nằm trên cùng một UI với những thông tin cơ bản của podcast thì có thể sử dụng hàm update() để thực hiện hành động này, tuy nhiên trong trường hợp updateCoverImage() nằm trên một UI khác với UI cập nhật cơ bản của podcasts thì nên tách ra thành 1 controller mới, với method update() đã được đề cập ở trên, tránh trường hợp sử dụng chung logic trong controller action làm code phức tạp. Quy tắc 3: Để pivot model là resource riêng biệt Bài toán tiếp theo cho phép người dùng đã đăng nhập có thể subscribe hoặc unsubscribe podcasts. Figure 16: URI trước khi refactor cho vấn đề subscribe/unsubscribe Trong case này thì mối quan hệ sẽ là many-many relationship: Một user subscribes nhiều Podcasts và một podcasts có thể được subscribe bởi nhiều users. User 1 – M Subscriptions M – 1 Podcasts Chúng ta sẽ tiến hành tạo ra SubscriptionController để model trường hợp M-N đó Figure 17: URI cho vấn đề subscribe Vậy thì vấn đề được đặt ra: podcast_id này giờ lấy ở đâu, thay vì trên URL thì podcast_id bây giờ có thể được truyền qua request body - thông qua hidden field hoặc query string parameter. Figure 18: Method store() 203
  5. Figure 19: Podcast model Ở đây, subscription sẽ là 1 model riêng biệt, được map vào bảng podcast_user với nhiệm vụ xử li pivot table. Figure 20: Subscription model Đối với trường hợp unsubscribe, thay vì creating resource như hàm store() thì sẽ là deleting resource với hàm delete(), sử dụng method DELETE. Với trường hợp này, {id} cũng lấy từ request body tương tự như ở trên, {id} ở đây là subscription_id. Figure 21: Delete subscription 204
  6. Quy tắc 4: Think of different states as different resources Bài toán tiếp theo cho phép người dùng publish hoặc unpublish podcast, thông qua việc chuyển trạng thái cột published_at từ NULL sang current datetime hoặc ngược lại Figure 22: URI trước khi refactor Trong trường hợp này, việc sử dụng hàm update() trong podcast đã có tác dụng cập nhật thông tin podcasts, nên không thể sử dụng update() để cập nhật trạng thái podcasts Khi publish 1 podcast mới, điều đó cũng giống như việc sử dụng hàm store() trong PublishedPodcastsController. URI sau khi refactor sẽ như sau Figure 23: URI sau khi refactor Để xử lí việc đó, hãy tạo ra controller mới có tên là PublishedPodcastsController Tương tự, {id} sẽ được truyền qua request body, thông qua hidden field hoặc query string parameter Với trường hợp unpublish sẽ là Figure 24: URI cho unpublished podcasts Với {id} là id của podcast được published Code sẽ được thực thi như sau 205
  7. C. Kết luận Mỗi tổ chức đều có quy định cấu trúc dự án riêng, nhằm giúp dự án dễ tiếp cận, bảo trì và mở rộng nếu cần thiết. Việc theo các practice bằng cách quy các actions trong controller thành 7 methods như đã định nghĩa ở trên là một trong những cách giúp cho project gặt hái thành công, giúp tiết kiệm thời gian và công sức cho bản thân chính nhà phát triển cũng như những người tiếp tục duy trì dự án sau này. Link Github tham khảo: 206