Saya punya Animal
model, berdasarkan animal
tabel.
Tabel ini berisi type
bidang, yang bisa berisi nilai-nilai seperti kucing atau anjing .
Saya ingin dapat membuat objek seperti:
class Animal extends Model { }
class Dog extends Animal { }
class Cat extends Animal { }
Namun, bisa menjemput binatang seperti ini:
$animal = Animal::find($id);
Tetapi di mana $animal
akan menjadi contoh Dog
atau Cat
tergantung pada type
bidang, bahwa saya dapat memeriksa menggunakan instance of
atau yang akan bekerja dengan metode tipe mengisyaratkan. Alasannya adalah bahwa 90% dari kode dibagikan, tetapi satu dapat menggonggong, dan yang lainnya dapat mengeong.
Saya tahu bahwa saya dapat melakukannya Dog::find($id)
, tetapi bukan itu yang saya inginkan: Saya dapat menentukan jenis objek hanya setelah diambil. Saya juga bisa mengambil Hewan, dan kemudian berjalan find()
di objek yang tepat, tetapi ini melakukan dua panggilan basis data, yang jelas saya tidak mau.
Saya mencoba mencari cara untuk "secara manual" instantiate model Eloquent seperti Dog from Animal, tetapi saya tidak dapat menemukan metode yang sesuai. Adakah ide atau metode yang saya lewatkan?
*_type
nama untuk menentukan model subtipe. Dalam kasus saya, saya benar-benar hanya memiliki satu meja, jadi sementara itu fitur yang bagus, tidak dalam kasus saya.Jawaban:
Anda dapat menggunakan Hubungan Polimorfik di Laravel seperti yang dijelaskan dalam Dokumen Laravel Resmi . Inilah cara Anda dapat melakukannya.
Tentukan hubungan dalam model seperti yang diberikan
Di sini Anda akan membutuhkan dua kolom dalam
animals
tabel, yang pertama adalahanimable_type
dan yang lainnya adalahanimable_id
untuk menentukan jenis model yang dilampirkan pada saat runtime.Anda dapat mengambil model Anjing atau Kucing seperti yang diberikan,
Setelah itu, Anda dapat memeriksa kelas
$anim
objek dengan menggunakaninstanceof
.Pendekatan ini akan membantu Anda untuk ekspansi di masa depan jika Anda menambahkan jenis hewan lain (yaitu rubah atau singa) dalam aplikasi. Ini akan bekerja tanpa mengubah basis kode Anda. Ini adalah cara yang benar untuk mencapai kebutuhan Anda. Namun, tidak ada pendekatan alternatif untuk mencapai polimorfisme dan bersemangat memuat bersama tanpa menggunakan hubungan polimorfik. Jika Anda tidak menggunakan hubungan Polymorphic , Anda akan berakhir dengan lebih dari satu panggilan basis data. Namun, jika Anda memiliki satu kolom yang membedakan jenis modal, mungkin Anda memiliki skema terstruktur yang salah. Saya sarankan Anda meningkatkannya jika Anda ingin menyederhanakannya untuk pengembangan di masa depan juga.
Menulis ulang internal model
newInstance()
dannewFromBuilder()
bukan cara yang baik / disarankan dan Anda harus memperbaikinya setelah Anda mendapatkan pembaruan dari framework.sumber
Saya pikir Anda bisa mengganti
newInstance
metode padaAnimal
model, dan memeriksa tipe dari atribut dan kemudian init model yang sesuai.Anda juga harus mengganti
newFromBuilder
metode.sumber
type
dalam database?Jika Anda benar-benar ingin melakukan ini, Anda bisa menggunakan pendekatan berikut di dalam model Hewan Anda.
sumber
Seperti yang dinyatakan OP dalam komentarnya: Desain basis data sudah ditetapkan dan oleh karena itu Hubungan Polimorfik Laravel tampaknya tidak menjadi pilihan di sini.
Saya suka jawaban dari Chris Neal karena saya harus melakukan sesuatu yang serupa baru-baru ini (menulis Driver Database saya sendiri untuk mendukung Eloquent untuk file dbase / DBF) dan mendapatkan banyak pengalaman dengan internal Laravel's Eloquent ORM.
Saya telah menambahkan cita rasa pribadi saya untuk membuatnya agar kode lebih dinamis dengan tetap menjaga pemetaan eksplisit per model.
Fitur yang didukung yang saya uji dengan cepat:
Animal::find(1)
berfungsi seperti yang ditanyakan dalam pertanyaan AndaAnimal::all()
berfungsi jugaAnimal::where(['type' => 'dog'])->get()
akan mengembalikanAnimalDog
-objek sebagai koleksiAnimal
-model jika tidak ada pemetaan yang dikonfigurasi (atau pemetaan baru muncul di DB)Kekurangan:
newInstance()
dannewFromBuilder()
seluruhnya (salin dan tempel). Ini berarti jika akan ada pembaruan dari kerangka kerja ke fungsi anggota ini Anda harus mengadopsi kode dengan tangan.Saya harap ini membantu dan saya siap untuk saran, pertanyaan, dan kasus penggunaan tambahan dalam skenario Anda. Berikut adalah kasus penggunaan dan contoh untuk itu:
Dan ini adalah contoh bagaimana itu dapat digunakan dan di bawah hasil masing-masing untuk itu:
yang menghasilkan sebagai berikut:
Dan jika Anda ingin Anda menggunakan di
MorphTrait
sini tentu saja kode lengkap untuk itu:sumber
Saya rasa saya tahu apa yang Anda cari. Pertimbangkan solusi elegan ini yang menggunakan ruang lingkup permintaan Laravel, lihat https://laravel.com/docs/6.x/eloquent#query-scopes untuk informasi tambahan:
Buat kelas induk yang menyimpan logika bersama:
Buat anak (atau beberapa) dengan cakupan kueri global dan
saving
pengendali acara:(Hal yang sama berlaku untuk kelas lain
Cat
, cukup ganti konstanta)Ruang lingkup kueri global bertindak sebagai modifikasi kueri default, sehingga
Dog
kelas akan selalu mencari catatan dengantype='dog'
.Katakanlah kita memiliki 3 catatan:
Sekarang panggilan
Dog::find(1)
akan menghasilkannull
, karena ruang lingkup permintaan default tidak akan menemukan yangid:1
mana aCat
. MemanggilAnimal::find(1)
danCat::find(1)
keduanya akan berfungsi, meskipun hanya yang terakhir memberi Anda objek Cat yang sebenarnya.Yang menyenangkan dari pengaturan ini adalah Anda dapat menggunakan kelas-kelas di atas untuk membuat hubungan seperti:
Dan relasi ini secara otomatis hanya akan memberi Anda semua hewan dengan
type='dog'
(dalam bentukDog
kelas). Lingkup kueri diterapkan secara otomatis.Selain itu, panggilan
Dog::create($properties)
akan secara otomatis mengaturtype
ke'dog'
karenasaving
kait acara (lihat https://laravel.com/docs/6.x/eloquent#events ).Perhatikan bahwa panggilan
Animal::create($properties)
tidak memiliki standar,type
jadi di sini Anda perlu mengaturnya secara manual (yang diharapkan).sumber
Meskipun Anda menggunakan Laravel, dalam hal ini, saya pikir Anda tidak harus berpegang pada jalan pintas Laravel.
Masalah yang Anda coba selesaikan adalah masalah klasik yang dipecahkan oleh banyak bahasa / kerangka kerja lainnya menggunakan pola metode Pabrik ( https://en.wikipedia.org/wiki/Factory_method_pattern ).
Jika Anda ingin kode Anda lebih mudah dipahami dan tanpa trik tersembunyi, Anda harus menggunakan pola yang terkenal alih-alih trik tersembunyi / sulap di bawah tenda.
sumber
Cara termudah adalah dengan membuat metode di kelas Hewan
Menyelesaikan model
Ini akan mengembalikan instance dari kelas Hewan, Anjing atau Kucing tergantung pada jenis model
sumber