fossar / selfoss

multipurpose rss reader, live stream, mashup, aggregation web application
https://selfoss.aditu.de
GNU General Public License v3.0
2.36k stars 343 forks source link

Switch away from fatfree for routing #1265

Closed jtojnar closed 2 years ago

jtojnar commented 3 years ago

Slowly tearing fatfree out of the codebase.

We should probably wait for after 2.19 so that we can stuff depending on use newer PHP.

Alternatives:

See also:

kktsvetkov commented 2 years ago

@jtojnar I've created a new router, https://github.com/ertuo-php/ertuo -- Are you interested to have a look ?

jtojnar commented 2 years ago

@kktsvetkov Interesting concept. Unfortunately, selfoss still targets PHP 5.6 for now so we cannot use it. I hope to bump target version soon and then I will re-evaluate.

Could you clarify few points?

kktsvetkov commented 2 years ago

@jtojnar Thanks for having a look at it!

  • Still do not understand what are the keys used for other than the default route.
  • How does the tree select a route when multiple child route groups in a route group match?

These two are related. The keys are used to select the next child route.

  • What other routing frameworks did you look at?

Only PHP libraries. The main focus was on Fast Route and Symfony as the most popular ones.

  • Why are you using an empty key with both fallback (404) and empty chunk?

The fallback is not really a 404, it is a default value you might want to use instead. I wanted to have the route tree configuration as simple as possible and re-using the empty key kind of makes sense as in both cases (no match or empty step) there is nothing accepted.

  • It is not clear to me how to attach handlers and dispatch those

It is the same concept Symfony is using. At the end of the dispatching, they have an array with parameters, and some of the parameters are "reserved" as they carry the handler that should be called.

The methods corresponding to HTTP verbs do indeed set some attributes. At the end of dispatching, it is just like you put it -- you get the matched handler from the result attributes and call it.

jtojnar commented 2 years ago
  • Still do not understand what are the keys used for other than the default route.
  • How does the tree select a route when multiple child route groups in a route group match?

These two are related. The keys are used to select the next child route.

It is not obvious to me from the README how are they used to select next child. Oh, I see now. The exploded parts are looked up in the iterable. Maybe the README could emphasize this point.

  • It is not clear to me how to attach handlers and dispatch those

It is the same concept Symfony is using. At the end of the dispatching, they have an array with parameters, and some of the parameters are "reserved" as they carry the handler that should be called.

In Symfony, you directly set controller on the route you create and that then gets dispatched somehow.

$routes->add('api_post_show', '/api/posts/{id}')
        ->controller([BlogApiController::class, 'show'])
        ->requirements(['id' => '\d+'])
        ->methods(['GET', 'HEAD']);

I understand that running the controller is not actually a goal for a routing library but I feel it would still be useful to show a simple example of that.

kktsvetkov commented 2 years ago

Thanks, this is good feedback.

The README is a struggle, as it is not my strongest part to explain how things work. I've written a couple of articles trying to get into more details:

The methods and controller thing, perhaps for the same route (user/123) there will be different action methods associated with the different HTTP verbs. I don't want to repeat things in route declarations, so the best thing I came up with was to attach these pairs of HTTP methods and action callbacks:

yield '' => Route::add()
    ->get('BlogPost::show')
    ->post('BlogPost::update') ...

This is in the README, but obviously, it is not obvious :sunglasses: Since a day or two ago I also added Route::any() and Route::match() and I just have to figure out an easier way to actually extract the controller action. Calling the action is a bit more nuanced since different projects and frameworks pass different things, not only the Request object for example but some DI stuff as well. It is better if each decides how to handle it.