post-image

Tối ưu code Laravel của chính mình(P1)

Tổng quan

1. Mở đầu

Một ngày đẹp trời bạn nhìn lại những dòng code của bạn 6 tháng hay một năm trước, chắc hẳn nhiều người sẽ phải thốt lên rằng sao ngày trước mình có thể những dòng code lởm đến vậy. Nếu bạn cũng đang trong tình trạng này thì cũng đừng quá lo lắng, 6 tháng hay 1 năm nữa bạn nhìn lại những gì bạn cho là clean code trước kia thì giờ chúng cũng chỉ là mớ code lộn xộn thôi. Lại nói về phong cách code, mỗi coder chắc chắn đã và đang ảnh hưởng phong cách code của một ai đó.

Ví dụ như mình, hồi sinh viên thì ảnh hưởng bởi mấy anh chị dạy video trên Việt JackKhoa Phạm. Đi thực tập thì ảnh hưởng bởi mấy anh cùng công ty. Dù bạn có đang code theo phong cách nào đó thì cũng phải tuân thủ các rules về convention . . .

Dần dần làm việc quen thì mình lại hay tìm tòi ra những Best Practices. Mổ xẻ xem cách này đã tối ưu chưa, code có sạch, đẹp hơn không. Ở khuôn khổ bài viết này, mình sẽ lục lại tất cả những đoạn code mà mình cho là lởm nhất mà những người làm quen mới Laravel thường sử dụng và đưa ra những cách giải quyết tối ưu hơn.

Một phần để code đẹp hơn nữa là đừng ngại thay đổi và nhờ người khác review code hộ. Ai góp ý hãy suy nghĩ một cách thật nghiêm túc chứ đừng kiểu làm thế này cũng được mà anh và tiếp tục dùng những dòng code thối đấy cho những dự án sau.

89c8102e 046e 4877 88b9 85368e76719a

2. Tối ưu code

2.1 Mass assignment

Mass Assignment là gì? Mass Assignment xuất phát từ ngôn ngữ Ruby on Rails, là tính năng cho phép lập trình một cách tự động gán các tham số của một HTTP request vào các biến hoặc đối tượng trong lập trình. Ví dụ: chúng ta có một form đăng ký người dùng như sau, các tên trường nhập liệu trùng với tên cột trong bảng users trong CSDL. 6 tháng trước mình lưu vào DB theo kiểu này

$user = new User; $user->name = $request->name; $user->password = $request->password; $user->role = $request->role; $user->save();
Code language: PHP (php)

Nhưng nhiều hệ thống còn lấy cả năm sinh, giới tính . . . dài thật dài. Vây là đoạn code của chúng ta cũng dài thật dài. Thực ra có chỉ cần viết như này thôi.

$user = User::create($request->all());
Code language: PHP (php)

Cẩn thận hơn nữa thì có thể dùng $request->only('name', 'password', 'role'). Code vừa ngắn vừa dễ hiểu đúng không các bạn. Nhớ là để dùng Mass Assignment chúng ta phải fillable các thuộc tính này trong Model nữa nhé. Tài liệu tham khảo ở đây .

2.2 Business logic should be in service class

Nói nôm na là viết riêng class để xử lí phần code logic thôi. Lấy ví dụ tiếp về phần upload file trong Laravel nhé.

public function store(Request $request) { if ($request->hasFile('image')) { $request->file('image')->move(public_path('images') . 'temp'); } .... }
Code language: PHP (php)

Nhiều bạn kể cả junior dev giờ vẫn chơi kiểu này. Viết lai cho đẹp cái nào

public function store(Request $request) { $this->articleService->handleUploadedImage($request->file('image')); .... } class ArticleService { public function handleUploadedImage($image) { if (!is_null($image)) { $image->move(public_path('images') . 'temp'); } } }
Code language: PHP (php)

Giải thích một chút thôi nhé. Viết riêng 1 class ArticleService rồi ta sẽ inject những instance đó vào thông qua constructor rồi gọi tới hàm handleUploadedImage của class ArticleService. Nhìn chuyên nghiệp hơn chưa các bạn.

2.3 Eloquent

Đặt vấn đề chúng ta có 2 bảng posts và categories có quan hệ một nhiều với nhau được thể hiện qua model Post và Category. Bây giờ trong view Category bạn muốn lấy ra bài viết đầu tiên chẳng hạn. Đơn giản thôi mà

// Category Model public function posts() { return $this->hasMany(Post::class); } $category->posts->first();
Code language: PHP (php)

Nhìn thuận mắt đấy nhưng nếu viết lại mình sẽ viết như thế này

