-
Notifications
You must be signed in to change notification settings - Fork 4
git version of the sfTaskExtraPlugin version repository to be used as a git module in my projects
License
aalbagarcia/sfDependentSelectPlugin
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Este repositorio es un espejo del plugin sfDependentSelectPlugin que puede encontrarse en los repositorios de plugins de symfony: http://www.symfony-project.org/plugins/sfDependentSelectPlugin sfDependentSelectPlugin ======================= Un plugin para enlazar selects de una forma simple y elegante $this->widgetSchema['pais_id'] = new sfWidgetFormDoctrineChoice(array( 'model' => 'Pais', )); $this->widgetSchema['provincia_id'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Provincia', 'depends' => 'Pais', )); Características =============== * Modo estático y modo AJAX con módulo automático (respetando add\_empty, {peer|table}\_method, order\_by, etc), * Permite enlazar listas de array con listas de modelos o entre modelos (niveles ilimitados) y también que dos selects dependan de uno, * Soporte para Doctrine y Propel, * Fácil de personalizar y extender. Instalación =========== * Instalar el plugin $ php symfony plugin:install sfDependentSelectPlugin * Publicar javascript $ php symfony plugin:publish-assets * Limpiar la cache $ php symfony cache:clear * Habilitar el plugin en la configuración del proyecto _(config/ProjectConfiguration.class.php)_ $this->enablePlugins(..., 'sfDependentSelectPlugin'); * Habilitar el módulo *sfDependentSelectAuto* si deseas utilizar ajax automatizado _(apps/{tuapp}/config/settings.yml)_ all: .settings: ... enabled_modules: [sfDependentSelectAuto] Dependencias ============ * _jQuery:_ solamente si utilizas AJAX. Debes agregarla tú en caso necesites esta funcionalidad. **Importante**: La versión de jQuery debe ser igual o mayor a 1.4. Contenido ========= Widgets ------- * _sfWidgetFormArrayDependentSelect:_ despliega un select utilizando como fuente de datos un array, * _sfWidgetFormDoctrineDependentSelect:_ utiliza Doctrine como fuente de datos, * _sfWidgetFormPropelDependentSelect:_ utiliza Propel como fuente de datos. Actions ------- * _sfActionsDependentSelect:_ implementa una acción "_ajax" que devuelve los valores necesarios para cada petición. Modules ------- * _sfDependentSelectAuto:_ es el módulo utilizado por defecto por los widgets, simplemente extiende a _sfActionsDependentSelect_. Ejemplos de uso =============== Los ejemplos son para Doctrine, pero puedes hacerlo de igual forma para propel utilizando la clase _sfWidgetFormPropelDependentSelect_. Pais, provincia y ciudad ------------------------ Típico caso en que se tiene paises, provincias y ciudades relacionadas. Al momento de crear una dependencia a la ciudad desde otra entidad, se crea un gran problema a la hora de mostrar el formulario, ya que aparecen todas las ciudades de todas las provincias de todos los paises mezclados. A continuación, la solución. Se hará con una persona que vive en una ciudad. * _config/doctrine/schema.yml_: Pais: columns: nombre: string(45) Provincia: columns: pais_id: integer nombre: string(45) relations: Pais: foreignAlias: Provincias Ciudad: columns: provincia_id: integer nombre: string(45) relations: Provincia: foreignAlias: Ciudades Persona: columns: ciudad_id: integer nombre: string(45) relations: Ciudad: foreignAlias: Personas * _lib/form/doctrine/PersonaForm.class.php_: class PersonaForm extends BasePersonaForm { public function configure() { // widgets $this->widgetSchema['pais_id'] = new sfWidgetFormDoctrineChoice(array( 'model' => 'Pais', 'add_empty' => 'Seleccione país', )); $this->widgetSchema['provincia_id'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Provincia', 'depends' => 'Pais', 'add_empty' => 'Seleccione provincia', )); $this->widgetSchema['ciudad_id'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Ciudad', 'depends' => 'Provincia', 'add_empty' => 'Seleccione ciudad', )); // siempre el orden de los selects tienen que ser según la dependencia. // es decir, primero pais > provincia > ciudad $this->widgetSchema->moveField('ciudad_id', 'after', 'provincia_id'); // validadores $this->validatorSchema['pais_id'] = new sfValidatorDoctrineChoice(array( 'model' => 'Pais', )); $this->validatorSchema['provincia_id'] = new sfValidatorDoctrineChoice(array( 'model' => 'Provincia', )); // el validador de 'ciudad_id' está en BasePersonaForm } } Listo, con eso ya debería funcionar. Se mostrará el select "independiente" para paises, y de acuerdo al valor de éste serán las provincias mostradas. De igual forma para con las ciudades según la provincia. Cabe destacar que el ejemplo anterior no hace llamadas remotas (AJAX), simplemente carga los selects con los diferentes grupos de datos y los va mostrando de acuerdo a los valores seleccionados en el select del cual depende. Por eso notarás la rapidez en el funcionamiento, ideal para pequeños/medianos conjuntos de datos. Cuando los niveles son profundos y con grandes cantidades de opciones, seguramente querrás utilizar AJAX. A continuación veremos cómo modificar el ejemplo anterior para añadir esta característica facilmente. ### Ahora con AJAX **Importante:** Recuerda cargar jQuery sino no se harán las llamadas remotas. * _lib/form/doctrine/PersonaForm.class.php_: class PersonaForm extends BasePersonaForm { public function configure() { // widgets $this->widgetSchema['pais_id'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Pais', 'add_empty' => 'Seleccione país', 'ajax' => true, )); // si hubieses querido cargarlo sin ajax (ya que son pocos datos): #$this->widgetSchema['pais_id'] = new sfWidgetFormDoctrineChoice(array( # 'model' => 'Pais', # 'add_empty' => 'Seleccione pais', #)); $this->widgetSchema['provincia_id'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Provincia', 'depends' => 'Pais', 'add_empty' => 'Seleccione provincia', 'ajax' => true, // aprovechamos para mostrar otras opciones #'ref_method' => 'getPaisId', #'url' => sfContext::getInstance()->getController()->genUrl('sfDependentSelectAuto/_ajax'), #'cache' => true, // están disponibles las mismas que sfWidgetForm{Doctrine|Propel}Choice 'order_by' => array('nombre', 'asc'), #'method' => '__toString', #'key_method' => 'getId', #'table_method' => 'getPaisesEuropeos', )); $this->widgetSchema['ciudad_id'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Ciudad', 'depends' => 'Provincia', 'add_empty' => 'Seleccione ciudad', 'ajax' => true, 'order_by' => array('nombre', 'asc'), )); ... } } Eso es todo. Las llamadas remotas son atendidas por un módulo especial llamado _sfDependentSelectAuto_ que devuelve los valores respetando todas las opciones establecidas como si de forma local se tratase. Más adelante se explican detalladamente las opciones disponibles y cómo funciona el módulo automático para que lo puedas extender y devolver tus propios datos. Antes de eso, otro ejemplo. Productos y servicios --------------------- Se utilizará como ejemplo un caso de ventas de productos y servicios que extienden a un ítem. Al momento de cargar el detalle de la factura, se selecciona el tipo (producto o servicio) y luego se despliegan los mismos en otro select. Este ejemplo muestra cómo mezclar arrays con modelos. * _config/doctrine/schema.yml_: Item: columns: nombre: string(45) precio: float tipo: string(45) Producto: inheritance: extends: Item type: column_aggregation keyField: tipo keyValue: producto Servicio: inheritance: extends: Item type: column_aggregation keyField: tipo keyValue: servicio Factura: columns: numero: integer fecha: date FacturaConcepto: columns: factura_id: integer item_id: integer cantidad: float relations: Factura: foreignAlias: Conceptos Item: foreignAlias: Conceptos * _lib/model/doctrine/Item.class.php_ class Item extends BaseItem { private static $tipos = array( 'producto' => 'Productos', 'servicio' => 'Servicios', ); public static function getTipos() { return self::$tipos; } } * _lib/form/doctrine/FacturaConceptoForm.class.php_ class FacturaConceptoForm extends BaseFacturaConceptoForm { public function configure() { // widgets $this->widgetSchema['tipo'] = new sfWidgetFormChoice(array( 'choices' => Item::getTipos(), )); // si quieres cargar los tipos con ajax, podrías haber utilizado // la clase sfWidgetFormArrayDependentSelect que se explica luego $this->widgetSchema['item_id'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Item', 'depends' => 'tipo', 'ajax' => true, ), array( 'size' => 5, )); // validadores $this->validatorSchema['tipo'] = new sfValidatorChoice(array( 'choices' => Item::getTipos(), )); } } Aparecerá un select en donde seleccionas el tipo, luego de seleccionarlo se carga la lista de items de ese tipo. Puedes combinar arrays y modelos libremente. Utilizando sólo arrays ---------------------- Veamos el ejemplo de paises y provincias pero esta vez utilizando un array como fuente de datos. Para ello se utilizará el widget _sfWidgetFormArrayDependentSelect_. A los datos los pondremos en una clase contenedora. * _lib/MisDatos.class.php:_ abstract class MisDatos { private static $paises = array( 'ar' => 'Argentina', 'br' => 'Brasil', ); private static $provincias = array( 'ar' => array( 'ar_bue' => 'Buenos Aires', 'ar_cba' => 'Córdoba', ), 'br' => array( 'br_sp' => 'São Paulo', 'br_mg' => 'Minas Gerais', ), ); public static function getPaises() { return self::$paises; } public static function getProvincias() { return self::$provincias; } } * _lib/form/doctrine/PersonaForm.class.php:_ class PersonaForm extends BasePersonaForm { public function configure() { // widgets $this->widgetSchema['pais'] = new sfWidgetFormArrayDependentSelect(array( 'callable' => array('MisDatos', 'getPaises'), )); $this->widgetSchema['provincia'] = new sfWidgetFormArrayDependentSelect(array( 'callable' => array('MisDatos', 'getProvincias'), 'depends' => 'pais', )); // validadores $this->validatorSchema['pais'] = new sfValidatorChoice(array( 'choices' => MisDatos::getPaises(), )); $this->validatorSchema['provincia'] = new sfValidatorChoice(array( 'choices' => MisDatos::getProvincias(), )); } } También funciona hablitando ajax. Referencia de opciones ====================== Por widget, en negritas están las requeridas. El símbolo ">" significa que ese widget extiende al otro. sfWidgetFormDependentSelect --------------------------- * _**depends** = null:_ ¿De qué otro select se depende? Puede ser el nombre del campo o bien del modelo. El widget se encargará de determinar los detalles de acuerdo a este valor, * _add\_empty = true:_ Especifica si agrega una opción en blanco. Puede ser true, false o el texto que quieras que aperzca, * _ajax = false:_ Especifica si se utilizan llamadas remotas para rellenar los valores del select, * _cache = true:_ Especifica si se utilizará la caché de valores de un select una vez que ya fué cargado remotamente (para evitar peticiones extras), * _url = sfDependentSelectAuto/\_ajax:_ URL dónde se harás las llamadas remotas para cargar los selects. El valor no debe ser del tipo "modulo/accion" ni nombre de ruta (puede utilizar el método genUrl de sfWebController para genrarla), * _params = array():_ Parámetros adicionales que desea enviar a la llamada remota. * _source\_class = null:_ La clase que se utilizará como orígen de datos. (Ver más adelante en personalizando). * _source\_params = array():_ Parámetros enviados al orígen de datos. * _force\_group = null:_ Indica el grupo de valores que debe mostrarse por defecto al cargar la página. sfWidgetFormArrayDependentSelect > sfWidgetFormDependentSelect -------------------------------------------------------------- * _**callable** = null:_ Función o método que se llamará para solicitar el array. En caso de ser un método estático, indicar: array('Clase', 'metodo'). No utilices instancias de clases como $this, ya que no funcionará en las llamadas remotas. * _callable\_params = null:_ Parametros que se le pasarán a la función o método indicado en la opción "callable". * heredadas: **depends, add_empty, ajax, cache, url, params** sfWidgetFormObjectDependentSelect > sfWidgetFormDependentSelect --------------------------------------------------------------- * _**model** = null:_ Modelo que se listará en este select * _method = __toString:_ Método para el texto cada opción * _key\_method = getId:_ Método para el valor de cada opción * _ref\_method = null:_ Método de la relación con 'depends' * _order\_by = null:_ Órden del lista, del tipo array('columna', {'asc'|'desc'}) * heredadas: **depends, add_empty, ajax, cache, url, params** sfWidgetFormDoctrineDependentSelect > sfWidgetFormObjectDependentSelect ----------------------------------------------------------------------- * _table\_method = null:_ Método que se llamará en la clase Table del objeto para devolver los valores a mostrar. * heredadas: **depends, add_empty, ajax, cache, url, params, model, method, key_method, ref_method, order_by** sfWidgetFormPropelDependentSelect > sfWidgetFormObjectDependentSelect --------------------------------------------------------------------- * _peer\_method = doSelect:_ Método que se llamará en la clase PEER del objeto para devolver los valores a mostrar. * heredadas: **depends, add_empty, ajax, cache, url, params, model, method, key_method, ref_method, order_by** Personalizando ============== En el caso que desees tener el control de los datos en las llamadas tanto locales como remotas puedes hacerlo de varias formas. Utilizando la opción '{table|peer}\_method' ------------------------------------------- Si trabajas con un ORM, lo más fácil será especificar la opción '{table|peer}\_method' en el widget y luego implementarla en la clase correspondiente. Veamos un ejemplo para devolver sólo aquellas provincias que sean productoras. * _lib/form/doctrine/PersonaForm.class.php:_ class PersonaForm extends BasePersonaForm { public function configure() { ... $this->widgetSchema['provincia'] = new sfWidgetFormDoctrineDependentSelect(array( 'model' => 'Provincia', 'depends' => 'Pais', 'table_method' => 'getProductorasQuery', )); ... } } * _lib/model/doctrine/ProvinciaTable.class:_ class ProvinciaTable extends Doctrine_Table { public function getProductorasQuery($refValue) { return $this->createQuery('p')->where('p.es_productora = true and p.pais_id = ?', $refValue); } } Es importante que sepas que cuando la acción llama al método {table|peer}, se le pasa como parámetro el valor de referencia, para que puedas filtrar tus datos de acuerdo al valor del select del cual depende. Creando tu propio orígen de datos o widget ------------------------------------------ En el caso que quieras utilizar otra fuente de datos (por si no usas un ORM o simplemente un canal RSS), puedes crear facilmente uno personalizado. De esta forma podrás aprovochar la automatización de las llamadas tanto locales como remotas y enlazarlas con otros selects dependientes que utilicen otros orígenes. Veamos cómo hacer para crear tu propio orgien de datos para provincias productoras. * _lib/ProvinciaProductoraSource.class.php:_ class ProvinciaProductoraSource extends sfDependentSelectObjectSource { // como vamos a trabajar con objetos extendemos a sfDependentSelectObjectSource // en tu caso puedes extender directamente a sfDependentSelectSource public function getObjects($fk = null) { return ProvinciaTable::getInstance()->createQuery('p') ->where('p.es_productora = true and p.pais_id = ?', $fk) ->execute(); } public function getObject($pk) { // este método se utiliza para la reconstrucción de la cadena de // selects como se explica más adelante. de momento sólo preocuparnos // en devolver el objeto que corresponde con el parametro $pk return ProvinciaTable::getInstance()->find($pk); } } * _lib/form/doctrine/PersonaForm.class.php:_ class PersonaForm extends BasePersonaForm { public function configure() { ... // nota que cuando utilizas tu propia fuente de datos tienes que // utilizar el widget sfWidgetFormDependentSelect $this->widgetSchema['provincia'] = new sfWidgetFormDependentSelect(array( 'depends' => 'Pais', 'source_class' => 'ProvinciaProductoraSource', // tambien puedes enviarle parametros al source #'source_params'=> array('excepto' => 'Santa Fé'), )); ... } } En este ejemplo hemos extendido a _sfDependentSelectObjectSource_ que facilita el trabajo con objetos, pero si por ejemplo quieres crear una fuente RSS puedes basarte en cómo lo hace _sfWidgetFormArrayDependentSelect_. También puedes crear tu propio widget para que utilice siempre este orígen o bien añadirle otra funcionalidad, revisa las clases sfDependentSelectSource y sfWidgetFormDependentSelect para conocer como crear las tuyas. Extendiendo tu módulo --------------------- Otra forma es extender tu módulo a _sfActionsDependentSelect_ y definir métodos de la forma getValuesFor{name} y getRefValueFor{name}. No olvides especificar tu acción en la opción 'url' del widget. Veamos en el ejemplo de paises, si quisiéramos devolver las provincias de esta forma. * _apps/tuapp/modules/tumodulo/actions/actions.class.php:_ class tuModuloActions extends sfActionsDependentSelect { protected function getValuesForPersonaProvincia($request, $source) { // en este caso trabajamos con objetos, pero puedes hacerlo de cualquier // forma siempre y cuando devuelvas un array con los id como clave // y los textos como valores del array. $valores = array(); $provincias = ProvinciaTable::getInstance()->createQuery('p') ->where('p.pais_id = ? and p.es_productora = true') ->execute($request->getParameter('_ds_ref')); // el parámetro _ds_ref contiene el valor del select del cual depende // en este caso sería el valor de pais.id foreach ($provincias as $provincia) { $valores[$provincia->getId()] = $provincia->getNombre(); } return $valores; } protected function getRefValueForPersonaProvincia($request, $source) { // este método se utiliza para "reconstruir" la cadena de selects // dependientes desde el orden inverso. es decir, si sólo se tiene // el valor de provincia, tenemos que saber de qué pais es para // seleccionarlo en el select de paises. // en este caso el valor de _ds_ref es el valor actual del select // de provincias (provincia.id) y debemos devolver a qué país // corresponde (provincia.pais_id) $provincia = ProvinciaTable::getInstance()->find($request->getParameter('_ds_ref')); return $provincia->getPaisId(); } } Nota que cada método también recibe como parámetro la fuente de datos por si te es necesario. Puedes revisar la clase _sfActionsDependentSelect_ para conocer más sobre cómo extender la funcionalidad en tu módulo. Si no puedes extender tu módulo a la clase antes mencionada y definitivamente ninguna de las formas ofrecidas satisfacen tus necesidades, deberás implementar una acción en tu módulo que contenga toda la funcionalidad como la acción '\_ajax' lo hace en la clase sfActionsDependentSelect. Con Firebug puedes ver los parámetros enviados vía POST. Sobre el código javascript ========================== Por defecto se incluye en el plugin dos versiones del script, normal y minimizado. Siempre se cargará el minimizado a menos que cambies tu configuración: * _apps/tuapp/config/app.yml:_ dev: app: ... sfDependentSelectPlugin: js: normal También puedes establecer 'false' para que no se cargue el script. El valor para mostrarlo minimizado es 'minimized'. ¿Sugerencias? ============= Desgraciadamente no hablo inglés y he elaborado la mayor parte del código tratando de usar terminología en ese idioma con ayuda de traductores automáticos. Pido disculpas por incoherencias encontradas y agradecería me informen para poder corregirlas. Me pueden escribir a mi correo personal por cualquier sugerencia o duda. Con mucho gusto estaré dispuesto a mejorar los aspectos que me indiquen. Agradecimientos =============== A [TRADUCTORES] por ofrecerse tan amablemente a corregir y traducir la documentación y a todos los miembros de la lista de symfony-es por formar esta gran comunidad. TODO ---- * Traducir documentación a inglés * Probar funcionamiento en symfony 1.1 y 1.2
About
git version of the sfTaskExtraPlugin version repository to be used as a git module in my projects
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published