public function cast($field = null, $data = [], $options = [])
    {
        $defaults = [
            'parent'    => null,
            'basePath'  => null,
            'exists'    => null,
            'defaults'  => true
        ];
        $options += $defaults;

        $options['class'] = $this->reference();

        if ($field !== null) {
            $name = $options['basePath'] ? $options['basePath'] . '.' . $field : (string) $field;
        } else {
            $name = $options['basePath'];
        }

        if ($name === null) {
            return $this->_cast($name, $data, $options);
        }

        foreach([$name, preg_replace('~[^.]*$~', '*', $name, 1)] as $entry) {
            if (isset($this->_relations[$entry])) {
                return $this->_relationCast($field, $entry, $data, $options);
            }
            if (isset($this->_columns[$entry])) {
                return $this->_columnCast($field, $entry, $data, $options);
            }
        }

        if ($this->locked()) {
            return $data;
        }

        if (is_array($data)) {
            $options['class'] = Document::class;
            $options['basePath'] = $name;

            if (isset($data[0])) {
                return $this->_castArray($name, $data, $options);
            } else {
                return $this->_cast($name, $data, $options);
            }
        }

        return $data;
    }