zfcampus / zf-apigility

BSD 3-Clause "New" or "Revised" License
258 stars 51 forks source link

Input filters applied on DB-connected resources, not on code-connected #22

Closed RobQuistNL closed 10 years ago

RobQuistNL commented 10 years ago

As I was working with Apigility, I noticed that filters did not get applied.

I went through the code with a collegue, and found that it does get applied on DB-connected resources.

See; https://github.com/zfcampus/zf-apigility/blob/master/src/ZF/Apigility/DbConnectedResource.php#L32

Is this intentional? Wouldn't it be better to have the filters being applied on both code- and DB-connected resources? At least I would expect it, as the validation is done on both code and DB connected resources, but the filters only on DB connected.

weierophinney commented 10 years ago

The input filter does get applied to code-connected resources, and Apigility will report validation errors.

You are confusing validation with retrieving the validated data.

With DB-connected resources, we made a decision that any values not in the input filter should be deemed suspect, as they likely will lead to DB errors for unknown columns during insertions or updates. As such, we inject the input filter into the DB-Connected resource, and pull validated values from it (when an input filter exists, that is).

For Code-Connected resources, you, as a developer, need to decide what to do. If you are doing any data normalization (i.e., adding filters to fields), or if you want to ensure you only get the values defined in the input filter, then you need to inject your input filter into your code-connected resource, and pull them from there. We are not doing this by default at this point, as we do not want to make any assumptions about your required workflow; you should write your own factory to provide your own injections.

RobQuistNL commented 10 years ago

:+1: thanks Matthew!

weierophinney commented 10 years ago

@RobQuistNL Actually, just remembered something: you do have access to the input filter in your code-connected resources, via the ResourceEvent instance:

$event = $this->getEvent();
$inputFilter = $event->getInputFilter();
if (!$inputFilter) {
    // Something is either wired incorrectly, or you did not create any fields for the resource
} else {
    $rawValues = $inputFilter->getRawValues();
    $filteredValues = $inputFilter->getValues();
    // etc.
}
RobQuistNL commented 10 years ago

Neat, we have indeed created a trait "FilterResourceInputTrait"

trait FilterResourceInputTrait {
    /**
     * Filter the input of given data
     *
     * @param $data
     * @return array
     */
    protected function getFilteredData($data)
    {
        if ($this->getInputFilter()) {
            $filter = $this->getInputFilter();
            $data = $filter->getValues();
            return $data;
        } else {
            $data = (array)$data;
            return $data;
        }
    }
}

Included this trait in all code-connected resources, and used them like this;

class AccountResource extends AbstractResourceListener
{
    use FilterResourceInputTrait;

    /**
     * Create a resource
     *
     * @param  mixed $data
     * @return ApiProblem|mixed
     */
    public function create($data)
    {
        $data = $this->getFilteredData($data);
        //Do stuff with the data
    }
}

This seems to suffice. Thanks for your time @weierophinney :)