Comments (10)
// 1. We have only 1 route and we have to add a subst param ['action' => 'create'] in UrlHelper
IIRC, the url helper re-uses the current request value if none is given.
// 2. We to examine the request action-attribute in the controller(handler) handle method to reroute the request to internal method
What's wrong with an abstract controller class which handles the crud logic detection and redirects to an action method if it exists?
from mezzio.
Hello @geerteltink,
-
But the url-helper is used to build different uris, not just for the current route.
$url('/news/article/index')
vs$url('/news/article', ['action' => 'index'])
$url('/news/article/create')
vs$url('/news/article', ['action' => 'create'])
$url('/news/article/update', ['id' => 123])
vs$url('/news/article', ['action' => 'update', 'id' => 123])
For me atomic route-names are clearer. -
Nothing wrong about it, that's how we do it right now, but, repeating briefly my opening comment:
- We'd have 2 alternative middleware/handlers definitions (that other routers/frameworks already support)
- route definitions looks nicer too...
- the actual callable middleware is now actually intercepting those 2 types of definitions but is unable to resolve them
-
Being an addon, one can still continue to use an abstract controller (either a middleware or a request-handler or both....I use a middleware to avoid instantiating another wrapper (lazy-middleware) and to pass down to the next middleware when the action is not matched by any internal method)
kind regards
from mezzio.
@geerteltink
There is also another benefits for atomic routes:
- define per-route supported http-verbs
- define per-route middleware pipelines
ROUTE VERBS ROUTE-MIDDLEWARES
news/article/create POST [BodyParserMiddleware, ArticleValidatorMiddleware, News\ArticleController::create]
news/article/edit GET [News\ArticleController::edit]
news/article/update PUT|PATCH|POST [BodyParserMiddleware, ArticleValidatorMiddleware, News\ArticleController::update]
news/article/delete DELETE|POST [News\ArticleController::delete]
All previous routes expect an handler with the same-dependenies (article persistence model/orm) but they require different http-methods and may require additional middlewares.
from mezzio.
I'm trying to understand what the underlying idea is. I'm guessing it is the generation of factories, or having too many of them.
There are factory generators available with mezzio/mezzio-tooling
and laminas/laminas-servicemanager
. You can also reuse a factory for different handlers to inject the same dependencies.
Your proposal might be a nice solution, but it might complicate the configuration with too many options.
I would suggest not to use this $app->route('/news/article/update/{id}', ['News\ArticleController::update']);
. You loose the FQCN completion.
I guess you end up with something like this:
$app->route('/news/article/create', [News\ArticleController::class, 'create']);
However that might give issues because middleware can be an array, so you would get something like this:
$app->route('/news/article/create', [
[News\ArticleController::class, 'create']
]);
Something else I'm thinking about is that you create your own MiddlewareInterface. I think you can wrap your controller class in that and pass it directly. A side effect is that you can't cache the config.
from mezzio.
Hello @geerteltink,
I would like to be able to handle both cases ("fqcn::method" and ["fqcn", "method"]) while keeping atomic routes for the reasons I stated earlier. Both the array version ["fqcn", "method"] and the string version are actually intercepted by the callable-handler case: https://github.com/mezzio/mezzio/blob/master/src/MiddlewareFactory.php#L78 so in this example:
$app->route('/news/article/create', [News\ArticleController::class, 'create']);
the array is not treated as a mw-pipeline, if the class exists and the specified method is callable.
My idea is to intercept the is_callable() case and add a sub case with a $this->controller() call, where the controller method would handle 3 sub cases:
string "FQCN::method" => tranform into [FQCN::class, 'method'] array case
array [FQCN::class, 'method'] => create a LazyControllerMIddleware
array [$obj, 'method'] => create a ControllerMIddleware
I also prefer FQCN completion, but it would be nice to handle the "class::method" callable string case, like other routers do. It may be also visually clearer than the array case, even if I'd most probably use the last mentioned.
pseudo-code
//...
class LazyControllerMIddleware implements MiddlewareInterface
{
public function __construct(
Container $container,
$middleware
) {
$this->container = $container;
$this->middleware = $middleware;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
list($fcqn, $method) = $this->resolve($this->middleware);
// if we want to handle the [$instance, 'method'] case
if (is_object($fcqn)) {
return $fcqn->{$method}($request);
}
if ($this->container->has($fcqn)) {
$controller = $this->container->get($fcqn);
return $controller->{$method}($request);
}
return $handler->handle($request);
}
private function resolve($middleware): array
{
if (is_array($middleware)) {
return $middleware;
}
if (is_string($middleware) && 0 < strpos($middleware, '::')) {
return explode('::', $middleware);
}
///throw ...
}
}
//...
kind regards
from mezzio.
I started implementing this, but I found out that I need the root-container (to pull controller-instances if defined as services, I updated my example above) not the middle-ware controller. So i need the MiddlewareFactory to have access to the root-container i.e. I need to make MiddlewareFactoryFactory pass down the root-container as well...
from mezzio.
I added a branch (against develop) with a possible implementation:
https://github.com/pine3ree/mezzio/tree/add-controller-middleware
-
added access to the root-container in MiddlewareFactory
-
added controller-middleware (pulling the controller from the cntainer or just creating a new instance)
-
many unrelated tests are failing (even in the original master) some also complaining about missing psr stream interface. I can make test/Middleware tests pass with php>=7.2. php-7.1 seems not to work in all branches...
pinging @geerteltink (and @michalbundyra @weierophinney if interested in this matter)
kind regards
from mezzio.
Simple test with skeleton:
<?php
// file: src/App/Controller/Home
declare(strict_types=1);
namespace App\Controller;
use Laminas\Diactoros\Response\TextResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class Home
{
public function door(ServerRequestInterface $request) : ResponseInterface
{
return new TextResponse(__METHOD__);
}
public function kitchen(ServerRequestInterface $request) : ResponseInterface
{
return new TextResponse(__METHOD__);
}
public function bedroom(ServerRequestInterface $request) : ResponseInterface
{
return new TextResponse(__METHOD__);
}
}
<?php
// file: config/routes.php
declare(strict_types=1);
use App\Controller\Home;
use Mezzio\Application;
use Mezzio\MiddlewareFactory;
use Psr\Container\ContainerInterface;
return static function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
$app->get('/', App\Handler\HomePageHandler::class, 'home');
$app->get('/api/ping', App\Handler\PingHandler::class, 'api.ping');
$app->get('/home/door', [App\Controller\Home::class, 'door'], 'home/door');
$app->get('/home/kitchen', 'App\Controller\Home::kitchen', 'home/kitchen');
$app->get('/home/bedroom', [
// test mw-pipeline including a callable-array-controller definition
function ($request, $next) {
$response = $next->handle($request);
$text = $response->getBody()->getContents();
$response->getBody()->write("{$text}\nI did not do anything!");
return $response;
},
[App\Controller\Home::class, 'bedroom'],
], 'home/bedroom');
};
from mezzio.
This is not something we really want to enable. A big part of the reason we removed the handler-as-controller documentation you linked for version 3 was because we feel the paradigm of one route, one handler, helps tremendously with preventing the "fat controller" tendency of MVC systems. (One thing to note, however: we also consider a route to consist not just of the path, but also the HTTP method.)
More importantly, we feel that handlers should implement the RequestHandlerInterface
. This makes it clear what their purpose is.
What you want to achieve can be done in userland easily already, and would consist of the following parts:
-
A special
RequestHandlerInterface
implementation that would receive the PSR-11 container and the middleware specification as constructor arguments. Internally, it would split them on the::
string, retrieve the left side from the container, and call the method from the right side:[$class, $method] = explode('::', $this->specification); $instance = $this->container->get($class); return $instance->$method($request);
Alternately, you could have it handle arrays in the format
[$className, $methodName]
, or even handle both scenarios. -
An extension to the
Mezzio\MiddlewareFactory
that adds a method with a name likecontroller()
or similar. This method would create an instance of the class defined in 1. above. (If you look in this class, you'll see another reason why we do not want to support syntax like what you propose: it means strings and/or arrays now have two possible resolutions, and it becomes far more difficult to understand which is being requested!) -
Override the
Mezzio\MiddlewareFactory
service in your application to return your extension instead. -
In your
routes.php
, you could then do:$app->route('/home/bedroom', [ // some middleware, and then... $factory->controller(App\Controller\Home::class . '::bedroom') ], 'home/bedroom');
Again, this is not something we want to add at this time, as it is yet one more way to define handlers, and we want to encourage defining RequestHandlerInterface
implementations, versus having a convention-driven approach.
from mezzio.
just wanted to give it a try :-)
- Override the Mezzio\MiddlewareFactory service in your application to return your extension instead.
Yes, this is what I have been doing, and ported that code into my proposed branch...the nice thing is that the callable case is already intercepting these 2 cases ensuring the existence of class/method combination....anyway...it's fine...I understand the will to encourage atomic req-handlers in these repos!
kind regards
from mezzio.
Related Issues (20)
- Create `Mezzio\ApplicationPipeline` implementation for better static code analysis HOT 1
- Missing good examples of configuration template paths for laminas-view in mezzio HOT 3
- Replace `RequestHandlerRunner` factory with `RequestHandlerRunnerInterface` factory HOT 3
- Page with the description of the installation is missing
- Dependency Dashboard
- [RFC]: Provide an ApplicationProgrammaticConfigDelegator that pairs with ApplicationConfigInjectionDelegator HOT 4
- Script MezzioInstaller\OptionalPackages::install handling the pre-install-cmd event terminated with an exception HOT 3
- Upgrading to PHP 8.2 breaks Dependency Injection HOT 9
- [RFC]: Remove Pimple from Mezzio documentation and from Mezzio skeleton HOT 3
- bin/mezzio-tooling is incompatible with mezzio/mezzio-tooling v2 HOT 7
- [RFC]: Deprecate `ApplicationConfigInjectionDelegator` and `Application#post`, etc.
- Provide `RouteCollectorInterface` and `MiddlewarePipeInterface` delegators to supersede `ApplicationConfigInjectionDelegator` HOT 1
- jsoumelidis/zend-sf-di-config[0.4.0, ..., 0.4.1] require php ^7.1 -> your php version 8.0.99 HOT 1
- PhpDebugBar documentation is outdated. HOT 1
- [RFC]: Remove Aura.Di from Mezzio documentation and from Mezzio skeleton HOT 1
- [RFC]: Add Symfony DependencyInjection as container HOT 2
- [RFC]: Add chubbyphp-container as container
- Move router integration tests to router packages
- PHP 8.1 Support HOT 12
- Support for laminas-httphandlerrunner 2.x versions HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mezzio.