diff --git a/service_container/factories.rst b/service_container/factories.rst index f67b8c35ebd..29419c8d886 100644 --- a/service_container/factories.rst +++ b/service_container/factories.rst @@ -301,5 +301,161 @@ previous examples takes the ``templating`` service as an argument: ->args([service('templating')]) ; }; + +Usage example +------------- + +The following example is intended to show how to create and use a factory method in Symfony framework. +Suppose you want to realize the factory method pattern for services, that describe two delivery methods - DHL and UPS. + +Services (subclasses) definition:: + + // src/Deliveries/DeliveryInterface.php + namespace App\Deliveries; + + interface DeliveryInterface + { + public function getPrice(): float; + } + + // src/Deliveries/DHL.php + namespace App\Deliveries; + + use App\Deliveries\DeliveryInterface; + + class DHL implements DeliveryInterface + { + public string $priceLabel; + + public function getPrice(): float + { + return 100; + } + } + + // src/Deliveries/UPS.php + namespace App\Deliveries; + + use App\Deliveries\DeliveryInterface; + + class UPS implements DeliveryInterface + { + public string $priceLabel; + + public function getPrice(): float + { + return 200; + } + } + +Static factory definition:: + + // src/Factories/DeliveryFactory.php + namespace App\Factories; + + use App\Deliveries\DeliveryInterface; + use App\Deliveries\DHL; + use App\Deliveries\UPS; + + class DeliveryFactory + { + public static function create(string $deliveryMethod): DeliveryInterface + { + switch ($deliveryMethod) { + case 'dhl': + $delivery = new DHL; + break; + case 'ups': + $delivery = new UPS; + break; + default: + throw new \InvalidArgumentException('Unknown delivery method given'); + } + + // Since DeliveryFactory is a factory of DHL and UPS instances, + // priceLabel property with exactly this value ('Delivery price is: ') + // is accessible for them - you will see it soon. + $delivery->priceLabel = 'Delivery price is: '; + + return $delivery; + } + + } + +Next, use settings similar to those in the sections above. These settings allow you to define a factory method for subclasses without explicitly extending the abstract class (i.e., without ``class DHL extends DeliveryFactory``)! + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + App\Deliveries\DHL: + factory: ['App\Factories\DeliveryFactory', 'create'] + arguments: ['dhl'] + App\Deliveries\UPS: + factory: ['App\Factories\DeliveryFactory', 'create'] + arguments: ['ups'] + + + .. code-block:: xml + + + + + + + + + + + dhl + + + + ups + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use App\Deliveries\DHL; + use App\Deliveries\UPS; + use App\Factories\DeliveryFactory; + + return function(ContainerConfigurator $configurator) { + $services = $configurator->services(); + + $services->set(DHL::class) + ->factory([DeliveryFactory::class, 'create']) + ->args(['dhl']) + ; + $services->set(UPS::class) + ->factory([DeliveryFactory::class, 'create']) + ->args(['ups']) + ; + }; + +Now we can use our delivery services as usual (via dependency injection). The only difference is that subclasses instances of services are created in the factory. Let's get those services in controller:: + + public function getDeliveriesPrice(DHL $dhl, UPS $ups) + { + // ... + + // $dpd->priceLabel and $ups->priceLabel are fulfilled in factory method. + $dhlPriceMessage = $dhl->priceLabel . $dhl->getPrice(); + $upsPriceMessage = $ups->priceLabel . $ups->getPrice(); + + // ... + } .. _`factory design pattern`: https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)