public function isValid($vals)
    {
        $fields = $this->getFields();
        if (!Arr::iterable($fields)) {
            return true;
        }

        $result = true;

        // validate each field
        foreach ($fields as $k => $field) {
            // check if required
            if ($this->isRequired($field)) {
                if (!array_key_exists($k, $vals)
                        || is_null($vals[$k])
                        || $vals[$k] === ''
                        || $vals[$k] === false
                        || ($field['type'] === 'checkbox' && empty($vals[$k]))
                    ) {
                    $result = false;
                    $this->_messages[$k] = $this->getFieldRequiredMessage($k);
                    continue;
                }
            }

            // check maxlength
            if (array_key_exists('maxlength', $field)) {
                if (strlen($vals[$k]) > $field['maxlength']) {
                    $result = false;
                    $this->_messages[$k] = 'Value too long';
                    continue;
                }
            }

            // after this point, we're only checking values based on type
            if (!array_key_exists($k, $vals)) {
                continue;
            }
            if (!array_key_exists('type', $field)) {
                continue;
            }
 
            // Select
            if ($field['type'] === 'select') {
                if (!array_key_exists($vals[$k], $field['options'])) {
                    $result = false;
                    $this->_messages[$k] = 'Invalid option';
                }
                continue;
            }

            // Email
            if ($field['type'] === 'email') {
                if (!filter_var($vals[$k], FILTER_VALIDATE_EMAIL)) {
                    $result = false;
                    $this->_messages[$k] = 'Invalid email address';
                }
                continue;
            }

            // URL
            if ($field['type'] === 'url') {
                if (!filter_var($vals[$k], FILTER_VALIDATE_URL)) {
                    $result = false;
                    $this->_messages[$k] = 'Invalid URL';
                }
                continue;
            }

            // Color
            // http://www.w3.org/TR/html-markup/input.color.html#input.color.attrs.value
            if ($field['type'] === 'color') {
                if (!preg_match('/^#[0-9a-f]{6}$/i', $vals[$k])) {
                    $result = false;
                    $this->_messages[$k] = 'Invalid color';
                }
                continue;
            }
        }
        return $result;
    }