noodlehaus/dispatch

(Proposal) If a filter returns a value, pass that to on() callback

Closed this issue · 2 comments

Basically I want something like this:

filter('task', function($taskId) {
    return Task::find($taskId);
});

// GET /tasks/123
on('GET', '/task/:task', function($task) {
    // Passed value is the result of the filter
    assert(is_null($task) || $task instanceof Task);

    // Original value still available in params(): 123
    params('task');
});

The downside - we can have more than one filter per param - I'm not sure what the best solution would be for that. I guess either:

  1. Passing the result of one filter to the next, or the original value if nothing is returned (only makes sense if every filter returns a value),

    foreach ($filters as $symbol => $callback) {
        $res = call_user_func($callback, $val);
        if (!is_null($res)) $val = $res;
    }
    
  2. Passing the original value to each filter, and passing an array of filtered values if more than one filter returns something.

    // Get the results of the filters
    $results = [];
    foreach ($filters as $symbol => $callback) {
        $res = call_user_func($callback, $val);
        if (!is_null($res)) $results[] = $res;
    }
    
    // Decide what to pass to the on() callback
    switch (count($results)):
        case 0:
            // No filters returned values, pass original value
            $values[$symbol] = $val;
            break;
    
        case 1:
            // 1 filter returned a value, pass this on it's own
            $values[$symbol] = array_shift($results);
            break;
    
        default:
            // More than 1 filter returned a value, so pass an array
            $values[$symbol] = $results;
            break;
    }
    
    ...
    call_user_func_array($on, $values);
    

I'll probably hack something up, but do you have any thoughts on this?

hi ross. this is just how i really wanted it before, until i had a need to stack filters on params. my work around for this is to put the things I generate in filters into scope() calls. so from your example:

<?php
// load the task and scope() it
filter('task', function ($taskId) {
  $task = Task::find($taskId);
  scope('task', $task);
}

on('GET', '/tasks/:task', function ($taskId) {
  // we can fetch task data from scope()
  $taskData = scope('task');
  // ...
});

i never thought about chaining/piping (ala middleware) the filter return values (if any), and since the starting value is still accessible via params(), it might be a good idea. can't think of a use case for it up front though, but if you do, please share.

i looked at how expressjs does this and it's somewhat similar to the filter + scope combination. they allow filters to stuff the request object with new properties.

let's not confine this to the filter concept. it could be something else as well.

My only real use case was that once I'd put the task model into the scope, I didn't have a use for the parameter 😄

Laravel has something like this with model binding. I agree a different hook is the way to go; I don't think there's a sensible way to chain results while you can optionally return null. I've added a bind() function in #32 - thoughts?