feat(mng): 시스템 필드 정의 DB 기반 관리 기능 구현
## 주요 변경사항
### 1. 시스템 필드 정의 DB 마이그레이션
- 기존 하드코딩된 Constants/SystemFieldDefinitions.php 제거
- 신규 Models/SystemFieldDefinition.php 모델 추가
- system_field_definitions 테이블 기반 동적 관리
### 2. 테이블 등록 시 자동 필드 생성
- DB 실제 테이블 목록에서 선택하여 등록
- MySQL INFORMATION_SCHEMA에서 컬럼 정보 자동 조회
- COLUMN_COMMENT를 필드명(한글)으로 사용
- IS_NULLABLE로 필수 여부 자동 설정
- 시스템 컬럼(id, tenant_id, timestamps 등) 자동 제외
### 3. 필드명 동기화 기능
- 기존 등록된 테이블의 필드명을 DB COMMENT로 업데이트
- POST /source-tables/{table}/sync-field-names API 추가
### 4. 시딩 상태 계산 수정
- getFieldCountFor(): is_seed_default=true인 필드만 카운트
- getTotalFieldCountFor(): 전체 활성 필드 카운트 (신규)
- "제외" 필드가 있어도 시딩 완료 상태 정상 표시
### 5. UI 개선
- 시스템 필드 정의 탭에서 테이블별 관리
- 테이블 헤더에 "필드명 동기화", "삭제" 버튼 추가
- 테이블 선택 모달에서 COMMENT(한글명) 표시
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
|
||||
use App\Constants\SystemFieldDefinitions;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\SystemFieldDefinition;
|
||||
use App\Services\ItemFieldSeedingService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
@@ -148,11 +150,12 @@ public function resetAll(Request $request): JsonResponse
|
||||
public function customFields(Request $request): View
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
$sourceTables = SystemFieldDefinition::getSourceTableOptions();
|
||||
|
||||
if (! $tenantId || $tenantId === 'all') {
|
||||
return view('item-fields.partials.custom-fields', [
|
||||
'fields' => collect([]),
|
||||
'sourceTables' => SystemFieldDefinitions::SOURCE_TABLES,
|
||||
'sourceTables' => $sourceTables,
|
||||
'error' => '테넌트를 선택해주세요.',
|
||||
]);
|
||||
}
|
||||
@@ -162,7 +165,7 @@ public function customFields(Request $request): View
|
||||
|
||||
return view('item-fields.partials.custom-fields', [
|
||||
'fields' => $fields,
|
||||
'sourceTables' => SystemFieldDefinitions::SOURCE_TABLES,
|
||||
'sourceTables' => $sourceTables,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -274,7 +277,7 @@ public function sourceTables(): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => SystemFieldDefinitions::SOURCE_TABLES,
|
||||
'data' => SystemFieldDefinition::getSourceTableOptions(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -364,4 +367,503 @@ protected function buildErrorReport(array $errorLog, ?int $tenantId): string
|
||||
|
||||
return $report;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 시스템 필드 정의 관리 (system_field_definitions 테이블)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 시스템 필드 정의 목록 (HTMX partial)
|
||||
*/
|
||||
public function systemFieldDefinitions(Request $request): View
|
||||
{
|
||||
$query = SystemFieldDefinition::query()
|
||||
->where('is_active', true)
|
||||
->orderBy('source_table')
|
||||
->orderBy('order_no');
|
||||
|
||||
// 소스 테이블 필터
|
||||
if ($request->filled('source_table')) {
|
||||
$query->where('source_table', $request->input('source_table'));
|
||||
}
|
||||
|
||||
// 검색
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->input('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('field_key', 'like', "%{$search}%")
|
||||
->orWhere('field_name', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
$definitions = $query->get();
|
||||
$sourceTables = SystemFieldDefinition::getSourceTableOptions();
|
||||
|
||||
return view('item-fields.partials.system-field-definitions', compact('definitions', 'sourceTables'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 시스템 필드 정의 추가
|
||||
*/
|
||||
public function storeSystemFieldDefinition(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'source_table' => 'required|string|max:50',
|
||||
'source_table_label' => 'required|string|max:50',
|
||||
'field_key' => 'required|string|max:100|regex:/^[a-zA-Z0-9_]+$/',
|
||||
'field_name' => 'required|string|max:100',
|
||||
'field_type' => 'required|string|in:textbox,number,dropdown,checkbox,date,textarea',
|
||||
'order_no' => 'nullable|integer|min:0',
|
||||
'is_required' => 'nullable|boolean',
|
||||
'is_seed_default' => 'nullable|boolean',
|
||||
'default_value' => 'nullable|string|max:255',
|
||||
'options' => 'nullable|array',
|
||||
]);
|
||||
|
||||
// 중복 체크
|
||||
$exists = SystemFieldDefinition::where('source_table', $validated['source_table'])
|
||||
->where('field_key', $validated['field_key'])
|
||||
->exists();
|
||||
|
||||
if ($exists) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '이미 존재하는 필드 키입니다.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
// order_no가 없으면 마지막 순서로 설정
|
||||
if (! isset($validated['order_no'])) {
|
||||
$maxOrder = SystemFieldDefinition::where('source_table', $validated['source_table'])->max('order_no');
|
||||
$validated['order_no'] = ($maxOrder ?? 0) + 1;
|
||||
}
|
||||
|
||||
$validated['is_required'] = $validated['is_required'] ?? false;
|
||||
$validated['is_seed_default'] = $validated['is_seed_default'] ?? true;
|
||||
$validated['is_active'] = true;
|
||||
|
||||
SystemFieldDefinition::create($validated);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '시스템 필드 정의가 추가되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시스템 필드 정의 수정
|
||||
*/
|
||||
public function updateSystemFieldDefinition(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$definition = SystemFieldDefinition::find($id);
|
||||
|
||||
if (! $definition) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '시스템 필드 정의를 찾을 수 없습니다.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'source_table' => 'nullable|string|max:50',
|
||||
'source_table_label' => 'nullable|string|max:50',
|
||||
'field_key' => 'nullable|string|max:100|regex:/^[a-zA-Z0-9_]+$/',
|
||||
'field_name' => 'required|string|max:100',
|
||||
'field_type' => 'required|string|in:textbox,number,dropdown,checkbox,date,textarea',
|
||||
'order_no' => 'nullable|integer|min:0',
|
||||
'is_required' => 'nullable|boolean',
|
||||
'is_seed_default' => 'nullable|boolean',
|
||||
'default_value' => 'nullable|string|max:255',
|
||||
'options' => 'nullable|array',
|
||||
]);
|
||||
|
||||
// source_table 또는 field_key 변경 시 중복 체크
|
||||
$newSourceTable = $validated['source_table'] ?? $definition->source_table;
|
||||
$newFieldKey = $validated['field_key'] ?? $definition->field_key;
|
||||
|
||||
if ($newSourceTable !== $definition->source_table || $newFieldKey !== $definition->field_key) {
|
||||
$exists = SystemFieldDefinition::where('source_table', $newSourceTable)
|
||||
->where('field_key', $newFieldKey)
|
||||
->where('id', '!=', $id)
|
||||
->exists();
|
||||
|
||||
if ($exists) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '이미 존재하는 필드 키입니다.',
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
$definition->update($validated);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '시스템 필드 정의가 수정되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시스템 필드 정의 삭제
|
||||
*/
|
||||
public function destroySystemFieldDefinition(int $id): JsonResponse
|
||||
{
|
||||
$definition = SystemFieldDefinition::find($id);
|
||||
|
||||
if (! $definition) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '시스템 필드 정의를 찾을 수 없습니다.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$definition->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '시스템 필드 정의가 삭제되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시스템 필드 정의 순서 변경
|
||||
*/
|
||||
public function reorderSystemFieldDefinitions(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'items' => 'required|array',
|
||||
'items.*.id' => 'required|integer',
|
||||
'items.*.order_no' => 'required|integer|min:0',
|
||||
]);
|
||||
|
||||
foreach ($validated['items'] as $item) {
|
||||
SystemFieldDefinition::where('id', $item['id'])->update(['order_no' => $item['order_no']]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '순서가 변경되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 소스 테이블 추가 (DB 테이블의 컬럼을 자동으로 필드 정의로 생성)
|
||||
*/
|
||||
public function storeSourceTable(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'source_table' => 'required|string|max:50',
|
||||
'source_table_label' => 'required|string|max:50',
|
||||
]);
|
||||
|
||||
$tableName = $validated['source_table'];
|
||||
$tableLabel = $validated['source_table_label'];
|
||||
|
||||
// 실제 DB에 테이블이 존재하는지 확인
|
||||
if (! Schema::hasTable($tableName)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '데이터베이스에 존재하지 않는 테이블입니다.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
// 이미 등록된 테이블인지 확인
|
||||
$exists = SystemFieldDefinition::where('source_table', $tableName)->exists();
|
||||
|
||||
if ($exists) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '이미 존재하는 테이블입니다.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
// 시스템/메타 컬럼 제외 목록
|
||||
$excludedColumns = [
|
||||
'id', 'tenant_id', 'created_at', 'updated_at', 'deleted_at',
|
||||
'created_by', 'updated_by', 'deleted_by',
|
||||
];
|
||||
|
||||
// MySQL INFORMATION_SCHEMA에서 컬럼 정보 조회 (COMMENT 포함)
|
||||
$dbName = config('database.connections.mysql.database');
|
||||
$columnInfos = DB::select("
|
||||
SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_COMMENT, IS_NULLABLE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?
|
||||
ORDER BY ORDINAL_POSITION
|
||||
", [$dbName, $tableName]);
|
||||
|
||||
$createdCount = 0;
|
||||
$orderNo = 1;
|
||||
|
||||
foreach ($columnInfos as $columnInfo) {
|
||||
$column = $columnInfo->COLUMN_NAME;
|
||||
|
||||
// 시스템 컬럼 제외
|
||||
if (in_array($column, $excludedColumns)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columnType = Schema::getColumnType($tableName, $column);
|
||||
$fieldType = $this->suggestFieldType($columnType);
|
||||
|
||||
// COMMENT가 있으면 사용, 없으면 컬럼명 변환
|
||||
$fieldName = ! empty($columnInfo->COLUMN_COMMENT)
|
||||
? $columnInfo->COLUMN_COMMENT
|
||||
: ucwords(str_replace('_', ' ', $column));
|
||||
|
||||
// 필수 여부 (NOT NULL이면 필수)
|
||||
$isRequired = $columnInfo->IS_NULLABLE === 'NO';
|
||||
|
||||
SystemFieldDefinition::create([
|
||||
'source_table' => $tableName,
|
||||
'source_table_label' => $tableLabel,
|
||||
'field_key' => $column,
|
||||
'field_name' => $fieldName,
|
||||
'field_type' => $fieldType,
|
||||
'order_no' => $orderNo++,
|
||||
'is_required' => $isRequired,
|
||||
'is_seed_default' => true,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$createdCount++;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => "'{$tableLabel}' 테이블이 등록되었습니다. ({$createdCount}개 필드 생성)",
|
||||
'field_count' => $createdCount,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 소스 테이블 삭제 (해당 테이블의 모든 필드 정의 삭제)
|
||||
*/
|
||||
public function destroySourceTable(string $sourceTable): JsonResponse
|
||||
{
|
||||
$count = SystemFieldDefinition::where('source_table', $sourceTable)->count();
|
||||
|
||||
if ($count === 0) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '테이블을 찾을 수 없습니다.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
SystemFieldDefinition::where('source_table', $sourceTable)->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => "테이블과 {$count}개의 필드 정의가 삭제되었습니다.",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 소스 테이블 필드명 동기화 (DB COMMENT로 업데이트)
|
||||
*/
|
||||
public function syncSourceTableFieldNames(string $sourceTable): JsonResponse
|
||||
{
|
||||
// 등록된 테이블인지 확인
|
||||
$fields = SystemFieldDefinition::where('source_table', $sourceTable)->get();
|
||||
|
||||
if ($fields->isEmpty()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '등록되지 않은 테이블입니다.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
// 실제 DB에 테이블이 존재하는지 확인
|
||||
if (! Schema::hasTable($sourceTable)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '데이터베이스에 존재하지 않는 테이블입니다.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
// MySQL INFORMATION_SCHEMA에서 컬럼 정보 조회 (COMMENT 포함)
|
||||
$dbName = config('database.connections.mysql.database');
|
||||
$columnInfos = DB::select("
|
||||
SELECT COLUMN_NAME, COLUMN_COMMENT, IS_NULLABLE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?
|
||||
", [$dbName, $sourceTable]);
|
||||
|
||||
// 컬럼명 => COMMENT 매핑
|
||||
$columnComments = collect($columnInfos)->keyBy('COLUMN_NAME');
|
||||
|
||||
$updatedCount = 0;
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$columnInfo = $columnComments->get($field->field_key);
|
||||
|
||||
if ($columnInfo && ! empty($columnInfo->COLUMN_COMMENT)) {
|
||||
// COMMENT가 있고, 현재 필드명과 다르면 업데이트
|
||||
if ($field->field_name !== $columnInfo->COLUMN_COMMENT) {
|
||||
$field->update([
|
||||
'field_name' => $columnInfo->COLUMN_COMMENT,
|
||||
'is_required' => $columnInfo->IS_NULLABLE === 'NO',
|
||||
]);
|
||||
$updatedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => $updatedCount > 0
|
||||
? "{$updatedCount}개 필드명이 DB COMMENT로 업데이트되었습니다."
|
||||
: '업데이트할 필드가 없습니다. (이미 동기화됨)',
|
||||
'updated_count' => $updatedCount,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 테이블 목록 조회 (등록 가능한 테이블)
|
||||
*/
|
||||
public function databaseTables(Request $request): JsonResponse
|
||||
{
|
||||
// 이미 등록된 테이블 목록
|
||||
$registeredTables = SystemFieldDefinition::pluck('source_table')->unique()->toArray();
|
||||
|
||||
// 시스템 테이블 제외 목록
|
||||
$excludedTables = [
|
||||
'migrations', 'password_reset_tokens', 'sessions', 'cache', 'cache_locks',
|
||||
'jobs', 'job_batches', 'failed_jobs', 'personal_access_tokens',
|
||||
'system_field_definitions', 'item_fields', 'item_field_values',
|
||||
'audit_logs', 'activity_log', 'telescope_entries', 'telescope_entries_tags',
|
||||
'telescope_monitoring', 'pulse_aggregates', 'pulse_entries', 'pulse_values',
|
||||
];
|
||||
|
||||
// DB 테이블 목록 조회
|
||||
$tables = collect(DB::select('SHOW TABLES'))
|
||||
->map(fn ($table) => array_values((array) $table)[0])
|
||||
->filter(function ($table) use ($excludedTables, $registeredTables) {
|
||||
// 시스템 테이블 제외
|
||||
if (in_array($table, $excludedTables)) {
|
||||
return false;
|
||||
}
|
||||
// 피벗 테이블 제외 (2개의 테이블명이 _로 연결된 경우)
|
||||
if (preg_match('/^[a-z]+_[a-z]+_[a-z]+$/', $table) && ! str_contains($table, 'field')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
->map(function ($table) use ($registeredTables) {
|
||||
// 테이블 컬럼 정보 조회
|
||||
$columns = Schema::getColumnListing($table);
|
||||
$columnDetails = [];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$type = Schema::getColumnType($table, $column);
|
||||
$columnDetails[] = [
|
||||
'name' => $column,
|
||||
'type' => $type,
|
||||
'type_label' => $this->getColumnTypeLabel($type),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'table' => $table,
|
||||
'columns' => $columnDetails,
|
||||
'column_count' => count($columns),
|
||||
'is_registered' => in_array($table, $registeredTables),
|
||||
];
|
||||
})
|
||||
->values();
|
||||
|
||||
// 검색 필터
|
||||
if ($request->filled('search')) {
|
||||
$search = strtolower($request->input('search'));
|
||||
$tables = $tables->filter(function ($t) use ($search) {
|
||||
return str_contains(strtolower($t['table']), $search);
|
||||
})->values();
|
||||
}
|
||||
|
||||
// 등록되지 않은 테이블만 필터
|
||||
if ($request->boolean('unregistered_only', false)) {
|
||||
$tables = $tables->filter(fn ($t) => ! $t['is_registered'])->values();
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $tables,
|
||||
'total' => $tables->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 테이블의 컬럼 목록 조회 (COMMENT 포함)
|
||||
*/
|
||||
public function tableColumns(string $table): JsonResponse
|
||||
{
|
||||
if (! Schema::hasTable($table)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '테이블을 찾을 수 없습니다.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
// MySQL INFORMATION_SCHEMA에서 컬럼 정보 조회 (COMMENT 포함)
|
||||
$dbName = config('database.connections.mysql.database');
|
||||
$columnInfos = DB::select("
|
||||
SELECT COLUMN_NAME, COLUMN_TYPE, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?
|
||||
ORDER BY ORDINAL_POSITION
|
||||
", [$dbName, $table]);
|
||||
|
||||
$columnDetails = [];
|
||||
|
||||
foreach ($columnInfos as $info) {
|
||||
$type = Schema::getColumnType($table, $info->COLUMN_NAME);
|
||||
$columnDetails[] = [
|
||||
'name' => $info->COLUMN_NAME,
|
||||
'type' => $type,
|
||||
'type_label' => $this->getColumnTypeLabel($type),
|
||||
'comment' => $info->COLUMN_COMMENT ?: null,
|
||||
'is_nullable' => $info->IS_NULLABLE === 'YES',
|
||||
'suggested_field_type' => $this->suggestFieldType($type),
|
||||
];
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'table' => $table,
|
||||
'columns' => $columnDetails,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 타입 라벨 변환
|
||||
*/
|
||||
private function getColumnTypeLabel(string $type): string
|
||||
{
|
||||
return match ($type) {
|
||||
'integer', 'bigint', 'smallint', 'tinyint' => '정수',
|
||||
'decimal', 'float', 'double' => '실수',
|
||||
'string', 'varchar', 'char' => '문자열',
|
||||
'text', 'longtext', 'mediumtext' => '텍스트',
|
||||
'boolean' => '불린',
|
||||
'date' => '날짜',
|
||||
'datetime', 'timestamp' => '일시',
|
||||
'json' => 'JSON',
|
||||
default => $type,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 타입에 따른 필드 타입 추천
|
||||
*/
|
||||
private function suggestFieldType(string $type): string
|
||||
{
|
||||
return match ($type) {
|
||||
'integer', 'bigint', 'smallint', 'tinyint', 'decimal', 'float', 'double' => 'number',
|
||||
'boolean' => 'checkbox',
|
||||
'date', 'datetime', 'timestamp' => 'date',
|
||||
'text', 'longtext', 'mediumtext' => 'textarea',
|
||||
default => 'textbox',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user