Skip to content

Commit 093e018

Browse files
ryuringclaude
andcommitted
カスタムフィールドの型判定を読み込み済みの links から引く
processCustomFields() がフィールドごとに CustomLinks を検索しており、 カスタムエントリーの登録・更新でフィールド数に比例したクエリが出ていた。 BcCcFile 型の判定を追加した際にその検索が2度走るようになり顕在化した。 CustomEntriesService::setup() が CustomEntriesTable::setLinks() を通じて CustomLinks を CustomFields 付きで読み込み済みであり、processCustomFields() が呼ばれる時点でフィールド定義はメモリ上にある。それを参照する buildFieldTypeMap() を用意し、フィールド型のマップを1回だけ組み立てて 渡す形に変える。本体の CustomEntriesTable が links を使い回すのと同じ扱い。 これに伴い、独自にDBを引いていた getCustomFieldType() / isFileUploadField() / isFileUpload() を削除する。対応するテストは buildFieldTypeMap() の検証に置き換えた。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent b016e37 commit 093e018

2 files changed

Lines changed: 74 additions & 95 deletions

File tree

plugins/bc-mcp/src/Mcp/BcCustomContent/CustomEntriesTool.php

Lines changed: 28 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,10 @@ public function addCustomEntry(
200200

201201
// カスタムフィールドの値を追加(ファイルアップロード処理を含む)
202202
if (!empty($customFields)) {
203-
$processedFields = $this->processCustomFields($customFields, $customTableId);
203+
$processedFields = $this->processCustomFields(
204+
$customFields,
205+
$this->buildFieldTypeMap($customEntriesService)
206+
);
204207
$data = array_merge($data, $processedFields);
205208
}
206209

@@ -264,7 +267,10 @@ public function editCustomEntry(
264267

265268
// カスタムフィールドの値を追加(ファイルアップロード処理を含む)
266269
if (!empty($customFields)) {
267-
$processedFields = $this->processCustomFields($customFields, $customTableId);
270+
$processedFields = $this->processCustomFields(
271+
$customFields,
272+
$this->buildFieldTypeMap($customEntriesService)
273+
);
268274
$data = array_merge($data, $processedFields);
269275
}
270276

@@ -290,16 +296,21 @@ public function editCustomEntry(
290296
* @param int $customTableId カスタムテーブルID
291297
* @return array
292298
*/
293-
protected function processCustomFields(array $customFields, int $customTableId): array
299+
protected function processCustomFields(array $customFields, array $fieldTypes): array
294300
{
295301
$processedFields = [];
296302

297303
foreach($customFields as $fieldName => $value) {
298304
if (is_array($value)) {
299305
// 配列の場合、json形式またはファイルアップロードの可能性をチェック
300306
$processedFields[$fieldName] = $value;
301-
} elseif ($this->isFileUpload($value, $customTableId, $fieldName)) {
302-
// ファイルアップロードデータの処理(フィールドタイプもチェック)
307+
continue;
308+
}
309+
310+
$isFileField = (($fieldTypes[$fieldName] ?? null) === 'BcCcFile');
311+
312+
if ($isFileField && $this->isFileUploadable($value)) {
313+
// ファイルアップロードデータの処理
303314
$uploadResult = $this->processFileUpload($value);
304315
if ($uploadResult !== false) {
305316
// 戻り値が配列の場合はUploadedFileオブジェクトに変換、文字列の場合はそのまま
@@ -311,7 +322,7 @@ protected function processCustomFields(array $customFields, int $customTableId):
311322
} else {
312323
throw new InvalidArgumentException("ファイルアップロードに失敗しました ({$fieldName})");
313324
}
314-
} elseif (!empty($value) && $this->isFileUploadField($customTableId, $fieldName)) {
325+
} elseif ($isFileField && !empty($value)) {
315326
// BcCcFile型のフィールドに、アップロード可能な形式(data: URI・URL・配列)
316327
// ではない値が渡された場合は、実在しないファイル名等がそのままDBへ
317328
// 書き込まれてしまうため、明示的にエラーとして扱う
@@ -326,67 +337,23 @@ protected function processCustomFields(array $customFields, int $customTableId):
326337
}
327338

328339
/**
329-
* カスタムフィールドのタイプを取得
330-
*
331-
* @param int $customTableId カスタムテーブルID
332-
* @param string $fieldName フィールド名
333-
* @return string|null フィールドタイプ(BcCcFileなど)、見つからない場合はnull
334-
*/
335-
protected function getCustomFieldType(int $customTableId, string $fieldName): ?string
336-
{
337-
try {
338-
$customLinksTable = \Cake\ORM\TableRegistry::getTableLocator()->get('BcCustomContent.CustomLinks');
339-
340-
$customLink = $customLinksTable->find()
341-
->contain(['CustomFields'])
342-
->where([
343-
'CustomLinks.custom_table_id' => $customTableId,
344-
'CustomLinks.name' => $fieldName
345-
])
346-
->first();
347-
348-
if ($customLink && $customLink->custom_field) {
349-
return $customLink->custom_field->type;
350-
}
351-
352-
return null;
353-
} catch (\Exception $e) {
354-
// エラーログを出力
355-
error_log('カスタムフィールドタイプの取得に失敗: ' . $e->getMessage());
356-
return null;
357-
}
358-
}
359-
360-
/**
361-
* カスタムフィールドがファイルアップロードフィールドかどうかを判定
340+
* フィールド名からフィールドタイプを引くマップを作る
362341
*
363-
* @param int $customTableId カスタムテーブルID
364-
* @param string $fieldName フィールド名
365-
* @return bool BcCcFileフィールドの場合true
366-
*/
367-
protected function isFileUploadField(int $customTableId, string $fieldName): bool
368-
{
369-
$fieldType = $this->getCustomFieldType($customTableId, $fieldName);
370-
return $fieldType === 'BcCcFile';
371-
}
372-
373-
/**
374-
* ファイルアップロードデータかどうかを判定(カスタムエントリー用)
342+
* CustomEntriesService::setup() が CustomEntriesTable::setLinks() を通じて
343+
* CustomLinks を CustomFields 付きで読み込み済みのため、それを参照する。
344+
* フィールドごとに DB を引き直すと N+1 になる。
375345
*
376-
* @param mixed $value 判定対象の値
377-
* @param int $customTableId カスタムテーブルID
378-
* @param string $fieldName フィールド名
379-
* @return bool ファイルアップロードデータの場合true
346+
* @param \BcCustomContent\Service\CustomEntriesService $customEntriesService setup() 済みのサービス
347+
* @return array<string, string|null> フィールド名 => フィールドタイプ
380348
*/
381-
protected function isFileUpload($value, int $customTableId, string $fieldName): bool
349+
protected function buildFieldTypeMap($customEntriesService): array
382350
{
383-
// フィールドタイプがBcCcFileでない場合は対象外
384-
if (!$this->isFileUploadField($customTableId, $fieldName)) {
385-
return false;
351+
$fieldTypes = [];
352+
foreach((array)$customEntriesService->CustomEntries->links as $link) {
353+
$fieldTypes[$link->name] = $link->custom_field->type ?? null;
386354
}
387355

388-
// 値の形式チェック
389-
return $this->isFileUploadable($value);
356+
return $fieldTypes;
390357
}
391358

392359
/**

plugins/bc-mcp/tests/TestCase/Mcp/BcCustomContent/CustomEntriesToolTest.php

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -649,11 +649,19 @@ public function testProcessCustomFields()
649649
'array_field' => ['値1', '値2']
650650
];
651651

652+
// フィールド型のマップ。いずれも BcCcFile ではない
653+
$fieldTypes = [
654+
'text_field' => 'BcCcText',
655+
'number_field' => 'BcCcText',
656+
'image_field' => 'BcCcText',
657+
'array_field' => 'BcCcText',
658+
];
659+
652660
// リフレクションを使ってプライベートメソッドをテスト
653661
$reflection = new \ReflectionClass($this->CustomEntriesTool);
654662
$method = $reflection->getMethod('processCustomFields');
655663

656-
$result = $method->invoke($this->CustomEntriesTool, $customFields, 1); // customTableId = 1 を追加
664+
$result = $method->invoke($this->CustomEntriesTool, $customFields, $fieldTypes);
657665

658666
$this->assertIsArray($result);
659667
$this->assertEquals('テキスト値', $result['text_field']);
@@ -664,54 +672,58 @@ public function testProcessCustomFields()
664672
}
665673

666674
/**
667-
* test getCustomFieldType method
675+
* test buildFieldTypeMap method
676+
*
677+
* フィールド型は CustomEntriesService::setup() が読み込んだ links から引く。
678+
* フィールドごとに DB を引き直すと N+1 になるため、読み込み済みのものを
679+
* 参照していることを確認する。
668680
*/
669-
public function testGetCustomFieldType()
681+
public function testBuildFieldTypeMap()
670682
{
671-
$customTableId = 1;
672-
$fieldName = 'test_field';
683+
$dataBaseService = $this->getService(BcDatabaseServiceInterface::class);
684+
$customTablesService = $this->getService(CustomTablesServiceInterface::class);
673685

674-
// リフレクションを使ってプライベートメソッドをテスト
675-
$reflection = new \ReflectionClass($this->CustomEntriesTool);
676-
$method = $reflection->getMethod('getCustomFieldType');
686+
$this->loadFixtureScenario(CustomFieldsScenario::class);
677687

678-
// フィールドタイプが取得できない場合はnullを返す
679-
$result = $method->invoke($this->CustomEntriesTool, $customTableId, $fieldName);
680-
$this->assertNull($result);
681-
}
688+
$customTablesService->create([
689+
'type' => 'contact',
690+
'name' => 'contact',
691+
'title' => 'お問い合わせタイトル',
692+
'display_field' => 'お問い合わせ'
693+
]);
682694

683-
/**
684-
* test isFileUploadField method
685-
*/
686-
public function testIsFileUploadField()
687-
{
688-
$customTableId = 1;
689-
$fieldName = 'test_field';
695+
$customEntriesService = $this->getService(CustomEntriesServiceInterface::class);
696+
$customEntriesService->setup(1);
690697

691-
// リフレクションを使ってプライベートメソッドをテスト
692698
$reflection = new \ReflectionClass($this->CustomEntriesTool);
693-
$method = $reflection->getMethod('isFileUploadField');
699+
$method = $reflection->getMethod('buildFieldTypeMap');
700+
$result = $method->invoke($this->CustomEntriesTool, $customEntriesService);
701+
702+
$this->assertIsArray($result);
703+
704+
// links に載っているフィールドはすべてマップに含まれる
705+
$this->assertNotEmpty($customEntriesService->CustomEntries->links, 'links が読み込まれていません');
706+
foreach($customEntriesService->CustomEntries->links as $link) {
707+
$this->assertArrayHasKey($link->name, $result);
708+
$this->assertSame($link->custom_field->type ?? null, $result[$link->name]);
709+
}
694710

695-
// フィールドタイプが取得できない場合はfalseを返す
696-
$result = $method->invoke($this->CustomEntriesTool, $customTableId, $fieldName);
697-
$this->assertFalse($result);
711+
$dataBaseService->dropTable('custom_entry_1_contact');
698712
}
699713

700714
/**
701-
* test isFileUpload method
715+
* test buildFieldTypeMap method - setup 前は空を返す
716+
*
717+
* links が未読み込みでも例外にならず、結果として全フィールドが
718+
* 「BcCcFile ではない」=通常の値として扱われる。
702719
*/
703-
public function testIsFileUpload()
720+
public function testBuildFieldTypeMapWithoutSetup()
704721
{
705-
$customTableId = 1;
706-
$fieldName = 'test_field';
707-
$base64Data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChAI9jAuoqQAAAABJRU5ErkJggg==';
722+
$customEntriesService = $this->getService(CustomEntriesServiceInterface::class);
708723

709-
// リフレクションを使ってプライベートメソッドをテスト
710724
$reflection = new \ReflectionClass($this->CustomEntriesTool);
711-
$method = $reflection->getMethod('isFileUpload');
725+
$method = $reflection->getMethod('buildFieldTypeMap');
712726

713-
// フィールドタイプが取得できない場合、ファイルアップロード形式でもfalseを返す
714-
$result = $method->invoke($this->CustomEntriesTool, $base64Data, $customTableId, $fieldName);
715-
$this->assertFalse($result);
727+
$this->assertSame([], $method->invoke($this->CustomEntriesTool, $customEntriesService));
716728
}
717729
}

0 commit comments

Comments
 (0)