2017-05-08 4 views
0

Existe-t-il une prise en charge de l'utilisation d'objets de commande abstraits dans les paramètres d'action du contrôleur? Ensuite, selon les paramètres donnés dans une requête JSON, il sélectionnerait l'objet de commande correct?Action de commande abstraite Grails Objet de commande Commande

Par exemple quelque chose comme:

class SomeController { 

    def someAction(BaseCommand cmd){ 
     // cmd could be instance of ChildCommandOne or ChildCommandTwo 
    } 

    class BaseCommand { 
     String paramOne 
    } 

    class ChildCommandOne extends BaseCommand { 
     String paramTwo 
    } 

    class ChildCommandTwo extends BaseCommand { 
     String paramThree 
    } 

} 

A partir de maintenant, je me sers request.JSON pour détecter le passé dans les paramètres et instancier l'objet correct de commande. Est-ce ma seule option pour gérer ce genre de cas?

EDIT:

Pour clarifier le cas d'utilisation ici. J'ai deux modèles de domaine qui partagent le même modèle de domaine de classe de base et je modélise l'héritage dans la base de données en utilisant le modèle table-per-hierarchy par défaut.

Dans mon cas, l'un des modèles de domaine enfant Model A nécessite une chaîne non annulable appelée body, qui est une entrée de texte, tandis que l'autre Model B nécessite une chaîne non annulable appelé directUrl. Ceux-ci représentent les annonces qui peuvent être faites sur la plate-forme. Model A étant une écriture dans l'entrée qui contient le corps de l'annonce tandis que Model B représente un lien vers un site tiers qui contient l'annonce réelle.

Dans ce genre de scénarios, j'ai toujours mis une instruction if dans l'action du contrôleur qui détermine l'objet de commande associé à instancier, mais j'espère une méthode plus propre.

+1

Eh bien, je ne pense pas que c'est une bonne approche, si vous avez 3 commandes différentes, pourquoi ne pas créer u 3 actions différentes? Peut-être que ce serait plus facile si vous expliquiez votre cas d'utilisation. – rgrebski

+0

@rgrebski J'ai mis à jour la question pour contenir mon cas d'utilisation réel. De plus, comment distinguer les 3 actions sans avoir à déclarer formellement des points de terminaison séparés dans mes mappages url? – dvisco

Répondre

0

Cela ne fonctionnera pas de cette façon. Grails a besoin d'une classe concrète (avec le constructeur public par défaut) pour bind demander des paramètres à une instance d'objet de commande. Par conséquent, cette classe doit être définie explicitement comme l'argument de l'action.

+0

Donc, traditionnellement comment quelqu'un gérerait ce cas? Utilisez des points de terminaison distincts pour distinguer les différentes commandes? – dvisco

+0

appelez le 'bindData()' sur une instance spécifique vous-même. c'est simple – injecteer

0

Je suppose que vous devrez appeler la liaison manuellement en fonction de la carte.
Voir RootModel.from (Carte géographique). Dans votre cas Carte serait params du contrôleur

import static com.google.common.base.Preconditions.checkNotNull 

import spock.lang.Specification 
import spock.lang.Unroll 

class CommandHierarchySpec extends Specification { 

    @Unroll 
    def "should create object of type #type for map: #map"() { 
     when: 
      def modelObj = RootModel.from(map) 
     then: 
      modelObj.class == type 
     where: 
      type | map 
      ModelA | [body: 'someBody', test: 'test'] 
      ModelB | [directUrl: 'directUrl', test: 'test'] 
    } 

    def "should throw ISE when map does not contain neither body nor url"() { 
     when: 
      RootModel.from(a: 'b') 
     then: 
      thrown(IllegalStateException) 
    } 
} 


abstract class RootModel { 
    static RootModel from(Map map) { 
     checkNotNull(map, "Parameter map mustn't be null") 

     RootModel rootModel 
     if (map.body) { 
      rootModel = new ModelA() 
     } else if (map.directUrl) { 
      rootModel = new ModelB() 
     } else { 
      throw new IllegalStateException("Cannot determine command type for map: $map") 
     } 

     map.findAll { key, value -> rootModel.hasProperty(key) } 
       .each { 
      rootModel.setProperty(it.key, it.value) 
     } 

     rootModel 
    } 
} 

class ModelA extends RootModel { 
    String body 
} 

class ModelB extends RootModel { 
    String directUrl 
} 
+0

C'est une approche intéressante. Pour en jouer, je pense que je pourrais annoter les objets de commande de domaine qui sont valides et avoir une annotation de discriminateur qui annote la propriété qui le distingue entre les autres commandes. Ensuite, testez jusqu'à ce que je trouve le premier objet de commande qui a une propriété discriminante correspondante dans la carte des paramètres. – dvisco