"Servis bulucu" servisleri, servis isimlerini alarak diğer servislere ulaşmamıza yardımcı olurlar. Bu yaklaşımın iki ana faydası vardır. İlk olarak, ana class sadece "servis bulucu" servisini bilir ve ikincisi ise, ana class içine enjekte edilen servislerin sayısı en az seviyede tutulur. Daha fazla bilgi için Service Subscribers & Locators sayfasını okuyun.


Servis bulucu olmadan


Bir servis bulucu servisimiz olmasaydı, kontrolörümüz aşağıdaki gibi olurdu.


class LocatorController
{
public function __construct(
ServiceA $serviceA,
ServiceB $serviceB,
ServiceC $serviceC,
ServiceD $serviceD,
...
) {
}
}

Yukarıdakinin yerine aşağıdaki örneğe sahip olacağız.


class LocatorController
{
public function __construct(
ServiceLocator $serviceLocator
) {
}
}

config/services.yaml


parameters:

services:
_defaults:
autowire: true
autoconfigure: true
public: false

App\:
resource: '../src/*'
exclude: '../src/{Entity,Repository,Service,Kernel.php}'

App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']

App\Factory\ModelFactory:
arguments:
$id: 1

LocatorController


Bu sadece bir gösteri amaçlı!


declare(strict_types=1);

namespace App\Controller;

use App\Factory\ModelFactoryInterface;
use App\Locator\ServiceLocatorInterface;
use App\Service\CountryServiceInterface;
use App\Util\CalculatorUtilInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

/**
* @Route("/locators")
*/
class LocatorController
{
private $serviceLocator;

public function __construct(
ServiceLocatorInterface $serviceLocator
) {
$this->serviceLocator = $serviceLocator;
}

/**
* @Route("/{type}")
* @Method({"GET"})
*/
public function getService(string $type): Response
{
if ('util' === $type) {
/** @var CalculatorUtilInterface $service */
$service = $this->serviceLocator->get(CalculatorUtilInterface::class);
$result = $service->sum(1, 2);
} elseif ('factory' === $type) {
/** @var ModelFactoryInterface $service */
$service = $this->serviceLocator->get(ModelFactoryInterface::class);
$result = $service->create('inanzzz');
} else {
$this->serviceLocator->get(CountryServiceInterface::class);
// This would throw an exception because it is not registered in ServiceLocator class
}

return new JsonResponse($result);
}
}

ServiceLocatorException


declare(strict_types=1);

namespace App\Exception;

use RuntimeException;

class ServiceLocatorException extends RuntimeException
{
}

ModelFactoryInterface


declare(strict_types=1);

namespace App\Factory;

use stdClass;

interface ModelFactoryInterface
{
public function create(string $name): stdClass;
}

ModelFactory


declare(strict_types=1);

namespace App\Factory;

use stdClass;

class ModelFactory implements ModelFactoryInterface
{
private $id;

public function __construct(int $id)
{
$this->id = $id;
}

public function create(string $name): stdClass
{
$model = new stdClass();
$model->id = $this->id;
$model->name = $name;

return $model;
}
}

CalculatorUtilInterface


declare(strict_types=1);

namespace App\Util;

interface CalculatorUtilInterface
{
public function sum(int $a, int $b): int;
}

CalculatorUtil


declare(strict_types=1);

namespace App\Util;

class CalculatorUtil implements CalculatorUtilInterface
{
public function sum(int $a, int $b): int
{
return $a+$b;
}
}

ServiceLocatorInterface


declare(strict_types=1);

namespace App\Locator;

interface ServiceLocatorInterface
{
public function get(string $id);
}

ServiceLocator


declare(strict_types=1);

namespace App\Locator;

use App\Exception\ServiceLocatorException;
use App\Factory\ModelFactoryInterface;
use App\Util\CalculatorUtilInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;

class ServiceLocator implements ServiceLocatorInterface, ServiceSubscriberInterface
{
private $locator;

public function __construct(ContainerInterface $locator)
{
$this->locator = $locator;
}

public static function getSubscribedServices()
{
return [
'App\Factory\ModelFactoryInterface' => ModelFactoryInterface::class,
'App\Util\CalculatorUtilInterface' => CalculatorUtilInterface::class,
];
}

public function get(string $id)
{
if (!$this->locator->has($id)) {
throw new ServiceLocatorException(sprintf(
'The entry for the given "%s" identifier was not found.',
$id
));
}

try {
return $this->locator->get($id);
} catch (ContainerExceptionInterface $e) {
throw new ServiceLocatorException(sprintf(
'Failed to fetch the entry for the given "%s" identifier.',
$id
));
}
}
}

Test


# GET /locators/factory
{"id":1,"name":"inanzzz"}

# GET /locators/util
3

# GET /locators/invalid
ServiceLocatorException
The entry for the given "App\Service\CountryServiceInterface" identifier was not found.