Kebutuhan User Model
PandaBear tidak menyediakan User Model sendiri dan tidak pernah meng-hard-code nama model tertentu.
Framework meminta user dari authentication guard:
$request->user()lalu bekerja dengan object apa pun yang dikembalikan guard tersebut.
Setiap fitur PandaBear hanya meminta capability tertentu dari object user.
Sebagian besar capability bersifat optional karena feature terkait juga dapat dimatikan.
Gunakan dokumentasi ini ketika:
- mengintegrasikan existing User Model dengan PandaBear;
- sebuah fitur tampak tidak bekerja;
- Anda ingin mengetahui interface, trait, method, atau column apa yang dicari framework.
Contoh Minimal
Panel dengan:
->auth()tidak memerlukan sesuatu di luar User Model Laravel standar.
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
/** @var list<string> */
protected $fillable = [
'name',
'email',
'password',
];
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Buat user:
php artisan panel:user \
--name="Ada" \
--email=ada@example.test \
--password=secret1232
3
4
Output:
INFO Created Ada <ada@example.test>.
INFO They can sign into the Admin panel at admin.2
Model minimal tersebut dapat:
- login;
- membuka Panel;
- menggunakan Page umum.
Capability di bawah ini hanya dibutuhkan jika feature terkait digunakan.
Kebutuhan per Feature
| Feature | Kebutuhan Model | Jika Tidak Ada |
|---|---|---|
| Login | Authenticatable melalui guard | Tidak ada authenticated user |
auth(verified: true) | MustVerifyEmail | verified middleware tidak menahan user |
| Per-account Panel access | PanelUser | Hanya Panel canAccess() yang diperiksa |
| Notification Centre | Laravel Notifiable | Bell 0 dan endpoint 403 |
| Email Code | notify() + two_factor_email_confirmed_at | Send 500 / factor off |
| TOTP | TwoFactorAuthenticatable | TOTP dianggap off |
| Passkeys | PasskeyUser + PasskeyAuthenticatable | passkeys=[] |
| Tenancy | HasPanelTenants | User dianggap tidak memiliki Tenant |
Semua check menggunakan:
method_exists()
atau
instanceof2
3
pada actual user object.
Framework tidak pernah melakukan:
class_exists(
'App\Models\User'
)2
3
Artinya custom model seperti:
App\Models\Adminmelalui secondary guard merupakan first-class use case.
Bagaimana User Model Ditemukan
HTTP request:
$request->user()mengikuti auth guard.
panel:user mengikuti config:
auth.defaults.guard
↓
auth.guards.{guard}.provider
↓
auth.providers.{provider}.model2
3
4
5
Contoh:
php artisan panel:user --guard=adminProvider tanpa resolvable model menghasilkan error yang jelas.
Panel Access
Contract:
namespace PandaPanel\Contracts;
interface PanelUser
{
public function canAccessPanel(
Panel $panel
): bool;
}2
3
4
5
6
7
8
Contoh:
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use PandaPanel\Contracts\PanelUser;
use PandaPanel\Core\Panel;
class User
extends Authenticatable
implements PanelUser
{
public function canAccessPanel(
Panel $panel
): bool {
return ! $this
->suspended;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Rule kedua berada pada Panel:
$panel->canAccess(
static fn (
?Authenticatable $user
): bool =>
$user?->is_admin
=== true
);2
3
4
5
6
7
Final check:
$panel
->isAccessibleTo(
$user
);2
3
4
Jika salah satu false:
falsePanel::canAccess() cocok untuk:
Panel ini khusus AdministratorPanelUser cocok untuk:
Account ini suspendedResolvePanel melakukan enforcement pada setiap request dengan 403.
Panel switcher juga menggunakan jawaban yang sama sehingga Panel yang tidak dapat diakses tidak ditawarkan.
Notification Centre
Contract:
namespace PandaPanel\Contracts;
interface PanelNotifiable
{
public function notifications();
public function unreadNotifications();
public function notify(
$instance
);
}2
3
4
5
6
7
8
9
10
11
12
Tidak ada native return types karena Laravel Notifiable trait juga tidak mendeklarasikannya.
Contoh:
use Illuminate\Notifications\Notifiable;
use PandaPanel\Contracts\PanelNotifiable;
class User
extends Authenticatable
implements PanelNotifiable
{
use Notifiable;
}2
3
4
5
6
7
8
9
Implementasi contract bersifat optional.
Notification controller juga menerima model yang sekadar memiliki method Laravel yang diperlukan.
Secara konsep:
abort_unless(
$user instanceof PanelNotifiable
|| (
is_object(
$user
)
&& method_exists(
$user,
'unreadNotifications'
)
),
403
);2
3
4
5
6
7
8
9
10
11
12
13
SharePanelData lebih toleran.
Jika:
- User tidak memiliki
unreadNotifications(); - notifications table belum dimigrate;
unread count menjadi:
0bukan membuat semua Panel Page error.
TOTP
Tambahkan:
use Laravel\Fortify\TwoFactorAuthenticatable;
class User
extends Authenticatable
{
use TwoFactorAuthenticatable;
}2
3
4
5
6
7
PandaBear memeriksa:
method_exists(
$user,
'hasEnabledTwoFactorAuthentication'
)
&& $user
->hasEnabledTwoFactorAuthentication();2
3
4
5
6
Trait digunakan daripada interface karena Fortify memang menyediakan behavior melalui trait.
Email Code
Email Code menggunakan column:
two_factor_email_confirmed_atCheck:
use PandaPanel\Auth\EmailCodeFactor;
EmailCodeFactor::isEnabledFor(
$user
);2
3
4
5
Raw attribute dibaca secara langsung untuk kompatibilitas dengan:
Model::preventAccessingMissingAttributes()Jika User Model diambil dengan narrowed select tanpa column tersebut, factor dianggap off.
Recommended casts:
protected function casts(): array
{
return [
'email_verified_at'
=> 'datetime',
'password'
=> 'hashed',
'two_factor_confirmed_at'
=> 'datetime',
'two_factor_email_confirmed_at'
=> 'datetime',
];
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Passkeys
Gunakan contract dan trait:
use Laravel\Fortify\Contracts\PasskeyUser;
use Laravel\Fortify\PasskeyAuthenticatable;
class User
extends Authenticatable
implements PasskeyUser
{
use PasskeyAuthenticatable;
}2
3
4
5
6
7
8
9
PandaBear Security Settings memerlukan:
instanceof PasskeyUseruntuk menampilkan list.
RequireTwoFactor hanya memeriksa method:
$user
->passkeys()
->exists();2
3
Karena itu model dengan trait tetapi tanpa interface mungkin lolos RequireTwoFactor tetapi Passkey list tetap kosong.
Solusinya:
deklarasikan trait dan contract.
Email Verification
Default:
$panel->auth();menambahkan:
auth
verified2
Tanpa verification:
$panel->auth(
verified: false
);2
3
Laravel verified middleware hanya bekerja untuk model yang mengimplementasikan:
Illuminate\Contracts\Auth\MustVerifyEmailJika interface tidak ada, middleware membiarkan user lewat.
Profile Settings juga menggunakan interface yang sama:
'mustVerifyEmail' =>
Auth::user()
instanceof MustVerifyEmail,2
3
Tenancy
Untuk tenant-scoped Panel gunakan:
PandaPanel\Contracts\HasPanelTenantsContract:
interface HasPanelTenants
{
public function getPanelTenants(
Panel $panel
): Collection;
public function canAccessPanelTenant(
Model $tenant,
Panel $panel
): bool;
}2
3
4
5
6
7
8
9
10
11
Contoh:
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use PandaPanel\Contracts\HasPanelTenants;
use PandaPanel\Core\Panel;
class User
extends Authenticatable
implements HasPanelTenants
{
/**
* @return BelongsToMany<
* Workspace,
* $this
* >
*/
public function workspaces():
BelongsToMany
{
return $this
->belongsToMany(
Workspace::class
);
}
/**
* @return Collection<int, Model>
*/
public function getPanelTenants(
Panel $panel
): Collection {
return $this
->workspaces()
->orderBy('id')
->get();
}
public function canAccessPanelTenant(
Model $tenant,
Panel $panel
): bool {
return $this
->workspaces()
->whereKey(
$tenant->getKey()
)
->exists();
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Dua method dipisahkan karena:
getPanelTenants()
→ display concern / switcher
canAccessPanelTenant()
→ security concern2
3
4
5
User tanpa contract dianggap tidak memiliki Tenant.
Columns
| Column | Ditulis oleh | Dibaca oleh |
|---|---|---|
name | Registration, panel:user | Profile/User menu |
email | Registration, panel:user | Profile/Auth |
password | Registration, panel:user | Auth |
email_verified_at | Laravel/panel:user | verified middleware |
two_factor_email_confirmed_at | PandaBear | Email Code |
two_factor_secret | Fortify | TOTP |
two_factor_recovery_codes | Fortify | TOTP |
two_factor_confirmed_at | Fortify | TOTP |
Hanya:
two_factor_email_confirmed_atyang ditambahkan PandaBear.
Publish migration:
php artisan vendor:publish --tag=panda-panel-migrations
php artisan migrate2
Migration defensive:
- tidak berjalan jika
userstidak ada; - tidak duplicate jika column sudah ada;
- menempatkan column setelah Fortify column jika tersedia.
Hidden Credentials
Recommended:
protected $hidden = [
'password',
'remember_token',
'two_factor_secret',
'two_factor_recovery_codes',
];2
3
4
5
6
Ini sangat penting karena application biasanya membagikan:
$request->user()sebagai:
auth.userke Inertia pada setiap Page.
Attribute yang tidak hidden dapat masuk ke serialized HTML payload.
User Shape yang Diharapkan Frontend
PandaBear tidak membagikan user.
Application harus menyediakan:
'auth' => [
'user' =>
$request->user(),
],2
3
4
Published components membaca:
| Component | Data |
|---|---|
NavUser.vue | auth.user |
Profile.vue | name, email, email_verified_at |
UserInfo dan UserMenuContent adalah host application components.
Package tidak menentukannya karena account menu merupakan application concern.
Shared TypeScript type:
export type User = {
id: number
name: string
email: string
avatar?: string
email_verified_at: string | null
two_factor_enabled?: boolean
created_at: string
updated_at: string
[key: string]: unknown
}2
3
4
5
6
7
8
9
10
11
Jika model menggunakan field berbeda dari:
nameAnda dapat:
- membuat accessor;
- atau menyesuaikan/fork host components.
Server-side PandaBear sendiri tidak membutuhkan exact property name.
Membuat User
php artisan panel:userOptions:
| Option | Arti | Default |
|---|---|---|
--name | Nama account | prompt |
--email | Login email | prompt |
--password | Password | hidden prompt |
--guard | Guard/model yang digunakan | application default |
--panel | Panel untuk access report | first Panel |
Command menggunakan:
forceFill()untuk:
name
email
hashed password
email_verified_at = now()2
3
4
Validation:
name:
required|string|max:255
email:
required|string|email|max:255
password:
required|string|min:82
3
4
5
6
7
8
Password command sengaja tidak menggunakan public registration password policy.
Setelah membuat account, command melaporkan hasil:
Panel::canAccess()
+
PanelUser::canAccessPanel()2
3
tetapi tidak membatalkan account jika belum boleh masuk.
Notes Penting
Broadcast Channel Name Fixed
PandaPanel\Broadcasting\PanelNotification::channelFor()menghasilkan:
App.Models.User.{auth identifier}bahkan jika actual model adalah:
App\Models\AdminJadi channel authorization perlu menggunakan nama tersebut.
Jangan Letakkan Privilege Flag di $fillable
Contoh berbahaya:
protected $fillable = [
'name',
'email',
'password',
'is_admin',
];2
3
4
5
6
Registration/profile write dapat membuka peluang privilege escalation jika input tambahan masuk ke request.
Promosikan privilege melalui explicit action/forceFill().
Tanpa Access Rules, Account Tidak Ditolak
Jika:
- User tidak mengimplementasikan
PanelUser; - Panel tidak mendeklarasikan
canAccess();
maka account tidak ditolak oleh Panel access layer.
canAccessPanel() Dipanggil Setiap Request
Method berjalan sebelum Page dibuat dan sebelum:
Panel::boot()Jaga agar murah.
Security Settings Membutuhkan password.confirm
Application harus menyediakan:
Fortify::confirmPasswordView()panel:user Aman untuk Non-Interactive Environment
Tanpa TTY, missing option menghasilkan validation error daripada command menunggu input selamanya.