v7.0.0-beta.2 release

This commit is contained in:
HeuJung
2026-04-20 20:37:49 +09:00
parent 6595fd0eb5
commit fa8eba7a0d
1165 changed files with 107118 additions and 762127 deletions
+167 -8
View File
@@ -2,10 +2,13 @@
namespace App\Extension;
use App\Contracts\Extension\CacheInterface;
use App\Contracts\Extension\ModuleInterface;
use App\Contracts\Extension\StorageInterface;
use App\Contracts\Extension\UpgradeStepInterface;
use App\Extension\Cache\ModuleCacheDriver;
use App\Extension\Storage\ModuleStorageDriver;
use Illuminate\Database\Seeder;
use ReflectionClass;
/**
@@ -32,6 +35,11 @@ abstract class AbstractModule implements ModuleInterface
*/
private ?StorageInterface $storage = null;
/**
* 캐시 드라이버 인스턴스 (캐시)
*/
private ?CacheInterface $cache = null;
/**
* manifest JSON 캐시
*/
@@ -121,14 +129,22 @@ abstract class AbstractModule implements ModuleInterface
}
/**
* 벤더명 반환 (디렉토리명에서 자동 추론)
* 벤더명 반환
*
* 디렉토리명이 'sirsoft-sample'이면 벤더명은 'sirsoft'
* module.json 의 vendor 필드를 우선 사용합니다.
* 값이 없으면 디렉토리명의 첫 단어(예: 'sirsoft-sample' → 'sirsoft')로 폴백합니다.
*
* @return string 사람이 읽는 벤더/개발자명 또는 폴백으로 얻은 식별자 prefix
*/
final public function getVendor(): string
{
$identifier = $this->getIdentifier();
$parts = explode('-', $identifier);
$manifestVendor = $this->loadManifest()['vendor'] ?? null;
if (is_string($manifestVendor) && $manifestVendor !== '') {
return $manifestVendor;
}
$parts = explode('-', $this->getIdentifier());
return $parts[0];
}
@@ -361,6 +377,52 @@ abstract class AbstractModule implements ModuleInterface
return [];
}
/**
* 런타임에 동적으로 생성되는 권한 식별자 목록을 반환합니다.
*
* `getPermissions()` 는 모듈 정의 시점의 **정적** 권한 구조를 반환하지만,
* 일부 모듈(예: sirsoft-board — 게시판 slug 당 권한 세트)은 런타임에 권한을
* 동적으로 생성합니다. 이런 권한은 저장 시 `extension_type=module` +
* `extension_identifier={module}` 로 기록되므로, `ModuleManager::cleanupStaleModuleEntries()`
* 가 **정적 정의에 없다는 이유로 전부 stale 로 오판해 삭제** 하는 회귀가 일어납니다.
*
* 동적 권한을 보유한 모듈은 본 메서드를 override 해 현재 DB/설정에 존재해야 하는
* 동적 권한 식별자(카테고리 + 액션 전체)를 flat 배열로 반환하세요. 반환값은
* cleanup 대상에서 자동 제외됩니다.
*
* @return array<int, string>
*/
public function getDynamicPermissionIdentifiers(): array
{
return [];
}
/**
* 런타임에 동적으로 생성되는 역할 식별자 목록을 반환합니다.
*
* `getRoles()` 정적 정의 외에 런타임에 추가되는 역할(예: 게시판 별 manager/step)이
* 있을 때 override 하세요. 반환된 식별자는 stale cleanup 대상에서 제외됩니다.
*
* @return array<int, string>
*/
public function getDynamicRoleIdentifiers(): array
{
return [];
}
/**
* 런타임에 동적으로 생성되는 메뉴 slug 목록을 반환합니다.
*
* `getAdminMenus()` 정적 정의 외에 런타임에 추가되는 메뉴(예: 게시판 별 메뉴)가
* 있을 때 override 하세요. 반환된 slug 는 stale cleanup 대상에서 제외됩니다.
*
* @return array<int, string>
*/
public function getDynamicMenuSlugs(): array
{
return [];
}
/**
* 모듈 설정 정보 반환
*
@@ -419,6 +481,27 @@ abstract class AbstractModule implements ModuleInterface
return [];
}
/**
* 브로드캐스트 채널 정의를 반환합니다.
*
* 모듈에서 WebSocket 실시간 채널이 필요한 경우 오버라이드합니다.
* 반환된 채널은 ModuleManager가 자동으로 Broadcast::channel()에 등록합니다.
*
* 네이밍 규칙: module.{identifier}.{resource}.{param}
*
* @return array<string, array{permission?: string, type?: string}>
* [
* 'module.vendor-module.orders.{id}' => [
* 'permission' => 'vendor-module.orders.read', // 권한 체크 (선택)
* 'type' => 'private', // 채널 타입 (기본: private)
* ],
* ]
*/
public function getChannels(): array
{
return [];
}
/**
* 스케줄 작업 목록 반환
*
@@ -453,7 +536,7 @@ abstract class AbstractModule implements ModuleInterface
* 빈 배열 반환 시 database/seeders/ 디렉토리의 모든 시더를 자동 검색합니다. (역호환)
* 오버라이드하여 실행할 시더와 순서를 명시적으로 정의하세요.
*
* @return array<class-string<\Illuminate\Database\Seeder>> 시더 클래스명 배열 (FQCN)
* @return array<class-string<Seeder>> 시더 클래스명 배열 (FQCN)
*/
public function getSeeders(): array
{
@@ -463,12 +546,19 @@ abstract class AbstractModule implements ModuleInterface
/**
* 모듈 의존성 반환
*
* 기본적으로 빈 배열 반환
* 모듈 개발자가 의존성이 필요한 경우 오버라이드
* module.json 의 dependencies 필드를 반환합니다.
* 중첩 구조 형식: ['modules' => [identifier => version, ...], 'plugins' => [...]]
*
* 기본 구현은 manifest JSON 파싱 결과를 그대로 반환하므로 모듈 개발자는
* module.json 에 의존성을 정의하면 되고 PHP 오버라이드는 권장하지 않습니다.
*
* @return array 중첩 구조 의존성 배열
*/
public function getDependencies(): array
{
return [];
$dependencies = $this->loadManifest()['dependencies'] ?? [];
return is_array($dependencies) ? $dependencies : [];
}
/**
@@ -624,6 +714,43 @@ abstract class AbstractModule implements ModuleInterface
return is_array($config) ? $config : [];
}
/**
* SEO 변수 메타데이터를 반환합니다.
*
* 모듈이 SEO 렌더링에 제공하는 변수를 page_type별로 선언합니다.
* SeoRenderer가 이 메서드를 호출하여 변수를 수집하고 자동 해석합니다.
*
* 각 변수는 source 타입에 따라 해석 방식이 결정됩니다:
* - setting: 모듈 환경설정 값 (엔진 자동 해석)
* - core_setting: 코어 설정 값 (엔진 자동 해석)
* - query: URL 쿼리 파라미터 (엔진 자동 해석)
* - route: 라우트 파라미터 (엔진 자동 해석)
* - data: 데이터소스 응답 필드 (템플릿 개발자가 vars에서 매핑)
*
* 기본적으로 빈 배열 반환.
* 모듈 개발자가 SEO 변수가 필요한 경우 오버라이드하세요.
*
* @return array page_type별 변수 정의 배열
* [
* 'product' => [
* 'product_name' => [
* 'description' => '상품명',
* 'source' => 'data',
* 'required' => true,
* ],
* 'commerce_name' => [
* 'description' => '쇼핑몰명',
* 'source' => 'setting',
* 'key' => 'basic_info.shop_name',
* ],
* ],
* ]
*/
public function seoVariables(): array
{
return [];
}
/**
* 그누보드7 코어 요구 버전 제약 반환
*
@@ -782,6 +909,38 @@ abstract class AbstractModule implements ModuleInterface
return 'modules';
}
/**
* 모듈 캐시 드라이버 인스턴스 반환
*
* 모듈별로 격리된 캐시를 제공합니다.
* 접두사 패턴: g7:module.{identifier}:{key}
*
* @return CacheInterface 캐시 드라이버 인스턴스
*/
public function getCache(): CacheInterface
{
if ($this->cache === null) {
$this->cache = new ModuleCacheDriver(
$this->getIdentifier(),
$this->getCacheStore()
);
}
return $this->cache;
}
/**
* 모듈에서 사용할 캐시 스토어 이름 반환
*
* 기본값은 환경설정 캐시 드라이버이며, 모듈 개발자가 다른 스토어를 사용하려면 오버라이드합니다.
*
* @return string 캐시 스토어 이름
*/
public function getCacheStore(): string
{
return config('cache.default');
}
/**
* 카테고리별 스토리지 기본 경로 반환
*