Brain-WP / Cortex

Routing system for WordPress
MIT License
348 stars 20 forks source link

[feature request] Closures as a value for the `template` setting #22

Closed wujekbogdan closed 2 years ago

wujekbogdan commented 6 years ago

At the moment a template can take only a string as an argument. It would be great if we also could use a closures. The closure could take $matches and ideally s $wp_query (if it's available at this point) as an argument.

Example:

$routes->addRoute( new QueryRoute(
    'my/route/{param}',
    function ( array $matches ) {
        return [
            'post_type' => 'my_post_type',
        ];
    },
    [
        'template' => function ( array $matches, \WP_Query $query ) {
            if ( $matches['param'] === 'something' && $query->post_count > 1 ) {
                return 'a-tempalte.php';
            }

            return 'another-template.php';
        }
    ]
) );
gmazzap commented 6 years ago

Hi @wujekbogdan yep, nothing against that. Don't have much time these days, but if you have time to work on a PR I could review & accept... Or you can wait a bit :)

gmazzap commented 6 years ago

@wujekbogdan I liked the idea and worked a bit on it.

However, it was no possible to implement it as you described.

Because of compatibilty reasons, the callback must be executed before the "before" callback of the route.

It means that WP_Query is not parsed yet so won't be possible to do pass a WP_Query object to the callback.

In next version I plan to improve this, but for now it works like this:

$routes->addRoute( new QueryRoute(
    'my/route/{param}',
    function ( array $matches ) {
        return [
            'post_type' => 'my_post_type',
        ];
    },
    [
        'template' => function ( array $vars, \WP $wp, array $matches ) {
            if ( $matches['param'] === 'something') {
                return 'a-template.php';
            }

            return 'another-template.php';
        }
    ]
) );

So you get the query variables that are going to be set, the WP instance and the matches, but not the query object.

But with a little trick you can obtain your wanted results. First you could define a class like this:

class ConditionalTemplate
{
  private $condition;

  public function __construct(callable $condition)
  {
    $this->condition = $condition;
  }

  public function __invoke(array $vars, \WP $wp, array $matches)
  {
    add_action('template_redirect', function () use ($matches) {

      global $wp_query; // at this point the query is available
      $cb = $this->condition;
      $template = $cb($matches, $wp_query);

      $template and add_filter('template_include', function () use ($template) {
        return $template;
      }, PHP_INT_MAX);
    }, 0);

    return null;
  }
}

and then:

$routes->addRoute( new QueryRoute(
  'my/route/{param}',
  function ( array $matches ) {
    return [
      'post_type' => 'my_post_type',
    ];
  },
  [
    'template' => new ConditionalTemplate(function (array $matches, \WP_Query $query) {
      if ($matches['param'] === 'something' && $query->post_count > 1) {
        return '/path/to/a-template.php';
      }
      return locate_template('index.php');
    });
  ]
) );

This should work quite fine, waiting for easier way on Cortex v2 :)

Note that this feature is not released, you could test by requiring Cortex from branch refactoring-fastorute I could merge after some feedback.

wujekbogdan commented 6 years ago

@gmazzap

I liked the idea and worked a bit on it.

That's great. Thank you very much!

But with a little trick you can obtain your wanted results.

Thanks for the example. It's a bit hacky, but your implementation is quite clean (and quite smart too) anyway.


This should work quite fine, waiting for easier way on Cortex v2 :)

So, you are working on a new version of Cortex? What features can we expect? Sorry for the offtopic, but I'm very curious what features the new version will bring. For example, I would love to be able not only to define routes but also to easily create URL-s that point to a specific route... but it such change would require named routes (that can possibly break the backward compatibility). Have a look at vue-router named routes and the router.push() method - this is what I'm talking about.

gmazzap commented 6 years ago

@wujekbogdan named routes and URL generation were a thing in Cortex 0. (based on Symphony routing component) and on the Cortex predecessor "Clever Rules" (now abandoned). They were removed from Cortex 1. switching to FastRoute. But, yes, you can expect they be there on v2. Besides of that, v2 will use latest version of FastRoute with native support for route groups, some caching mechanism and drop support for PHP 5. ETA: no idea, sometime in 2018, maybe.