モデル
Lase が提供する Eloquent モデルの一覧です。すべて CodebaseJp\Lase\Models 名前空間に属します。
コアモデル
Tenant
テナント(組織)を表すモデルです。
| カラム | 説明 |
|---|---|
id | 主キー |
public_id | 外部公開用 ID(プレフィックス: tnt_) |
code | サブドメインに使用されるユニークコード |
name | テナント名 |
email | 連絡先メールアドレス |
tel | 電話番号 |
contact_person | 担当者名 |
logo_path | ロゴ画像パス |
current_subscription_id | 現在のサブスクリプション ID |
suspended_at | 緊急停止した日時。NULL なら稼働中 |
suspension_reason | 緊急停止の理由(運用の記録用。テナント側には出しません) |
opening_time / closing_time | 営業時間(既定は 9:00〜18:00) |
business_weekdays | 営業する曜日(ISO の曜日番号の配列。NULL なら月〜金) |
休日は TenantHoliday(tenant_holidays。1日に1つ。is_public は日本の祝日を取り込んだもの)に持ち、$tenant->holidays で引けます。営業日の数え方は BusinessCalendar(サービス)です。
TIP
テナントオブジェクトはキャッシュ(tenant:{code}、TTL 60 分)に保存され、保存・削除時に自動でキャッシュが破棄されます。
緊急停止の状態は suspended_at だけが持ちます(NULL なら稼働中)。状態を表すカラムを別に持つと二重管理になるため、status のような列は設けていません。「誰が停止したか」はアクティビティログ(ADMIN_SUSPEND_TENANT / ADMIN_RESUME_TENANT)が持つため、テナント側には持たせていません。
Tenant::query()->active()->get(); // suspended_at が NULL のみ
$tenant->isSuspended(); // 停止中なら true緊急停止の状態は tenants のカラムに持つ
緊急停止は上記のキャッシュ破棄に相乗りすることで即時に反映されます。別テーブルに持つと Tenant の保存イベントが発火せず、停止が最大 60 分効きません。同じ理由で、 停止はキャッシュを共有するストア(Redis 等)を前提にしています。
User
テナントユーザーを表すモデルです。
| カラム | 説明 |
|---|---|
id | 主キー |
public_id | 外部公開用 ID(プレフィックス: usr_) |
name | 表示名 |
email | メールアドレス |
status | ステータス (UserStatus Enum) |
deleted_at | ソフトデリート |
AdminUser
管理画面にアクセスできる管理者ユーザーを表すモデルです。
| カラム | 説明 |
|---|---|
id | 主キー |
public_id | 外部公開用 ID |
name | 表示名 |
email | メールアドレス |
status | ステータス (AdminUserStatus Enum) |
TenantUser
テナントとユーザーの中間テーブルに対応するモデルです。テナントへの所属情報(ロール、ステータスなど)を保持します。
認証・招待
| モデル | 説明 |
|---|---|
AdminUserInvitation | 管理者の招待レコード |
TenantUserInvitation | テナントユーザーの招待レコード |
EmailChange | メールアドレス変更リクエスト |
ロール・権限
| モデル | 説明 |
|---|---|
AdminRole | 管理者ロール |
AdminRolePermission | 管理者ロールの権限 |
TenantRole | テナントロール |
TenantRolePermission | テナントロールの権限 |
役割は、テナントユーザー・管理者・パートナー担当者のどれも1人1つです(役割なしもあります)。所属の列(tenant_users.tenant_role_id・admin_users.admin_role_id・partner_users.partner_role_id)で持ち、リレーションは tenantRole() / adminRole() / partnerRole() です。役割なし、または役割が削除済みのときは権限を持ちません。役割を削除すると、その役割を持つ所属は役割なしになります。
複数の職務を兼ねる人には、権限を組み合わせた役割を作って割り当ててください。
サブスクリプション・プラン
Plan はテナントをまたぐ共通カタログです(tenant_id を持ちません)。テナントに見せるプランは Plan::selectable(?Subscription $currentSubscription) スコープで絞ります。公開中のノーマルプランは常に対象で、トライアル契約中はトライアルプラン、スペシャルプラン契約中は契約中のプラン自体(非公開でも)が加わります。テナント API の一覧・詳細はどちらもこのスコープを通します。
Plan ──[1:N]── PlanFeature (機能フラグ)
Plan ──[1:N]── PlanQuota (利用上限)
Plan ──[N:M]── Option (PlanOptionGroup を介して)
Subscription ──[N:1]── Plan
Subscription ──[N:M]── Option (SubscriptionOption)
Subscription ──[1:N]── SubscriptionFeature
Subscription ──[1:N]── SubscriptionQuota
Subscription ──[1:N]── SubscriptionUsage
Tenant ──[1:1]── Subscription (current_subscription_id)
Tenant ──[1:N]── GrantedFeature
Tenant ──[1:N]── GrantedQuota
Tenant ──[1:N]── GrantedUsage| モデル | 説明 |
|---|---|
Plan | 料金プラン |
PlanFeature | プランの機能フラグ |
PlanQuota | プランの利用上限 |
Option | オプション機能 |
OptionFeature | オプションの機能フラグ |
OptionQuota | オプションの利用上限 |
Addon | アドオン |
AddonFeature | アドオンの機能フラグ |
AddonQuota | アドオンの利用上限 |
Subscription | サブスクリプション |
SubscriptionFeature | サブスクリプションの機能フラグ |
SubscriptionQuota | サブスクリプションの利用上限 |
SubscriptionUsage | サブスクリプションの利用量 |
TenantAddon | テナントのアドオン紐付け |
GrantedFeature | テナントに直接付与された機能フラグ(テーブル: granted_features) |
GrantedQuota | テナントに直接付与された利用上限(テーブル: granted_quotas) |
GrantedUsage | テナントの利用量(テーブル: granted_usages) |
RegistrationPlanRule | 登録時のプランの規則。申込みフォームから登録したテナントに、トライアルの代わりに付けるプランを決める(テーブル: registration_plan_rules。運用) |
統計・利用量
| モデル | 説明 |
|---|---|
DailyUsage | 日次利用量 |
MonthlyUsage | 月次利用量 |
コンテンツ
| モデル | 説明 |
|---|---|
Announcement | 運営からのお知らせ。published() で利用者に見せるもの(公開で、日付が今日まで)、urgent() で緊急表示の期限(urgent_until)の内のものに絞る |
AdminImage | 管理者がアップロードした画像 |
AdminMail | 管理者が作成したメール |
AdminMailDelivery | メール配信記録 |
Inquiry | お問い合わせ |
BlockedDomain | 禁止ドメイン |
BlockedTenantCode | テナントコードとして使用不可の予約語(テーブル: blocked_tenant_codes、初期データはマイグレーションで投入) |
ActivityLog | 操作ログ |
TenantScreening | テナント登録時の生成AIによる審査結果(テーブル: tenant_screenings)。再審査を積み重ねられるよう tenant_id に unique は張らない。reviewed_at は運営が確認を済ませたかの状態で、誰が確認したかはアクティビティログ(ADMIN_REVIEW_TENANT_SCREENING)が持つ。Tenant::screenings() / Tenant::latestScreening() から引ける |
Concerns (Trait)
BelongsToTenant
テナントに属するモデル(tenant_id を持つモデル)に付与します。テナントのコンテキスト配下では、自テナントのレコードだけを対象にするグローバルスコープ(tenant)が適用されます。
class Subscription extends Model
{
use HasFactory, HasUlids, SoftDeletes, BelongsToTenant;
}コンテキストの有無は IdentifyTenant が現在のテナントを解決したかどうかで決まります。
| 実行文脈 | スコープ |
|---|---|
テナント API(routes/api/tenant.php) | 適用される |
| 管理画面・公開ページ | 適用されない(IdentifyTenant を通らないため) |
| コンソール・キュー | 適用されない(HTTP リクエストがないため) |
これにより、ルートモデルバインディングが他テナントのレコードを解決しなくなります(404 になります)。テナント API で ID を受け取るエンドポイントの多くはこの経路を通るため、テナント境界の一次防御として機能します。
意図的にテナントをまたいで参照する場合は、標準のグローバルスコープの外し方を使います。
Subscription::withoutGlobalScope('tenant')->findOrFail($id);特定のテナントを明示して絞る場合は forTenant() を使います(スコープの有無に関わらず動作します)。
Subscription::forTenant($tenantId)->get();WARNING
tenant_id を持っていても、テナントに属さないモデルには付与しません。User(tenant_id_to_be_deleted は移行前の残骸)、AdminMail(送信先テナントを持つ管理側のエンティティ)、MCP の OAuth 認可コード・トークン(認可サーバーがテナントのコンテキスト外で参照する)が該当します。
HasPublicId
public_id カラムを持つモデルに付与します。作成時に自動で {prefix}_{nanoid} 形式の ID が生成されます。
protected string $publicIdPrefix = 'tnt_';