Outils pour utilisateurs

Outils du site


Panneau latéral

ref:backoffice:filters

Filtres

Généralités

L'utilisation des filtres nécessite le module filter qui :

  • n'est pas visible en backoffice
  • apporte le nécessaire pour permettre d'intégrer un champ de type query dans le formulaire backoffice d'un document

Les filtres peuvent être utilisés dans différents contextes :

  • module comptes clients (groupes dynamiques)
  • module commandes (dossiers intelligents)

En fonction de ce que l'on attend d'un filtre, il implémente l'une, l'autre ou l'ensemble des méthodes suivantes :

  • getQuery permettant de requêter les documents qui satisfont le filtre
  • checkValue permettant de valider qu'un document donné satisfait le filtre

Intégration d'un champ de type query dans un document Change

Dans le modèle du document

Ajouter une propriété de type Lob dans le modèle du document.

Exemple :

<add name="query" type="Lob" min-occurs="1" />

Dans le formulaire backoffice du document

Dans les fichier où cette propriété est éditable, c'est à dire, selon les cas : create.xml et/ou properties.xml et/ou localization.xml, spécifier pour cette propriété les deux attributs suivants :

  • type : doit prendre la valeur query (le binding cQueryField du module filter sera alors utilisé)
  • listid : permet d'indiquer le modèle sur lequel on veut effectuer la requête sous la forme “module/document” et optionnellement spécifier quelles méthodes sont requises sur le filtre

Exemples :

<!-- accepte les filtres sur les comptes clients -->
<field name="query1" type="query" listid="customer/customer"/>
<!-- accepte les filtres que sur les comptes clients sur lesquels à la fois getQuery et checkValue sont implémentés -->
<field name="query2" type="query" listid="customer/customer::getQuery,checkValue"/>

Définition des filtres

Un champ du formulaire de type query va permettre de construire une requête à l'aide de différents filtres. Ces filtres doivent être définis par code dans n'importe quel module : la commande change.php compile-document-filters se charge de retrouver l'ensemble des filtres et de les grouper selon le modèle sur lequel ils portent. Un filtre est une classe PHP à placer dans le dossier monModule/persistentdocument/filters (seuls ces dossiers sont scannés par la commande de compilation des filtres).

Les filtres doivent implémenter l'interface f_persistentdocument_DocumentFilter qui définit les différentes méthodes nécessaires. Une implémentation par défaut d'un certain nombre de ces méthodes est disponible, il s'agit de la classe f_persistentdocument_DocumentFilterImpl. Dans la majorité des cas, c'est cette classe qu'étendra le filtre. Les méthodes à implémenter seront alors les suivantes :

  • getDocumentModelName() : méthode statique qui renvoie simplement le modèle sur lequel ce filtre s'applique, sous la forme modules_monModule/monDocument.
  • __construct() : le constructeur se charge de définir et configurer les différents paramètres du filtre. Ces paramètres peuvent être de deux types différents :
    • restriction (f_persistentdocument_DocumentFilterRestrictionParameter) : l'utilisateur pourra paramétrer une restriction complète : type de restriction, champ sur lequel elle porte et valeur. On peut spécifier la liste des champs et des restrictions disponibles pour un paramètre donné ou bien les spécifier en dur (ils ne seront alors pas modifiable dans l'interface)
    • valeur (f_persistentdocument_DocumentFilterValueParameter) : l'utilisateur pourra simplement saisir une valeur qui sera ensuite utilisée pour écrire la requête. Le champ proposé sera mono-valué et pourra être de n'importe quel type standard (entier, flottant, document, chaine…). On définit ce type à l'aide d'une BeanPropertyInfo qui peut être configurée à la main ou, s'il s'agit d'une propriété d'un document, récupérée directement d'un modèle. Attention, les contraintes de validation définies ne seront pas prises en compte, le champ sera juste obligatoire et validé avec via les validateurs par défaut du champ
  • getQuery() : cette méthode renvoie la query (instance de f_persistentdocument_criteria_Query) correspondant au filtre. Celle-ci n'est supposée être appelée que sur un filtre où tous les paramètres ont été remplis
  • checkValue($value) : cette méthode prend en paramètre une instance de document et renvoie true si elle satisfait le filtre, false sinon

