Alex Tartan Alex Tartan - 1 month ago 11
reST (reStructuredText) Question

ZF-Rest $event->getRouteMatch() is null when extending RestController

I'm using the

zf-rest
composer package in my project.

This is the
create
method in my resource file.

public function create($data)
{
var_dump($e->getRouteMatch()->getParams());
// all URL params are listed here with the correct values
die();
}


I've now extended
RestController
to
CustomRestController
and added

'controller_class' => CustomRestController::class


to the config file.

Now,
var_dump($event->getRouteMatch());
is
null
inside the Resource classs.

In
public function onBootstrap(MvcEvent $e)
, using

$eventManager->attach(
MvcEvent::EVENT_ROUTE,
function($e) {
// I can still read `$e->getRouteMatch()->getParams()`
}
);


Any suggestion on how to deal with this is greatly appreciated.

Answer

Updated answer:

The root problem is RestParametersListener. It's using FQCNs like:

$listener = $events->attach(
    RestController::class,
    // more params
);

So extending the RestController causes these attach/detach calls to avoid the new controller.

A clean fix was to extend RestParametersListener to CustomRestParametersListener and change the controller classes inside.

After extending this, I've also added it in module.config.php:

'service_manager' => [
    'invokables' => [
        'ZF\Rest\RestParametersListener' => CustomRestParametersListener::class,
    ],
    // ...
]

Initial answer:

Found a workaround until issues 102 and 103 get fixed by the zf-rest dev team

In my CustomRestController:

try {
    $this->manuallyInjectRouteMatch(); // added this line
    $value = $this->getResource()->create($data);
} catch (Throwable $e) {
    return $this->createApiProblemFromThrowable($e);
} catch (Exception $e) {
    return $this->createApiProblemFromException($e);
}

Where:

/**
 * By using this factory instead of the default one and by creating a CastorRestController
 * instead of RestController, the routeMatch property is "lost".
 * Manually injecting it so we have access to it inside the *Resource class
 */
private function manuallyInjectRouteMatch()
{
    $r = new \ReflectionProperty(Resource::class, 'routeMatch');
    $r->setAccessible(true);
    $r->setValue($this->getResource(), $this->getEvent()->getRouteMatch());
}

Also, the manuallyInjectRouteMatch needs to be called for the other REST methods as well