$categoty->posts()->first() ;
Code language: PHP (php)

Có sự khác nhau gì ở đây không ? Xin thưa là rất nhiều nhé

  • $category->posts sẽ query DB để lấy ra tất cả các posts thuộc về category hiện tại sau đó dựng các model Post, đưa vào Collection sau đó duyệt từng phần tử của Collection đó để lấy ra bản ghi đầu tiên từ điều kiện first() của ta.
  • $categoty->posts() thì như định nghĩa ở trên là return $this->hasMany(Post::class);, ta có thể thấy nó sẽ trả về instance của Category. Khi ta gọi $categoty->posts()->first() thì nó sẽ lấy 1 bản ghi duy nhất từ tầng database ra chứ không lấy tất cả bản ghi như $category->posts rồi mới lọc tiếp để lấy ra bản ghi đầu tiên nữa.

Nên nhớ dùng cái gì thì lấy cái đó ra thôi, với bảng vài nghìn bản ghi mà bạn cứ gọi tất ra rồi dùng một cái thì . . . (facefalm)

2.4 XorY methods

Eloquent có khá nhiều hàm kết hợp cả hai method như là “làm X đi, không thì làm Y” ví dụ như findOrFail hoặc firstOrCreate mà ngày trước mới học Laravel mình chưa biết. Ví dụ như trước mình hay viết kiểu kiểu

$user = User::find($id); if (!$user) { abort (404); }
Code language: PHP (php)

Nô nô, dùng kiểu này đi cho người lớn.

$user = User::findOrFail($id);
Code language: PHP (php)

Thêm một ví dụ nữa mà mình rất rất bị bắt lỗi

$user = User::where('email', $email)->first(); if (!$user) { User::create([ 'email' => $email ]); }
Code language: PHP (php)

Giờ cho viết lại mình sẽ viết

$user = User::firstOrCreate(['email' => $email]);
Code language: PHP (php)

Đấy 6 dòng giờ còn 1 dòng luôn.

2.5 Use eager loading

Cái này thì trong những bài viết của viblo cũng nói đến rất nhiều rồi nhưng có thể một số bạn vẫn chưa biết đến. Bạn có thể đọc thêm tại đây

@foreach (User::all() as $user) {{ $user->profile->name }} @endforeach
Code language: PHP (php)

Câu lệnh này với 100 user thì cần 101 câu truy vấn vào DB. Còn khi ta sử dụng with() hoặc load() thì code như sau

//Controller $users = User::with('profile')->get(); ...php //View @foreach ($users as $user) {{ $user->profile->name }} @endforeach
Code language: PHP (php)

Khi sử dụng with() với 100 user chỉ tốn 2 câu truy vấn thôi.

3.Tối ưu convention

Đã bao giờ bạn biết tự hỏi chuẩn đặt tên các file trong Laravel ? Câu trả lời sẽ là đây

TênChuẩnTốtChưa Tốt
ControllerSố ítArticleControllerArticlesController
RouteSố nhiềuarticles/1article/1
Named routesnake_case với ký hiệu chấmusers.show_activeusers.show-active, show-active-users
ModelSố ítUserUsers
hasOne or belongsTo relationshipSố ítarticleCommentarticleCommentsarticle_comment
All other relationshipsSố nhiềuarticleCommentsarticleCommentarticle_comments
TableSố nhiềuarticle_commentsarticle_commentarticleComments
Model propertysnake_case$model->created_at$model->createdAt
Foreign keySố ít với hậu tố _idarticle_idArticleIdid_articlearticles_id
Primary keyiduser_id
Migration2017_01_01_000000_create_articles_table2017_01_01_000000_articles
MethodcamelCasegetAllget_all
Method in resource controllerstoresaveArticle
VariablecamelCase$articlesWithAuthor$articles_with_author
CollectionSố nhiều, có nghĩa$activeUsers = User::active()->get()$active, $data
ObjectSố ít, có nghĩa$activeUser = User::active()->first()$users, $obj
Config and language files indexsnake_casearticles_enabledArticlesEnabledarticlesenabled
Viewsnake_caseshow_filtered.blade.phpshowFiltered.blade.phpshow-filtered.blade.php
Configsnake_casegoogle_calendar.phpgoogleCalendar.phpgoogle-calendar.php

Chỉ cần học thuộc bảng cửu chương này code bạn cũng có thể đẹp hơn đấy.

Các bạn có thể tham khảo các bài viết hay về Laravel tại đây.


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Tham khảo: Viblo

Leave a Reply

Your email address will not be published. Required fields are marked *