Using preInsert event for generating UUID with laminas-db

If you want to do something before insert data into database table, for example: generate id as UUID binary, you can do with preInsert event. For example, you have the following album table structure:
DROP TABLE IF EXISTS `album`; CREATE TABLE `album` ( `id` binary(16) NOT NULL COMMENT 'uuid binary', `artist` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ALTER TABLE `album` ADD PRIMARY KEY (`id`);
Now, to generate the UUID data, you can use ramsey/uuid, you can require:
➜ composer require ramsey/uuid
Now, time for the action!
Note, this is just a quick example to show you how it works. You can borrow your design architecture you love in real implementation.
For example, in laminas-mvc-skeleton application, we inject the IndexController with db adapter via factory:
namespace Application\Controller;
use Laminas\Db\Adapter\AdapterInterface;
class IndexControllerFactory
{
public function __invoke($c)
{
return new IndexController($c->get(AdapterInterface::class));
}
}
Now, we can update the module/Application/config/module.config.php controller factory:
// ...
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\IndexControllerFactory::class,
],
],
// ...
In our IndexController __construct, we can use the db adapter to create the TableGateway instance featuring EventFeature:
<?php
declare(strict_types=1);
namespace Application\Controller;
use Laminas\Db\Adapter\AdapterInterface;
use Laminas\Db\TableGateway\Feature\EventFeature;
use Laminas\Db\TableGateway\Feature\EventFeature\TableGatewayEvent;
use Laminas\Db\TableGateway\Feature\EventFeatureEventsInterface;
use Laminas\Db\TableGateway\TableGateway;
use Laminas\Mvc\Controller\AbstractActionController;
use Laminas\View\Model\ViewModel;
use Ramsey\Uuid\Uuid;
class IndexController extends AbstractActionController
{
private $albumTableGateway;
public function __construct(AdapterInterface $adapter)
{
$events = $this->getEventManager();
$events->attach(EventFeatureEventsInterface::EVENT_PRE_INSERT, function (TableGatewayEvent $event) {
$insert = $event->getParam('insert');
$insert->id = Uuid::uuid4()->getBytes();
});
$this->albumTableGateway = new TableGateway('album', $adapter, new EventFeature($events));
}
}
Above, with EventFeatureEventsInterface::EVENT_PRE_INSERT, we update the insert id with the binary value generated.
Let’s check with index action for insert:
// ...
public function indexAction()
{
$this->albumTableGateway->insert([
'artist' => 'Sheila on 7',
'title' => 'Pejantan Tangguh',
]);
return new ViewModel();
}
// ..
Ok, when open the index page, we will have the album table inserted with id generated in preInsert event.
mysql> SELECT LOWER(
-> CONCAT(SUBSTR(HEX(id), 1, 8)
-> , '-'
-> , SUBSTR(HEX(id), 9, 4)
-> , '-'
-> , SUBSTR(HEX(id), 13, 4)
-> , '-'
-> , SUBSTR(HEX(id), 17, 4)
-> , '-'
-> , SUBSTR(HEX(id), 21))
-> ) as id,
-> artist,
-> title
-> FROM album;
+--------------------------------------+-------------+------------------+
| id | artist | title |
+--------------------------------------+-------------+------------------+
| 551a8518-cdd2-4f3a-968d-a45a4b232b5e | Sheila on 7 | Pejantan Tangguh |
+--------------------------------------+-------------+------------------+
1 row in set (0.00 sec)
For complete event list, you can read the documentation https://docs.laminas.dev/laminas-db/table-gateway/#tablegateway-lifecycle-events
Using laminas-cli to Consume Symfony Console Command in Mezzio Application

So, you want to use Symfony Console Command in Mezzio Application? You can! There is laminas-cli for that. While it still in development, you already can give it a try. First, I assume that you already installed the mezzio application. Next, you can set minimum-stability and prefer-stable config in your composer.json:
➜ composer config minimum-stability dev ➜ composer config prefer-stable true
By above command, you can ensure that you can install the non-stable dependency, while prefer stable version if found. Next, you can require the laminas-cli via command:
➜ composer require laminas/laminas-cli
After installed, let’s create our first command: “HelloWorld command”, like the following:
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use function sprintf;
final class HelloWorld extends Command
{
protected function configure()
{
$this
->addArgument('message', InputArgument::REQUIRED, 'Greeting Message');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$message = $input->getArgument('message');
$output->writeln(sprintf('<info>Hello to world: %s<info>! ', $message));
return 0;
}
}
Greet! Now, time to register it to our App\ConfigProvider class:
<?php
declare(strict_types=1);
namespace App;
class ConfigProvider
{
public function __invoke(): array
{
return [
// ...
'laminas-cli' => $this->getCliConfig(),
// ...
];
}
public function getCliConfig(): array
{
return [
'commands' => [
// ...
'app:hello-world' => Command\HelloWorld::class,
// ...
],
];
}
public function getDependencies(): array
{
return [
'invokables' => [
// ...
Command\HelloWorld::class => Command\HelloWorld::class,
// ...
],
];
}
// ...
}
First, in getDependencies(), we register the command, if the command has dependency, you need to provide factory for it. And then, in __invoke() method, we register the commands, which you can move the commands definition in separate method. That’s it! Now, you can run the command:
➜ vendor/bin/laminas app:hello-world "Good Morning" Hello to world: Good Morning!
Using Swoole in Mezzio application with Sdebug

