Uvođenje ovisnosti u XOOPS
Kompatibilnost verzije
| Značajka | XOOPS 2.5.x | XOOPS 4.0 |
|---|---|---|
| Ručni DI (injekcija konstruktora) | ✅ Dostupan | ✅ Dostupan |
| PSR-11 Spremnik | ❌ Nije ugrađeno | ✅ Izvorna podrška |
\Xmf\Module\Helper::getContainer() | ❌ Samo 4.0 | ✅ Dostupan |
U XOOPS 2.5.x koristite ručno ubacivanje konstruktora (izričito prosljeđujući ovisnosti). Primjeri spremnika PSR-11 u nastavku odnose se na XOOPS 4.0.
Pregled
Section titled “Pregled”Dependency Injection (DI) je obrazac dizajna koji omogućuje komponentama da primaju svoje ovisnosti iz vanjskih izvora umjesto da ih stvaraju interno. XOOPS 4.0 predstavlja PSR-11 kompatibilnu podršku za DI spremnik.
Zašto ubrizgavanje ovisnosti?
Section titled “Zašto ubrizgavanje ovisnosti?”Bez DI (čvrsto spajanje)
Section titled “Bez DI (čvrsto spajanje)”class ArticleService{ private ArticleRepository $repository; private EventDispatcher $dispatcher;
public function __construct() { // Hard dependencies - difficult to test and modify $this->repository = new ArticleRepository(new XoopsDatabase()); $this->dispatcher = new EventDispatcher(); }}S DI (labava spojnica)
Section titled “S DI (labava spojnica)”class ArticleService{ public function __construct( private readonly ArticleRepositoryInterface $repository, private readonly EventDispatcherInterface $dispatcher ) {}}PSR-11 Spremnik
Section titled “PSR-11 Spremnik”Osnovna upotreba
Section titled “Osnovna upotreba”use Psr\Container\ContainerInterface;
// Get the container$container = \Xmf\Module\Helper::getHelper('mymodule')->getContainer();
// Retrieve a service$articleService = $container->get(ArticleService::class);
// Check if service existsif ($container->has(ArticleService::class)) { // Use the service}Konfiguracija spremnika
Section titled “Konfiguracija spremnika”use Psr\Container\ContainerInterface;
return [ // Simple class instantiation ArticleRepository::class => ArticleRepository::class,
// Interface to implementation binding ArticleRepositoryInterface::class => ArticleRepository::class,
// Factory function ArticleService::class => function (ContainerInterface $c): ArticleService { return new ArticleService( $c->get(ArticleRepositoryInterface::class), $c->get(EventDispatcherInterface::class) ); },
// Shared instance (singleton) 'database' => function (): XoopsDatabase { return XoopsDatabaseFactory::getDatabaseConnection(); },];Registracija usluge
Section titled “Registracija usluge”Automatsko ožičenje
Section titled “Automatsko ožičenje”// The container automatically resolves dependencies// when type hints are available
class ArticleController{ public function __construct( private readonly ArticleService $service, private readonly ViewRenderer $renderer ) {}}
// Container creates ArticleController with its dependencies$controller = $container->get(ArticleController::class);Ručna registracija
Section titled “Ručna registracija”return [ ArticleService::class => [ 'class' => ArticleService::class, 'arguments' => [ ArticleRepositoryInterface::class, EventDispatcherInterface::class, ], 'shared' => true, // Singleton ],
'article.handler' => [ 'factory' => [ArticleHandlerFactory::class, 'create'], 'arguments' => ['@database'], // Reference other service ],];Injekcija konstruktora
Section titled “Injekcija konstruktora”Preferirani pristup
Section titled “Preferirani pristup”final class ArticleService{ public function __construct( private readonly ArticleRepositoryInterface $repository, private readonly EventDispatcherInterface $dispatcher, private readonly LoggerInterface $logger ) {}
public function create(CreateArticleDTO $dto): Article { $this->logger->info('Creating article', ['title' => $dto->title]);
$article = Article::create($dto); $this->repository->save($article); $this->dispatcher->dispatch(new ArticleCreatedEvent($article));
return $article; }}Injekcija metode
Section titled “Injekcija metode”Za neobavezne ovisnosti
Section titled “Za neobavezne ovisnosti”class ArticleController{ public function __construct( private readonly ArticleService $service ) {}
public function show(int $id, ?CacheInterface $cache = null): Response { $cacheKey = "article_{$id}";
if ($cache && $cached = $cache->get($cacheKey)) { return $this->render($cached); }
$article = $this->service->findById($id);
$cache?->set($cacheKey, $article, 3600);
return $this->render($article); }}Povezivanje sučelja
Section titled “Povezivanje sučelja”Definirajte sučelja
Section titled “Definirajte sučelja”interface ArticleRepositoryInterface{ public function findById(int $id): ?Article; public function save(Article $article): void; public function delete(Article $article): void;}Implementacija vezivanja
Section titled “Implementacija vezivanja”return [ ArticleRepositoryInterface::class => XoopsArticleRepository::class,
// Or with factory ArticleRepositoryInterface::class => function (ContainerInterface $c) { return new XoopsArticleRepository( $c->get('database') ); },];Testiranje s DI
Section titled “Testiranje s DI”Lako ismijavanje
Section titled “Lako ismijavanje”class ArticleServiceTest extends TestCase{ public function testCreateArticle(): void { // Create mocks $repository = $this->createMock(ArticleRepositoryInterface::class); $dispatcher = $this->createMock(EventDispatcherInterface::class); $logger = $this->createMock(LoggerInterface::class);
// Inject mocks $service = new ArticleService($repository, $dispatcher, $logger);
// Set expectations $repository->expects($this->once())->method('save'); $dispatcher->expects($this->once())->method('dispatch');
// Test $dto = new CreateArticleDTO('Title', 'Content'); $article = $service->create($dto);
$this->assertInstanceOf(Article::class, $article); }}XOOPS Legacy integracija
Section titled “XOOPS Legacy integracija”Premošćivanje starog i novog
Section titled “Premošćivanje starog i novog”// Get service from container in legacy codefunction mymodule_get_articles(int $limit): array{ $container = \Xmf\Module\Helper::getHelper('mymodule')->getContainer(); $service = $container->get(ArticleService::class);
return $service->findRecent($limit);}Omatanje naslijeđenih rukovatelja
Section titled “Omatanje naslijeđenih rukovatelja”return [ 'article.handler' => function () { return xoops_getModuleHandler('article', 'mymodule'); },
ArticleRepositoryInterface::class => function (ContainerInterface $c) { return new LegacyArticleRepository( $c->get('article.handler') ); },];Najbolji primjeri iz prakse
Section titled “Najbolji primjeri iz prakse”- Inject Interfaces - Ovisi o apstrakcijama, ne o implementacijama
- Ubrizgavanje konstruktora - Dajte prednost konstruktoru nego ubrizgavanju postavljača
- Jedna odgovornost - Svaki class trebao bi imati nekoliko ovisnosti
- Izbjegavajte svijest o spremniku - Usluge ne bi trebale znati za spremnik
- Konfiguriraj, ne kodiraj - Koristite konfiguracijske datoteke za ožičenje
Povezana dokumentacija
Section titled “Povezana dokumentacija”- ../07-XOOPS-4.0/Implementation-Guides/PSR-11-Dependency-Injection-Guide - PSR-11 implementacija
- ../03-Module-Development/Patterns/Service-Layer - Uzorak usluge
- ../03-Module-Development/Best-Practices/Testing - Testiranje s DI
- ../07-XOOPS-4.0/XOOPS-4.0-Architecture - Pregled arhitekture