Cambiar el comportamiento del buscador multiple (Buscar ... En ...) u otros filtros

Sobreescribiendo en la clase CRUD la función buildFilter podemos cambiar el comportamiento para un cierto campo.

En el siguiente ejemplo, en el CRUD el id_paciente era una referencia a otra tabla y se inserta usando un picker, por lo que conviene buscarlo por nombre en vez de por su número.

    protected function buildFilter($fieldName = '', $value = '')
    {
        // No field specified? No filter
        if (\zfx\trueEmpty($fieldName) || \zfx\trueEmpty($value)) {
            return 'FALSE';
        }

        if ($fieldName == 'id_trabajador') {
            $cond = \zfx\FieldString::cond(
                'nombre',
                \zfx\FieldString::castHttp($value, $this->loc, true),
                true,
                'p_trabajadores');
            return "id_trabajador IN (SELECT id FROM p_trabajadores WHERE ($cond))";
        } else {
            return parent::buildFilter($fieldName, $value);
        }
    }

Añadir un filtro personalizado

Si se necesita colocar un filtro independiente de los generados automáticamente, modificar alguno de los existentes o suprimir su aparición, se puede hacer sobreescribiendo las funciones del controlador CRUD (derivado de Abs_AdmCrudController) llamadas schGetFilterBoxes, schGetFilterRanges y schGetFilters. Cada una de ellas devuelve un array de cajas de búsqueda, cajas de búsqueda con rango o filtros desplegables, respectivamente.

En las funciones sobreescritas se puede capturar y modificar o suprimir los filtros calculados por el sistema, o simplemente añadir nuestros propios filtros, y finalmente devolverlo.

Devolviendo un valor falso se estaría evitando que se dibujaran los filtros que se devolverían llamando a la función ordinaria.

En el siguiente ejemplo se construye un nuevo filtro que no tiene nada que ver con las columnas de la tabla (se le ha denominado mes) y se añade al principio de todos los filtros desplegables obtenidos con la función padre. Se trata de un filtro con los nombres de los meses, obtenidos desde CalTools::monthName.

public function schGetFilters()
{
    // Construyo el filtro del mes
    $filter = new \zfx\DataFilterList('filtro_mes_renovacionesrrmm');
    $filter->loc = $this->loc;
    $filter->label = 'Mes renovación';
    $filter->values = CalTools::monthName();
    $filter->col = '[mes]';
    $filter->selected = \zfx\aa($_SESSION, $this->schId, 'values', $filter->id);
    $filter->action = $this->_urlController() . 'fil/';
    $filter->class = 'zjNoSendKey' . ($this->schCompact ? ' _compact' : '');
    $filter->datas = array(
        'adm-action'        => 'submit',
        'adm-submit-action' => $filter->action,
        'adm-submit-form'   => '#' . $filter->getFormID(),
        'adm-submit-target' => $this->lstSelector
    );

    // Lo añado al principio
    $myFilters = array($filter);
    $filters = parent::schGetFilters();
    if ($filters) {
        return array_merge($myFilters, $filters);
    }
    else {
        return $myFilters;
    }
}

Para cambiar el comportamiento del filtro (o sea, la condición SQL que genera) hay que sobreescribir la función buildFilter.

Siguiendo con el ejemplo anterior se usa el mes elegido para hacer una comparación con una subconsulta.

protected function buildFilter($fieldName = '', $value = '')
{
    if ($fieldName == '[mes]') {
        $value = (int)$value;
        $filter = 
    "id_empresa IN (SELECT id_empresa FROM _vs_renovacionesrrmm_trabajador WHERE mm = $value)";
        return $filter;
    }
    else {
        return parent::buildFilter($fieldName, $value);
    }
}

Filtros primarios y secundarios

Por defecto todos los filtros aparecen ocultos. Pero si se quiere mostrar algún filtro en la barra de búsqueda, configurar al inicio del CRUD:

Para que salga el cuadro de búsqueda:

$this->filterPrimaryBox = true;

Para que salga el cuadro de búsqueda por rango:

$this->filterPrimaryRange = true;

Para que aparezca uno o más filtros desplegables (por ejemplo, activo y clase):

$this->filterPrimary = ['activo', 'clase']

