Create “Abstract Factory” service in PHP-DI with RequestedEntry and Wildcards
If you’re familiar with Zend Framework’s servicemanager, you may already used the abstract factory which acts as limbo when service not registered. When the pattern checked is matched, it will try to create service based on it, automatically.
We can do that in PHP-DI as well. For example, we are experimenting with my previous post on DDD in slim 4 framework:
├───Application ├───Domain │ ├───DomainException │ │ DomainException.php │ │ DomainRecordNotFoundException.php │ │ │ ├───Post │ │ Post.php │ │ PostNotFoundException.php │ │ PostRepository.php │ └───Infrastructure └───Persistence ├───Post │ ZendDbPostRepository.php
We are going to create service with pattern “App\Domain\Post\PostRepository” from App\Infrastructure\Persistence
namespace with as an object from “App\Infrastructure\Persistence\Post\ZendDbPostRepository”. In PHP-DI, we can combine both RequestedEntry and Wildcards definitions, the service definition will be like the following:
<?php declare(strict_types=1); use DI\ContainerBuilder; use Psr\Container\ContainerInterface; use Zend\Db\Adapter\AdapterInterface; use Zend\Db\ResultSet\HydratingResultSet; use Zend\Db\TableGateway\TableGateway; use Zend\Hydrator\ObjectPropertyHydrator; use DI\Factory\RequestedEntry; return function (ContainerBuilder $containerBuilder) { $containerBuilder->addDefinitions([ 'App\Domain\*\*Repository' => function (RequestedEntry $entry, ContainerInterface $c) { // get entity class name, // eg: "Post" by service named "App\Domain\Post\PostRepository" preg_match( '/(?<=App\\\\Domain\\\\)([A-Z][a-z]{1,})(?=\\\\\1Repository)/', $entry->getName(), $matches ); $entity = current($matches); $fullEntityClass = 'App\Domain' . str_repeat('\\' . $entity, 2); $fullRepoClass = 'App\Infrastructure\Persistence' . '\\' . $entity . '\ZendDb' . $entity . 'Repository'; $tableGateway = new TableGateway( $fullEntityClass::TABLE, $c->get(AdapterInterface::class), null, new HydratingResultSet(new ObjectPropertyHydrator(), new $fullEntityClass) ); return new $fullRepoClass($tableGateway); }, ]); };
That’s it!
Functional Test Symfony 4 with Kahlan 4
Yes, there is a bundle for it, but currently not fully work well with kahlan 4 yet. However, we can still use kahlan 4 for it. The simplest way is define Symfony 4 skeleton bootstrap in kahlan config, and use its property at specs, for example, we configure config at kahlan-config.php
as follows:
<?php // kahlan-config.php use App\Kernel; use Kahlan\Filter\Filters; use Symfony\Component\Debug\Debug; use Symfony\Component\HttpFoundation\Request; Filters::apply($this, 'bootstrap', function($next) { require __DIR__.'/config/bootstrap.php'; umask(0000); Debug::enable(); $root = $this->suite()->root(); $root->beforeAll(function () { $this->request = Request::createFromGlobals(); $this->kernel = new Kernel('test', false); }); return $next(); });
Above settings are minimal, if you need more setup, you can define there. If you didn’t require kahlan/kahlan:^4.0
, you can require via composer:
$ composer require --dev kahlan/kahlan:^4.0
Give a try
Let’s try testing a famous /lucky/number
from LuckyController
. We have the following controller:
<?php // src/Controller/LuckyController.php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Routing\Annotation\Route; class LuckyController extends Controller { /** * @Route("/lucky/number", name="lucky_number") */ public function number() { $number = mt_rand(0, 100); return $this->render('lucky/number.html.twig', [ 'number' => $number, ]); } }
And our twig file is:
{# templates/lucky/number.html.twig #} <h1>Your lucky number is {{ number }}</h1>
We can place test under spec
directory at root directory, for its test, we can create a spec/Controller
directory:
kahlan.config.php ├── spec │ └── Controller
Now, we can create the test as follows with make request to the ‘/lucky/number’ page and get its response. We can use toMatchEcho
matcher provided with regex to get match random number of mt_rand(0, 100)
that printed inside a response html content:
<?php // spec/Controller/LuckyControllerSpec.php namespace App\Spec\Controller; describe('LuckyController', function () { describe('/lucky/number', function () { it('shows lucky number', function () { $request = $this->request->create('/lucky/number', 'GET'); $response = $this->kernel->handle($request); expect(function () use ($response) { $response->send(); })->toMatchEcho( "#Your lucky number is ([0-9]|[1-8][0-9]|9[0-9]|100)#" ); }); }); });
Time to run it with command:
$ vendor/bin/kahlan
We will get the success output:
That’s it 😉
Zend Framework 2 : Routing – Literal, WildCard, Segment
Routing adalah aksi untuk mencocokkan request dari action controller yg diberikan. Routing menghubungkan keduanya. Proses request routing adalah kunci dari eksekusi flow aplikasi. Sistem Route yang dimiliki oleh Zend Framework 2 ini antara lain sebagai berikut : Baca Selengkapnya
leave a comment