Welcome to Abdul Malik Ikhsan's Blog

Create “Abstract Factory” service in PHP-DI with RequestedEntry and Wildcards

Posted in Slim 4, Tutorial PHP by samsonasik on November 6, 2019

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!

Tagged with:

Functional Test Symfony 4 with Kahlan 4

Posted in Symfony 4, testing by samsonasik on January 3, 2018

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 😉

Tagged with:

Zend Framework 2 : Routing – Literal, WildCard, Segment

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 21, 2012

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