Jawaban singkat
Opsi ketiga: Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
Jawaban panjang
Di satu sisi, (hampir) semua yang dapat Anda lakukan dalam kode, adalah kinerja yang lebih baik, daripada melakukannya dalam kueri.
Di sisi lain, mendapatkan lebih banyak data dari basis data dari yang diperlukan akan terlalu banyak data (penggunaan RAM dan sebagainya).
Dari sudut pandang saya, Anda membutuhkan sesuatu di antaranya, dan hanya Anda yang akan tahu di mana keseimbangannya, tergantung pada jumlahnya.
Saya sarankan menjalankan beberapa kueri, opsi terakhir yang Anda usulkan ( Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Permintaan semua pengidentifikasi, untuk semua izin (5 pertanyaan)
- Menggabungkan semua formulir menghasilkan memori, dan dapatkan nilai unik
array_unique($ids)
- Permintaan model Formulir, menggunakan pengidentifikasi dalam pernyataan IN ().
Anda dapat mencoba tiga opsi yang Anda usulkan dan memantau kinerja, menggunakan beberapa alat untuk menjalankan kueri beberapa kali, tetapi saya 99% yakin bahwa yang terakhir akan memberi Anda kinerja terbaik.
Ini juga dapat banyak berubah, tergantung pada basis data yang Anda gunakan, tetapi jika kita berbicara tentang MySQL, misalnya; Dalam Permintaan yang sangat besar akan menggunakan lebih banyak sumber daya database, yang tidak hanya akan menghabiskan lebih banyak waktu daripada permintaan sederhana, tetapi juga akan mengunci tabel dari menulis, dan ini dapat menghasilkan kesalahan kebuntuan (kecuali jika Anda menggunakan server slave).
Di sisi lain, jika jumlah bentuk id sangat besar, Anda dapat memiliki kesalahan untuk placeholder terlalu banyak, jadi Anda mungkin ingin memotong kueri dalam grup, katakanlah, 500 id (ini tergantung banyak, sebagai batas dalam ukuran, bukan dalam jumlah binding), dan menggabungkan hasilnya dalam memori. Bahkan jika Anda tidak mendapatkan kesalahan basis data, Anda mungkin melihat perbedaan besar dalam kinerja juga (saya masih berbicara tentang MySQL).
Penerapan
Saya akan berasumsi bahwa ini adalah skema basis data:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Jadi diizinkan adalah hubungan polimorfik yang sudah terkonfigurasi .
Karena itu, hubungannya adalah:
- Formulir Milik:
users.id <-> form.user_id
- Tim memiliki Formulir:
users.team_id <-> form.team_id
- Memiliki izin ke grup yang memiliki Formulir:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- Memiliki izin ke tim yang memiliki Formulir:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- Memiliki izin untuk Formulir:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Sederhanakan versi:
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
Versi detail:
// Owns Form
// users.id <-> forms.user_id
$userId = $user->id;
// Team owns Form
// users.team_id <-> forms.team_id
// Initialise the array with a first value.
// The permissions polymorphic relationship will have other teams ids to look at
$teamIds = [$user->team_id];
// Groups owns Form was not mention, so I assume there is not such a relation in user.
// Just initialise the array without a first value.
$groupIds = [];
// Also initialise forms for permissions:
$formIds = [];
// Has permissions to a group that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
$teamMorphType = Relation::getMorphedModel('team');
// Has permissions to a team that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
$groupMorphType = Relation::getMorphedModel('group');
// Has permission to a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Form'
$formMorphType = Relation::getMorphedModel('form');
// Get permissions
$permissibles = $user->permissible()->whereIn(
'permissible_type',
[$teamMorphType, $groupMorphType, $formMorphType]
)->get();
// If you don't have more permissible types other than those, then you can just:
// $permissibles = $user->permissible;
// Group the ids per type
foreach ($permissibles as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
$teamIds[] = $permissible->permissible_id;
break;
case $groupMorphType:
$groupIds[] = $permissible->permissible_id;
break;
case $formMorphType:
$formIds[] = $permissible->permissible_id;
break;
}
}
// In case the user and the team ids are repeated:
$teamIds = array_values(array_unique($teamIds));
// We assume that the rest of the values will not be repeated.
$forms = Form::query()
->where('user_id', '=', $userId)
->orWhereIn('id', $formIds)
->orWhereIn('team_id', $teamIds)
->orWhereIn('group_id', $groupIds)
->get();
Sumber daya yang digunakan:
Kinerja basis data:
- Permintaan ke database (tidak termasuk pengguna): 2 ; satu untuk mendapatkan yang diizinkan dan satu lagi untuk mendapatkan formulir.
- Tidak ada yang bergabung !!
- OR minimum yang mungkin (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, dalam memori, kinerja:
- foreach perulangan diizinkan dengan saklar di dalamnya.
array_values(array_unique())
untuk menghindari pengulangan id.
- Dalam memori, 3 array id (
$teamIds
, $groupIds
, $formIds
)
- Dalam memori, izin relevan koleksi fasih (ini dapat dioptimalkan, jika perlu).
Pro dan kontra
PROS:
- Waktu : Jumlah waktu kueri tunggal kurang dari waktu kueri besar dengan bergabung dan ATAU.
- Sumber Daya DB : Sumber daya MySQL yang digunakan oleh kueri dengan gabungan dan atau pernyataan, lebih besar dari yang digunakan oleh jumlah kueri yang terpisah.
- Uang : Lebih sedikit sumber daya basis data (prosesor, RAM, disk baca, dll.), Yang lebih mahal daripada sumber daya PHP.
- Kunci : Jika Anda tidak meminta server slave baca-saja, kueri Anda akan membuat lebih sedikit baris kunci baca (kunci baca dibagi di MySQL, sehingga tidak akan mengunci read lain, tetapi itu akan memblokir penulisan apa pun).
- Dapat diskalakan : Pendekatan ini memungkinkan Anda untuk membuat lebih banyak optimasi kinerja seperti memotong kueri.
CONS:
- Sumberdaya kode : Membuat perhitungan dalam kode, bukan dalam database, jelas akan mengkonsumsi lebih banyak sumber daya dalam contoh kode, tetapi terutama dalam RAM, menyimpan informasi tengah. Dalam kasus kami, ini akan menjadi array id, yang seharusnya tidak menjadi masalah.
- Pemeliharaan : Jika Anda menggunakan properti dan metode Laravel, dan Anda melakukan perubahan apa pun dalam database, akan lebih mudah untuk memperbarui dalam kode daripada jika Anda membuat pertanyaan dan pemrosesan yang lebih eksplisit.
- Terlalu banyak membunuh? : Dalam beberapa kasus, jika data tidak sebesar itu, mengoptimalkan kinerja mungkin terlalu banyak.
Bagaimana mengukur kinerja
Beberapa petunjuk tentang cara mengukur kinerja?
- Log kueri lambat
- TABEL ANALISIS
- TAMPILKAN STATUS TABEL SEPERTI
- MENJELASKAN ; Format Output EXPLAIN yang Diperpanjang ; menggunakan jelaskan ; jelaskan output
- TAMPILKAN PERINGATAN
Beberapa alat profil yang menarik: