Laravel Cloner package
Bài đăng này đã không được cập nhật trong 5 năm
Chào mọi người, hôm nay mình sẽ lại lượn về chủ đề Laravel 1 xíu nhé. Bài hôm nay nói về 1 điều 'nho nhỏ' mình mới đọc được trên Laravel News, nên tiện tay tìm hiểu thì viết bài luôn
Đặt vấn đề
1 ngày đẹp trời, bạn muốn thêm 1 chục bản ghi y hệt nhau vào CSDL, hoặc giả như khi bạn làm chức năng Duplicate Post cho người dùng có thể copy bài post chẳng hạn, hoặc chức năng kiểu như thế này: Vậy giờ map từng trường dữ liệu của bản ghi để copy à?
$new_record->title = $old_record->title
$new_record->excerpt = $old_record->excerpt
...
Cách này cũng ổn đấy, nhưng mà mình giới thiệu cho 1 cách nhanh hơn này . Chỉ cần 1 dòng thôi:
Post::first()->duplicate();
Tìm hiểu nào
Cài đặt
Trước tiên phải tải package về đã nhé
composer require bkwld/cloner
Tiếp theo để cài đặt, bạn cần phải khai báo sử dụng nữa. Chúng ta sẽ khai báo để sử dụng package này trong AppService Provider nhé
Tại thời điểm này, nếu bạn chạy thử php artisan serve
thì sẽ dính ngay lỗi sau nhé:
Nhìn lại file này bạn sẽ thấy
use Illuminate\Support\ServiceProvider;
use Bkwld\Cloner\ServiceProvider;
Vậy là 2 cái sử dụng cùng namespace rồi . Sửa thế nào đây? Đơn giản lắm, đổi namespace khác là ổn ngay . Mình đổi sang namespace ClonerPackage
nhé
use Bkwld\Cloner\ServiceProvider as ClonerProvider;
Chạy thử lại phát thấy ngon ngay
Được rồi, phần chuẩn bị đã xong, giờ chúng ta đi vào tìm cái sử dụng để test thử nào.
Sử dụng
Bây giờ bạn muốn sử dụng Cloner cho Model nào thì chỉ cần qua đó, thêm 1 dòng use \Bkwld\Cloner\Cloneable;
vào đó là được. Giả sử mình sử dụng cho Model Post nhé:
class Post extends Model
{
use \Bkwld\Cloner\Cloneable;
}
Nhân bản 1 bản ghi - Duplicate a record
Giờ bạn muốn thêm 1 chục bản ghi y hệt nhau vào CSDL, hoặc giả như khi bạn làm chức năng Duplicate Post cho người dùng có thể copy bài post chẳng hạn, hoặc chức năng kiểu như thế này: Vậy giờ map từng trường dữ liệu của bản ghi để copy à?
$new_record->title = $old_record->title
$new_record->excerpt = $old_record->excerpt
...
Cách này cũng ổn đấy, nhưng mà mình giới thiệu cho 1 cách nhanh hơn này . Chỉ cần 1 dòng thôi: Post::first()->duplicate();
$clone = Post::first()->duplicate(); //sử dụng biến clone để lưu giá trị duplicate
return $clone; //check thử xem biến clone bây giờ có gì
Giờ thì bạn sẽ thấy trong DB của mình có thêm 1 bản sao của bản ghi đầu tiên nhé
Copy 1 bản ghi sang bảng khác - database khác
Trong trường hợp bạn cần copy DB, hay copy bản ghi sang 1 bảng tương tự thì cũng tận dụng được package này luôn nhé:
Post::first()->duplicateTo('connection');
Ở đây ta copy bản ghi đầu tiên trong bảng post, và copy sang địa chỉ mới thông qua 1 connection
(Đọc tài liệu tại đây nhé)
Nhưng mà Model của mình có cả dây mơ rễ má relationship đi kèm thì sao?
Bây giờ đặt tình huống, Post
của mình thì có nhiều Image
hình ảnh minh họa đi kèm, mà nó cũng có nằm trong nhiều Category
- Danh mục sẵn rồi. Vậy thì khi Clone, muốn clone kèm cả các thể loại dây mơ rễ má đó bạn sửa lại Model 1 chút như sau nhé:
class Post extends Eloquent {
use \Bkwld\Cloner\Cloneable;
protected $cloneable_relations = ['images', 'categories']; //Chọn các relationship sẽ được clone kèm
public function images() {
return $this->hasMany('Image');
}
public function categories() {
return $this->belongsToMany('Category');
}
}
Sau khi sửa lại như vậy, chỉ cần dòng lệnh
Post::first()-> duplicate();
ở trên là kèm theo đã clone cả các Hình ảnh minh họa và cả Danh mục của bài post đấy rồi: Tất cả các hình ảnh được copy mới và thiết lập quan hệ với bản ghi mới, đồng thời tự động các trường trong pivot table của quan hệ N - N giữa Post và Category cũng tự động được thêm vào bản ghi mới tương ứng. Ngoài ra, nếu Image
lại dây mơ rễ má gì hơn nữa thì bạn lại có thể dùng $cloneable_relations
để thiết lập và clone cho đủ nhá
Chú ý: Quan hệ N - N thì không thể clone sang DB khác được nhé, vì chưa chắc DB mới đã có thể sử dụng mối quan hệ bảng trung gian này
Vậy có tự tùy chỉnh - chọn clone trường này, không clone chỗ nào không?
Về mặc định thì qua ví dụ Clone đầu tiên thì chắc mọi người cũng để ý rồi, Cloner sẽ không clone Id
, cũng như updated_at
và created_at
. Tuy nhiên là bạn có thể chọn thêm vài trường khác để Cloner không động vào các giá trị này:
class Image extends Eloquent {
use \Bkwld\Cloner\Cloneable;
protected $clone_exempt_attributes = [
'img_name',
'source',
]; //khai báo các trường không clone
public function post() {
return $this->belongsTo('Post');
}
public function onCloning($src, $child = null) { //trong trường hợp cần clone
$this->img_name = str_random(); //nếu muốn có thể tự động thêm giá trị khác vào trường mà mình đã chọn không clone
if ($child) echo 'This was cloned as a relation!'; // biến $child giúp xác định xem quá trình clone này có clone cả relation hay không
echo 'The original key is: ' . $src->getKey();
}
}
Việc xử lý thêm hàm onCloning()
vô cùng hữu ích khi mà bạn có thêm 1 trường cần Unique nữa ngoài trường ID, bởi khi bạn Clone thì không thể Clone cả trường Unique đó được, vì làm vậy thì nó đâu còn Unique nữa :v, nhưng lại cũng không thể để trống trường này, vì nó là NOT NULL. Vậy thì bạn có thể gán giá trị khác cho nó trong quá trình clone như trên nhé.
Clone file thì sao?
Hmm, giờ 1 trong số các trường cần clone có dẫn tới 1 file, thì muốn clone cả cái file mà nó dẫn tới có được không? Cloner
cho phép bạn có thể làm điều này nhưng với sự support của Bkwld\Upchuck.
Bạn có thể đọc thêm hướng dẫn và ví dụ gốc ở đây nhé: https://github.com/BKWLD/cloner#cloning-files
Nguồn và tham khảo thêm
Bài viết mình dịch và làm rõ chi tiết hơn từ bài viết gốc cho mọi người sử dụng, mọi người có thể tham khảo bài viết gốc ở đây nhé: https://github.com/BKWLD/cloner
All rights reserved