If you didn’t try Swoole, you need to try it. It is a PECL extension for developing asynchronous applications in PHP. If you build a Mezzio Application, there is already mezzio-swoole component that ease for its settings and usage.
First, if you didn’t have a mezzio skeleton, you can install the skeleton:
➜ composer create-project mezzio/mezzio-skeleton
Next, install the swoole extension:
➜ sudo pecl install swoole
After it, you can install the mezzio-swoole component:
➜ composer require mezzio/mezzio-swoole
That’s it, you can now open mezzio-skeleton directory and run the mezzio-swoole command, and we will get the following output:
➜ cd mezzio-skeleton ➜ ./vendor/bin/mezzio-swoole start Swoole is running at 127.0.0.1:8080, in /Users/samsonasik/www/mezzio-skeleton PHP Warning: Swoole\Server::start(): Using Xdebug in coroutines is extremely dangerous, please notice that it may lead to coredump! in /Users/samsonasik/www/mezzio-skeleton/vendor/mezzio/mezzio-swoole/src/SwooleRequestHandlerRunner.php on line 169
If you have Xdebug installed, you will get above command output “PHP Warning” output. To fix it, we can uninstall the Xdebug, and install Sdebug instead. We can do the following command:
➜ sudo pecl uninstall xdebug ➜ git clone https://github.com/swoole/sdebug.git ➜ cd sdebug && sudo ./rebuild.sh
Now, you will get the Sdebug information if we run php -v:
➜ ~ php -v
PHP 7.4.4 (cli) (built: Mar 24 2020 10:45:52) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Sdebug v2.9.3-dev, Copyright (c) 2002-2020, by Derick Rethans
with Zend OPcache v7.4.4, Copyright (c), by Zend Technologies
Let’s try run mezzio-swoole command again:
➜ cd mezzio-skeleton ➜ ./vendor/bin/mezzio-swoole start Swoole is running at 127.0.0.1:8080, in /Users/samsonasik/www/mezzio-skeleton Worker started in /Users/samsonasik/www/mezzio-skeleton with ID 0
If you got Segmentation fault in the future, that may because of the `Sdebug`, if you don’t require the ‘Xdebug’/’Sdebug’ feature. You can just uninstall them all together
Succeed! Now, time to benchmark! I used wrk for it. I tested it in Macbook Pro 2011, core i5, with 16GB RAM. I access the page with HTML+JS+CSS in there.
1. Without Swoole
Let’s CUT the previous mezzio-swoole ( type CTRL + C ) command and use PHP Development server:
➜ cd mezzio-skeleton ➜ composer serve > php -S 0.0.0.0:8080 -t public/ [Sun Apr 5 12:24:15 2020] PHP 7.4.4 Development Server (http://0.0.0.0:8080) started
Now, we can run the benchmark with run wrk command in separate terminal:
➜ wrk -c 1000 -t 10 http://localhost:8080/
Running 10s test @ http://localhost:8080/
10 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.33s 629.53ms 2.00s 50.33%
Req/Sec 16.09 12.96 60.00 69.26%
544 requests in 10.09s, 4.25MB read
Socket errors: connect 759, read 580, write 1, timeout 393
Requests/sec: 53.90
Transfer/sec: 430.92KB
2. With Swoole
Let’s CUT the previous PHP Development server command ( type CTRL + C ) command and use mezzio-swoole command:
➜ cd mezzio-skeleton ➜ ./vendor/bin/mezzio-swoole start Swoole is running at 127.0.0.1:8080, in /Users/samsonasik/www/mezzio-skeleton Worker started in /Users/samsonasik/www/mezzio-skeleton with ID 0
Now, we can run the benchmark with run wrk command in separate terminal:
➜ wrk -c 1000 -t 10 http://localhost:8080/
Running 10s test @ http://localhost:8080/
10 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.15s 590.35ms 2.00s 54.14%
Req/Sec 30.13 35.31 170.00 83.03%
1373 requests in 10.09s, 25.10MB read
Socket errors: connect 759, read 80, write 0, timeout 418
Requests/sec: 136.07
Transfer/sec: 2.49MB
Above, we get double total requests with swoole in same time! That’s it!
References:
- https://docs.mezzio.dev/mezzio-swoole/v2/intro/
- https://github.com/swoole/swoole-src/issues/1681#issuecomment-608980271/
- https://github.com/swoole/sdebug
- https://github.com/ezimuel/swoole-benchmark
- https://formulae.brew.sh/formula/wrk#default
5 comments