Návrh modulů a závislostí

Phalanx
Člen | 310
+
0
-

Ahoj, rád bych se zeptal, jak řešíte následující věc:

Mám hodně modulů:

ShopModule

  • FrontModule
  • AdminModule
  • Shop.php

BlogModule

  • FrontModule
  • Blog.php

a další

A pak například chci vygenerovat sitemapu, ale nechci tam vkládat každý modul samostatně viz:

<?php
class Sitemap {

   /** @var Shop @inject */
   protected $shop;

   /** @var Blog @inject */
   protected $blog;

   public function()
   {
	  $urls = array();
      $urls[] = $this->shop->getUrls();
      $urls[] = $this->blog->getUrls();
      return $urls;
   }

}
?>

Lze na tento problém nějak použít dědičnost?

<?php
   abstract class X {
       abstract function getUrls();
   }

   class Shop extends X
   {
		public function getUrls() {
           ...
           return $urls;
        }

   }

   class Blog extends X
   {
		public function getUrls() {
           ...
           return $urls;
        }

   }


class Sitemap {

   public function()
   {
	  $urls = array();
      $urls[] = "všechny třídy co rozšiřují třídu X";
      return $urls;
   }

}
?>

Děkuju za Váš čas

CZechBoY
Člen | 3608
+
+1
-

Takhle to spíš vypadá, že ta třída implementuje nějakej interface (metoda getUrls).
Najít si jaký třídy implementujou interface (třeba z DIC) je jednoduchá věc a nemusíš se starat ani o případné závislosti.

$serviceNames = $container->findByType(MujInterface::class);

$moduleUrlsFinders = [];
foreach ($serviceNames as $serviceName) {
    $moduleUrlsFinders[] = $container->getService($serviceName);
}

// následuje tvůj kod
foreach ($moduleUrlsFinders as $moduleUrlFinder) {
    $urls[] = $moduleUrlFinder->getUrls();
}
Jan Endel
Člen | 1016
+
+4
-

Ahoj,

to co hledáš je spíše interface:

interface ISiteLinksProvider
{
   public getSiteLinks(): array;
}

pak totiž můžeš vytvořit třídy:

class ShopModuleSiteLinksProvider implements ISiteLinksProvider
{
    public getSiteLinks(): array
    {
      //...
    }
}

class BlogModuleSiteLinksProvider implements ISiteLinksProvider
{
    public getSiteLinks(): array
    {
      //...
    }
}

pak mít třídu, co tyhle linky vrátí hromadně:

class Sitemap
{
    /**
     * @var array|ISiteLinksProvider[]
     */
    private $providers = [];


    public addProvider(ISiteLinksProvider $provider)
    {
       $this->providers[] = $provider;
    }


    public function getUrl()
    {
        //...
    }
}

a pak asi nejelegantnější a nejjednoduší bude DI Container extension, co ti to naplní podle interface:

class ApplicationExtension extends Nette\DI\CompilerExtension
{
	public function beforeCompile()
    {
       $builder = $this->getContainerBuilder();
       $linksProviders = $builder->findByType(ISiteLinksProvider::class);

       $sitemap = $builder->getDefinition($builder->getByType(Sitemap::class));

       foreach ($linksProviders as $linkProvider) {
           $sitemap->addSetup('addProvider', [$linkProvider]);
       }
    }
}
Phalanx
Člen | 310
+
0
-

@JanEndel To je přesně ono! Díky moc! Bylo mi jedno jestli použít abstract class nebo interface, nerozuměl jsem, jak to může fungovat dohromady.