On trouve aussi dans certains filtres d'autres méthodes qu'a priori on n'implémentera pas sur un filtre spécifique d'un projet :

  • isHidden() : si elle retourne true, ce filtre ne sera pas sélectionnable dans les listes (utilisé pour déprécier des filtres sans pour autant casser les documents qui l'utilisent déjà)
  • getAliases() : renvoie un tableau de noms de classes considérées comme des alias de ce filtre. Ces noms correspondent à des classes qui n'existent pas physiquement, si ces noms sont retrouvés dans la configuration d'un document, un instance de ce filtre sera utilisée (utilisé lors du renommage de la classe d'un filtre, par exemple pour le changer de module, sans casser les documents qui l'utilisent déjà)

Pour initialiser un nouveau filtre on utilise la commande :

change.php filter.add-filter <moduleName> <name> <modelName> [<commaSeparatedParamNames>]

Exemple : La commande suivante génèrera un filtre MonFiltre dans le module monmodule portant sur le modèle modules_catalog/catalog avec trois paramètres nommés respectivement param1, param2 et param3.

change.php filter.add-filter monmodule MonFiltre modules_catalog/catalog param1,param2,param3

On obtient alors la sortie suivante dans la console indiquant ce qui a été fait :

== AddFilter ==
Filter class path: /.../modules/monmodule/persistentdocument/filters/MonFiltreFilter.php
Filter locales in m.monmodule.bo.documentfilters: monfiltrefilter-label and monfiltrefilter-text

Exemples d'implémentations de filtres

Filtre sur les propriété d'un sous-document

class customer_BillingAddressFilter extends f_persistentdocument_DocumentFilterImpl
{
	public function __construct()
	{
		$amountParameter = f_persistentdocument_DocumentFilterRestrictionParameter::getNewInstance();
		$amountParameter->setAllowedPropertyNames(array(
			'modules_customer/billingaddress.creationdate',
			'modules_customer/billingaddress.title',
			'modules_customer/billingaddress.firstname',
			'modules_customer/billingaddress.lastname',
			'modules_customer/billingaddress.email',
			'modules_customer/billingaddress.company',
			'modules_customer/billingaddress.addressLine1',
			'modules_customer/billingaddress.addressLine2',
			'modules_customer/billingaddress.addressLine3',
			'modules_customer/billingaddress.zipCode',
			'modules_customer/billingaddress.city',
			'modules_customer/billingaddress.province',
			'modules_customer/billingaddress.country',
			'modules_customer/billingaddress.phone',
			'modules_customer/billingaddress.fax',
		));
		$this->setParameters(array('field' => $amountParameter));
	}
	
	/**
	 * @return String
	 */
	public static function getDocumentModelName()
	{
		return 'modules_customer/customer';
	}

	/**
	 * @return f_persistentdocument_criteria_Query
	 */
	public function getQuery()
	{
		$query = customer_CustomerService::getInstance()->createQuery();
		$query->createCriteria('billingAddress')->add($this->getParameter('field')->getValueForQuery());
		return $query;
	}
}

Filtre sur le résultat d'une projection

class customer_UsedCouponCountFilter extends f_persistentdocument_DocumentFilterImpl
{
	public function __construct()
	{
		$parameters = array();

		$info = new BeanPropertyInfoImpl('count', 'Integer');
		$parameter = f_persistentdocument_DocumentFilterRestrictionParameter::getNewHavingInstance($info);
		$parameters['count'] = $parameter;
		
		$this->setParameters($parameters);
	}

	/**
	 * @return String
	 */
	public static function getDocumentModelName()
	{
		return 'modules_customer/customer';
	}

	/**
	 * @return f_persistentdocument_criteria_Query
	 */
	public function getQuery()
	{
		$query = customer_CustomerService::getInstance()->createQuery()->setFetchColumn('this');
		$query->createCriteria('usedCoupon')->setProjection(Projections::rowCount('count'));
		$query->having($this->getParameter('count')->getValueForQuery());
		return $query;
	}
}

Filtre construit à partir de valeurs

class customer_LoginFilter extends f_persistentdocument_DocumentFilterImpl
{
	public function __construct()
	{
		$parameters = array();
		
		$info = new BeanPropertyInfoImpl('mode', 'String');
		$info->setLabelKey('mode de recherche');
		$info->setListId('modules_filter/matchmodes');
		$parameter = new f_persistentdocument_DocumentFilterValueParameter($info);
		$parameters['mode'] = $parameter;
		
		$info = new BeanPropertyInfoImpl('code', 'String');
		$info->setLabelKey('chaine recherchée');
		$parameter = new f_persistentdocument_DocumentFilterValueParameter($info);
		$parameters['code'] = $parameter;
		
		$this->setParameters($parameters);
	}

	/**
	 * @return String
	 */
	public static function getDocumentModelName()
	{
		return 'modules_customer/customer';
	}

	/**
	 * @return f_persistentdocument_criteria_Query
	 */
	public function getQuery()
	{
		$dfs = f_persistentdocument_DocumentFilterService::getInstance();
		$mode = $dfs->getMatchMode($this->getParameter('mode')->getValueForQuery());
		$query = customer_CustomerService::getInstance()->createQuery();
		$query->createCriteria('user')->add(Restrictions::like('login', $this->getParameter('code')->getValueForQuery(), $mode, true));
		return $query;
	}
}

Filtre implémentant à la fois ''getQuery()'' et ''checkValue()''

class order_ShopOrderFilter extends f_persistentdocument_DocumentFilterImpl
{
	public function __construct()
	{
		$parameters = array();
		
		$info = new BeanPropertyInfoImpl('shop', 'modules_catalog/shop');
		$shopParameter = new f_persistentdocument_DocumentFilterValueParameter($info);
		$parameters['shop'] = $shopParameter;
		$this->setParameters($parameters);
	}
	
	/**
	 * @return String
	 */
	public static function getDocumentModelName()
	{
		return 'modules_order/order';
	}

	/**
	 * @return f_persistentdocument_criteria_Query
	 */
	public function getQuery()
	{
		$shopIds =  DocumentHelper::getIdArrayFromDocumentArray($this->getParameter('shop')->getValueForQuery());
		return order_OrderService::getInstance()->createQuery()->add(Restrictions::in('shopId', $shopIds));
	}
	
	/**
	 * @param customer_persistentdocument_customer $value
	 * @return boolean
	 */
	public function checkValue($value)
	{
		if ($value instanceof order_persistentdocument_order)
		{
			$shopIds =  DocumentHelper::getIdArrayFromDocumentArray($this->getParameter('shop')->getValueForQuery());
			return in_array($value->getShopId(), $shopIds);
		}
		return false;
	}
}

Autres exemples

D'autres exemples sont disponibles dans les modules comptes clients (customer) ou commandes (order).

Exploiter un champ de type ''query''

Ce qui est stocké en base dans la propriété de type query est une représentation JSON de l'ensemble des filtres sélectionnés. Pour exploiter cet ensemble de filtre, on fait appel au service f_persistentdocument_DocumentFilterService qui dispose des méthodes :

  • checkValueFromJson($json, $value) : renvoie un booléen disant si $value satisfait l'ensemble de filtres décrits par $json
  • getQueryIntersectionFromJson($json) : renvoie un instance de f_persistentdocument_criteria_QueryIntersection ou f_persistentdocument_criteria_QueryUnion qui sont respectivement une intersection ou une union de plusieurs query. On ne peut pas ajouter de restriction ou appliquer des tris ou des projections à cette instance, comme on peut le faire sur une query. Tout ce que l'on peut faire c'est :
    • ajouter une autre query (méthode add($query)) : pour ajouter un filtre à cette intersection. Attention, cette query doit impérativement porter sur un modèle compatible avec celui de l'ensemble des query déjà incluses dans l'intersection.
    • récupérer les documents (méthode find()) : récupère les instances des documents répondant aux critères de l'ensemble des query de l'intersection.
    • récupérer les ids des documents (méthode findIds()) : récupère les identifiants des documents répondant aux critères de l'ensemble des query de l'intersection.

Exemple de validation d'un document :

	/**
	 * @param customer_persistentdocument_dynamiccustomergroup $group
	 * @param customer_persistentdocument_customer $customer
	 * @return boolean
	 */
	protected function doIsMember($group, $customer)
	{
		return f_persistentdocument_DocumentFilterService::getInstance()->checkValueFromJson($group->getQuery(), $customer);
	}

Exemple de requêtage :

	/**
	 * @param customer_persistentdocument_dynamiccustomergroup
	 * @return customer_persistentdocument_customer[]
	 */
	protected function doGetMembers($group)
	{
		$queryIntersection = f_persistentdocument_DocumentFilterService::getInstance()->getQueryIntersectionFromJson($group->getQuery());
		return $queryIntersection->find();
	}
		
	/**
	 * @param customer_persistentdocument_dynamiccustomergroup
	 * @return integer[]
	 */
	protected function doGetMemberIds($group)
	{
		$queryIntersection = f_persistentdocument_DocumentFilterService::getInstance()->getQueryIntersectionFromJson($group->getQuery());
		return $queryIntersection->findIds();
	}

Le document "modules_filter/queryfolder"

Le module filter propose un document de base pur implémenter des “dossiers intelligents”, c'est à dire des dossiers dont le contenu est dynamique et requêté selon un ensemble de filtres.

Pour réaliser un tel dossier dans un module il faut :

  • créer un modèle de document étendant modules_filter/queryfolder

Exemple :

<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://www.rbs.fr/schema/change-document/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.rbs.fr/schema/change-document/1.0 http://www.rbschange.fr/static/schema/change-document/3.5.xsd"
	model-version="3.5" extend="modules_filter/queryfolder">
</document>

  • dans les panneaux create et properties de son éditeur, spécifier l'attribut allow pour votre modèle de document et les méthodes requises sur les filtres

Exemple :

<?xml version="1.0" encoding="UTF-8"?>
<panel>
	<section labeli18n="modules.uixul.bo.doceditor.fieldsgroup.Title">
		<field name="label" />
		<field name="description" />
		<field name="query" type="query" allow="catalog/product::getQuery" />
	</section>
</panel>

  • dans la perspective, au niveau du noeud child il faut ajouter l'attribut from

	<child model="modules_catalog/simpleproduct" from="function" />

Filtre ne portant pas sur un document

Il est possible de faire des filtres ne portant par sur un document (par exemple sur un panier). Ceci fonctionne globalement de la même manière, avec les spécificités et restrictions suivantes :

  • on ne peut faire de query que sur un document, implémenter la méthode getQuery n'a donc pas de sens dans ce cas

La mise en œuvre a aussi quelques spécificités :

  • dans les éditeurs de documents, on déclare des propriétés sur ce type de filtres avec le type objectfilter au lieu de query

Exemple :

<field name="query" type="objectfilter" allow="order/cart::checkValue" />

  • la méthode getDocumentModelName du filtre

Exemple :

	/**
	 * @return string
	 */
	public static function getDocumentModelName()
	{
		return 'order/cart';
	}

ref/backoffice/filters.txt · Dernière modification: 2017/01/19 14:54 (modification externe)