Lời nói đầu
Lại là mình tiếp tục với series Những chức năng bạn có thể cần trong một project Laravel đây. Và trong bài viết hôm nay mình sẽ giới thiệu với các bạn cách thực hiện chức năng lưu log các hoạt động của người dùng trên trang web của bạn (thường thì chức năng được sử dụng ở bên admin là chủ yếu chứ mình thấy bên client cũng ít).
Nhưng dù là làm cho bên nào sử dụng thì mình cũng thấy đây là một chức năng cần thiết để bạn có thể quản lí trang web tốt hơn. Và không cần dài dòng nữa, mình xin giới thiệu đến các bạn package spatie/laravel-activitylog.
Nội dung
Cài đặt
Trước khi bắt đầu vào đặt thì chúng ta cần làm rõ là hiện tại package này đã có 3 phiên bản v1, v2 và v3. Tất nhiên là 3 phiên bản sẽ yêu cầu phiên bản PHP và Laravel khác nhau, vì vậy, nếu bạn đang sử dụng phiên bản PHP/Laravel cũ thì hãy cân nhắc lựa chọn phiên bản phù hợp nhé. Còn nếu như bạn cứ hàng mới nhất mà xài thì cũng không cần quan tâm lắm đâu
Version Requirement PHP Requirement Laravel v1 PHP 7.0+ Laravel 5.2 v2 PHP 7.1+ Laravel 5.5 v3 PHP 7.2+ Laravel 6 Sau khi đã nắm được các requirement thì chúng ta hãy bắt tay vào setup nào
- Cài đặt package thông qua composer:12composer require spatie/laravel-activitylog
- Publish migration: câu lệnh này sẽ tự động tạo cho bạn file migration để tạo bảng
activity_log
. Tuy nhiên cần chú ý rằng, model IDs mặc định được sử dụng dạng integers, vì vậy nếu bạn sử dụng UUIDs hoặc bất kì format nào khác thì bạn hãy nhớ sửa lại file migration cho trườngsubject_id
vàcauser_id
trước khi migrate nhé12php artisan vendor:publish --provider<span class="token operator">=</span><span class="token string">"SpatieActivitylogActivitylogServiceProvider"</span> --tag<span class="token operator">=</span><span class="token string">"migrations"</span>
Sau khi đã chắc chắn file migration đã đúng theo mục đích sử dụng rồi thì hãy tạo bảngactivity_log
với câu lệnh12php artisan migrate - Config file:123<span class="token comment">#publish config</span>php artisan vendor:publish --provider<span class="token operator">=</span><span class="token string">"SpatieActivitylogActivitylogServiceProvider"</span> --tag<span class="token operator">=</span><span class="token string">"config"</span>
Sau đó, bạn sẽ thu được file config có nội dung như sau (đã có comment cụ thể cho từng dòng nên chắc mình cũng không cần phải giải thích gì thêm nhỉ):1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950<span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token comment">/** If set to false, no activities will be saved to the database.*/</span><span class="token single-quoted-string string">'enabled'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'ACTIVITY_LOGGER_ENABLED'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token comment">/** When the clean-command is executed, all recording activities older than* the number of days specified here will be deleted.*/</span><span class="token single-quoted-string string">'delete_records_older_than_days'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token number">365</span><span class="token punctuation">,</span><span class="token comment">/** If no log name is passed to the activity() helper* we use this default log name.*/</span><span class="token single-quoted-string string">'default_log_name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'default'</span><span class="token punctuation">,</span><span class="token comment">/** You can specify an auth driver here that gets user models.* If this is null we'll use the default Laravel auth driver.*/</span><span class="token single-quoted-string string">'default_auth_driver'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token keyword">null</span><span class="token punctuation">,</span><span class="token comment">/** If set to true, the subject returns soft deleted models.*/</span><span class="token single-quoted-string string">'subject_returns_soft_deleted_models'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">false</span><span class="token punctuation">,</span><span class="token comment">/** This model will be used to log activity.* It should be implements the SpatieActivitylogContractsActivity interface* and extend IlluminateDatabaseEloquentModel.*/</span><span class="token single-quoted-string string">'activity_model'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token package">Spatie<span class="token punctuation"></span>Activitylog<span class="token punctuation"></span>Models<span class="token punctuation"></span>Activity</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span><span class="token comment">/** This is the name of the table that will be created by the migration and* used by the Activity model shipped with this package.*/</span><span class="token single-quoted-string string">'table_name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'activity_log'</span><span class="token punctuation">,</span><span class="token comment">/** This is the database connection that will be used by the migration and* the Activity model shipped with this package. In case it's not set* Laravel database.default will be used instead.*/</span><span class="token single-quoted-string string">'database_connection'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'ACTIVITY_LOGGER_DB_CONNECTION'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
- Cài đặt package thông qua composer:
Cách thực hiện để lưu lại log hoạt động
Làm việc với log thì chỉ có 2 thao tác cơ bản, đó là lưu log và hiển thị log, và dù là thao tác nào thì các bạn cũng phải nhớ use model của nó vào bằng cách
12<span class="token keyword">use</span> <span class="token package">Spatie<span class="token punctuation"></span>Activitylog<span class="token punctuation"></span>Models<span class="token punctuation"></span>Activity</span><span class="token punctuation">;</span>Lưu log: để có thể ghi log thì chắc chắn sẽ có 2 đối tượng tham gia, đó là
đối tượng tác động
vàđối tượng bị tác động
. Ở đây:đối tượng tác động
gọi là causer (thường sẽ là model User).- Để khai báo causer chúng ta sử dụng function
->causedBy($model)
hoặc dùng dạng rút gọn là->by($model)
. Nếu như bạn không khai báo thì nó sẽ tự hiểu causer là user đang log in. - Còn nếu bạn không muốn lưu lại causer thì chỉ cần gọi
causedByAnonynmous
/byAnonymous
.
- Để khai báo causer chúng ta sử dụng function
đối tượng bị tác động
gọi là subject (có thể là bài post, comment, …). Để khai báo subject chúng ta sử dụng function->performedOn($subjectModel)
hoặc->on($subjectModel)
.Ngoài ra, bạn cần khai báo
description
cho bản ghi để mô tả hành động mà causer thực hiện (ví dự như là thêm/sửa/xoá) bằng function->log('added/edited/deleted')
.Như vậy để lưu lại 1 bản ghi log đầy đủ ta sẽ có dạng như sau:
1234567<span class="token keyword">use</span> <span class="token package">Spatie<span class="token punctuation"></span>Activitylog<span class="token punctuation"></span>Contracts<span class="token punctuation"></span>Activity</span><span class="token punctuation">;</span><span class="token function">activity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">causedBy</span><span class="token punctuation">(</span><span class="token variable">$user</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">performedOn</span><span class="token punctuation">(</span><span class="token variable">$subjectModel</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">log</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'added/edited/deleted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>Đây là đối với bảng
activity_log
mặc định, còn nếu bạn có thêm trường khác vào trong bảng thì để điền giá trị vào trường ấy thì bạn sẽ sử dụng thêmtap()
như sau:12345678<span class="token function">activity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">causedBy</span><span class="token punctuation">(</span><span class="token variable">$user</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">performedOn</span><span class="token punctuation">(</span><span class="token variable">$subjectModel</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">tap</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>Activity <span class="token variable">$activity</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token variable">$activity</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">custom_field</span> <span class="token operator">=</span> <span class="token single-quoted-string string">'value'</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">log</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'added/edited/deleted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Hiển thị log
Để lấy ra các bản ghi log thì rất đơn giản, không khác gì một model bình thường
123<span class="token variable">$logs</span> <span class="token operator">=</span> Activity<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//lấy tất cả các bản ghi</span><span class="token comment">//Ngoài ra bạn có thể thêm điều kiện truy vấn tuỳ với bài toán của bạn</span>Xoá log: Rõ ràng nếu trang web của bạn hoạt động ổn (tức là có một lượng người truy cập và sử dụng thường xuyên) thì số lượng bản ghi trong bảng log sẽ ngày một nhiều lên và có thể làm chậm đi tốc độ truy vấn. Và bạn sẽ cần phải xoá các bản log đã quá cũ không cần thiết.
Và để làm được điều đó thì bạn sẽ thực hiện câu lệnh
12php artisan activitylog:cleanĐến đây hẳn bạn sẽ thắc măc là nếu chỉ có mỗi câu lệnh như trên thì sẽ XOÁ HẾT bản ghi à thì câu trả lời là KHÔNG. Trong file config phía trên có 1 dòng là
delete_records_older_than_days
tức là câu lệnh sẽ xoá những bản ghi cũ hơn ngày này (như config mặc định là 365 ngày).Ngoài ra, bạn cũng có thể thêm
--days=XXX
vào câu lệnh để có thể lựa chọn số ngày khác với trong config!
Lời kết
Về cơ bản thì như vậy là đã đủ để bạn có thể hoàn thành được chức năng lưu log đơn giản. Tuy nhiên tuỳ thuộc vào bài toán thực tế mà các bạn cần giải quyết thì sẽ cần phải customize lại cho phù hợp. Vì vậy hãy tìm hiểu thêm ở trong link document phía dưới.
Nếu thấy bài viết của mình hữu ích với bạn thì hãy upvote bài viết hoặc follow series Những chức năng bạn có thể cần trong một project Laravel để không bỏ lỡ các bài viết tiếp theo nhé!