Todas las opciones son acumulables. En cualquier caso, es obligatorio colocar en el bootstraper del CRUD para el bloque de búsqueda la opción _primary a TRUE. Ejemplo:

$this->_view->addSection('body', 'zaf/' . Config::get('admTheme') . '/crud-bootstrap-search', array(
    '_title'      => 'Buscar operaciones',
    '_controller' => 'SrvBdMantprevCrud',
    '_autoFocus'  => true,
    '_primary'    => true,
));

Dependencias entre filtros

Se puede utilizar el sistema zjDepMaster para crear dependencias entre filtros. Para ello se personaliza schGetFilters() añadiendo añadiendo la clase CSS y los data- adecuados.

Ejemplo en el proyecto S-Tools: El filtro de instalación va a modificar el filtro de equipo, mostrando en dicho filtro solo los equipos que pertenecen a la instalación seleccionada:

protected function schGetFilters(array $filterList = NULL)
{
    $filters = parent::schGetFilters($filterList);
    if (array_key_exists('id_srv_equipo', $filters) && array_key_exists('id_srv_instalacion', $filters)) {

        $filters['id_srv_instalacion']->setSelectClass('zjDepMaster');

        $filters['id_srv_instalacion']->datas['zaf-dep-target'] =
                "#zjFilterList_select_" . \zfx\StrFilter::safeEncode('id_srv_equipo');

        $filters['id_srv_instalacion']->datas['zaf-dep-source'] =
                 \zfx\Config::get('rootUrl') . 'com-ajax/eqins/1/';

        // Este es el valor que se le pasa a source cuando NO se quiere filtrar
        $filters['id_srv_instalacion']->datas['zaf-dep-all']    = '0';

    }
    return $filters;
}

Ordenar los filtros

Con la variable filterListOrder se puede especificar una lista de filtros que deben salir primero. Ejemplo:

$this->filterListOrder = [
   'pfiltro_zona',
   'id_srv_servicio',
   'id_srv_instalacion',
   'pfiltro_instrami',
   'id_srv_equipo'
];

Ordenando filtros personalizados

La variable filterListOrdersolo permite ordenar filtros estándar. Si se quiere ordenar cuando hay filtros personalizados, que se crearon sobreescribiendo la función schGetFilters(), entonces se necesita algo más de trabajo.

Para ordenar, es necesario ordenar el array que se obtiene de la llamada al padre de schGetFilters() antes de devolverlo:

protected function schGetFilters(array $filterList = NULL)
{
    $filters = parent::schGetFilters($filterList);
    $filters['mi-nuevo-filtro'] = $this->crearNuevoFiltro(...);

    // Ordenar aquí los filtros
    // ....


    return $filters;
}

Este es el código completo de una posible función que ordena todos los filtros:

public function ordenarFiltros(&$filtros)
{
    // Esto ordena los filtros según nuestro orden
    $l = array_flip($this->filterListOrder);
    $i = 1000;
    foreach (array_keys($filtros) as $filtro) {
        if (!array_key_exists($filtro, $l)) {
            $l[$filtro] = $i;
            $i++;
        }
    }
    uksort($filtros, function ($a, $b) use ($l) {
        return $l[$a] > $l[$b];
    });
}

Filtro automático al visitar una tabla

Hay que añadir al controlador bootstrap (no al crud) una llamada a Abs_AdmCrudController::sessionFilter().

Ejemplo:

Abs_AdmCrudController::sessionFilter(
   '_adm-sch-Ctrl_ProvTransportistasCrud',
   'filter_Ctrl_ProvTransportistasCrud_estado',
   'estado',
   '1',
   '',
   '',
   '("mec_v_provtransportista"."estado" = 1)'
);

Forzar filtros dependiendo de los permisos

Se usa applySessionFilter():

if ($this->_getUser()->hasPermission('zona-sysadmin')) {
    $this->applySessionFilter('sys-tareas-empleado', '', '', '', '', '');
    $this->defaultRS = array();
}
else {
    $this->applySessionFilter('sys-tareas-empleado',
                              'id_com_empleado',
                              $this->_getUser()->getRef2(),
                              '',
                              '',
                              'id_com_empleado=' . $this->_getUser()->getRef2());
    $this->defaultRS = array(
        'id_com_empleado' => $this->_getUser()->getRef2()
    );
}