Aggiornare select Ajax con symfony

Aggiornare select Ajax con symfony

17 febbraio 2012 by Cris

In questo tutorial mostreremo come realizzare, tramite symfony, uno script AJAX per modificare dinamicamente alcune select.

Come esempio, supponiamo che un utente voglia creare un nuovo prodotto, indicando il nome, la categoria e una o più sotto categorie.

Lo schema e i dati

Il database è formato dalle tabelle relative a Product, Category, Subcategory e SubcategoryPerProduct.

# config/doctrine/schema.yml

Product
:
  columns
:
    name
:          { type: string(255), notnull: true }
    category_id
:   { type: integer, notnull: true }
  relations
:
    Category
:      { local: category_id, foreignAlias: Products, onDelete: cascade}
   
Category
:
  columns
:
    name
:          { type: string(255), notnull: true }

Subcategory
:
  columns
:
    name
:          { type: string(255), notnull: true }
    description
:   { type: string(255) }
    category_id
:   { type: integer }
  relations
:
    Category
:      { local: category_id, foreignAlias: Subcategories, onDelete: cascade}

SubcategoryPerProduct
:
  columns
:
    subcategory_id
: { type: integer, notnull: true }
    product_id
:     { type: integer, notnull: true }
  relations
:
    Subcategory
:    { local: subcategory_id, foreignAlias: SubcategoriesPerProduct, onDelete: CASCADE }
    Product
:        { local: product_id, foreignAlias: SubcategoriesPerProduct, onDelete: CASCADE }

Per dare più spessore al nostro esempio, inseriamo anche alcuni dati di test.

# data/fixture/fixture.yml

Category
:
  restaurant
:
    id
:          1
    name
:        Restaurant
  clothing
:
    id
:          2
    name
:        Clothing
   
Subcategory
:
  condition_1
:
    Category
:    clothing
    name
:        Shoes
    description
: Shoes donec sollicitudin, tortor at porttitor sollicitudin.
  condition_2
:
    Category
:    clothing
    name
:        Clothes
    description
: Clothes morbi eu turpis ac sapien auctor consequat.
  condition_3
:
    Category
:    clothing
    name
:        trousers
    description
: Trousers cras purus nulla, venenatis vel sodales vel, viverra nec diam.
  condition_4
:
    Category
:    restaurant
    name
:        pizzeria
    description
: Pizzeria phasellus vel ligula quis odio.
  condition_5
:
    Category
:    restaurant
    name
:        Sandwich shop
    description
: Sandwich shop morbi eu turpis ac sapien auctor consequat.

ProductForm

Analizzando il form relativo a Product, si noterà che non compaiono i checkbox di selezione delle sotto categorie. Per raggiungere l’obiettivo è quindi necessario creare un widget sfWidgetFormDoctrineChoice.

# lib/form/doctrine/ProductForm.class.php

class ProductForm extends BaseProductForm
{
  public function configure()
  {    
    $this->widgetSchema['subcategory'] = new sfWidgetFormDoctrineChoice(array(
        'model' => 'Subcategory',
        'add_empty' => false,
        'expanded' => true,
        'multiple' => true,
        'query' => SubcategoryTable::getInstance()->createQuery()
      ));
   
    $this->validatorSchema['subcategory'] = new sfValidatorDoctrineChoice(array(
      'required' => true,
      'model' => 'Subcategory',
      'multiple' => true,
      'query' => SubcategoryTable::getInstance()->createQuery(),
      ));    
  }
}

In questo modo sono mostrate tutte le sotto categorie. Per mostrare solo quelle relative alla categoria selezionata al momento della creazione del form, aggiungiamo una query in SubcategoryTable e modifichiamo il form di Product.

# lib/model/doctrine/SubcategoryTable.class.php

public function findByCategoryQuery(Category $category)
{
    $q = $this->createQuery()
      ->where('category_id = ?', $category->getId());

    return $q;
}

# lib/form/doctrine/ProductForm.class.php

class ProductForm extends BaseProductForm
{
  public function configure()
  {    
    // Verifica se il prodotto ha già una categoria assegnata
    $category = $this->getObject()->getCategory();
    if (!$category->exists()) {
      $request = sfContext::getInstance()->getRequest();
      $params = $request->getParameter($this->getName());

      if ($params['category_id'])
        $category = CategoryTable::getInstance()->findOneById($params['category_id']);
      else
        $category = CategoryTable::getInstance()->findAll()->getFirst();
    }

    $this->widgetSchema['subcategory'] = new sfWidgetFormDoctrineChoice(array(
        'model' => 'Subcategory',
        'add_empty' => false,
        'expanded' => true,
        'multiple' => true,
        'query' => SubcategoryTable::getInstance()->findByCategoryQuery($category)
      ));
   
    $this->validatorSchema['subcategory'] = new sfValidatorDoctrineChoice(array(
      'required' => true,
      'model' => 'Subcategory',
      'multiple' => true,
      'query' => SubcategoryTable::getInstance()->findByCategoryQuery($category),
      ));
  }

  // Questo metodo serve per salvare le sotto categorie selezionate
  protected function doUpdateObject($values)
  {
    $subcategories = $values['subcategory'];
    unset($values['subcategory']);
    parent::doUpdateObject($values);

    $product = $this->getObject();
    foreach ($subcategories as $key => $subcategoryId) {
      $subcategoryPerProduct = new SubcategoryPerProduct();
      $subcategoryPerProduct->setSubcategoryId($subcategoryId);
      $product->SubcategoriesPerProduct[$key] = $subcategoryPerProduct;
    }
  }
}

Per terminare le modifiche al form di Product, non ci resta che assegnare un attributo class al widget delle categorie e un id al widget delle sotto categorie, che ci serviranno per lo script jQuery che implementeremo a breve.

# lib/form/doctrine/ProductForm.class.php

class ProductForm extends BaseProductForm
{
  public function configure()
  {
      // ...

      $this->getWidget('category_id')->setAttribute('class', 'category');

      $this->widgetSchema['subcategory'] = new sfWidgetFormDoctrineChoice(array(
        'model' => 'Subcategory',
        'add_empty' => false,
        'expanded' => true,
        'multiple' => true,
        'query' => SubcategoryTable::getInstance()->findByCategoryQuery($category),
        'renderer_class' => 'myWidgetFormSelectCheckbox'
      ));

      // ...
  }
}

Per assegnare l’id al widget delle sotto categorie occorre creare una classe che eredita da sfWidgetFormSelectCheckbox e implementare il metodo formatter.

# lib/widget/myWidgetFormSelectCheckbox.class.php

class myWidgetFormSelectCheckbox extends sfWidgetFormSelectCheckbox
{
  public function formatter($widget, $inputs)
  {
    $rows = array();
    foreach ($inputs as $input) {
      $rows[] = $this->renderContentTag('li', $input['input'] . $this->getOption('label_separator') . $input['label']);
    }
    return!$rows ? '' : $this->renderContentTag('ul', implode($this->getOption('separator'), $rows), array('id' => 'subcategory', 'class' => $this->getOption('class')));
  }
}

Creazione del modulo “subcategory”

Questo modulo deve definire una action per recuperare la lista delle sotto categorie, filtrate per categoria, e restituire i risultati su un template JSON. La risposta JSON sarà letta dello script AJAX per aggiornare i checkbox delle sotto categorie.

# apps/frontend/config/routing.yml

list_by_category
:
  url
: /category/:id/subcategory.:sf_format
  param
: { module: subcategory, action: listByCategory }
  requirements
:
    sf_format
: (?:json)
# apps/frontend/modules/subcategory/actions/actions.class.php

class subcategoryActions extends sfActions
{
  public function executeListByCategory(sfWebRequest $request)
  {
    $this->subcategories = SubcategoryTable::getInstance()->findByCategoryId($request->getParameter('id'));
  }
}
# apps/frontend/modules/subcategory/templates/listByCategorySuccess.json.php
{
<?php $count = $subcategories->count(); $i = 0; ?>
<?php foreach($subcategories as $subcategory) : $i++ ?>
  "<?php echo $subcategory->getId()?>": { "title": "<?php echo $subcategory->getName()?>" }<?php echo $i == $count ? '' : ','?>  
<?php endforeach ?>
}

Recuperare le sottocategorie con AJAX e jQuery

Siamo arrivati al l’ultimo punto del nostro tutorial. Non ci rimane che aggiungere uno script, che quando viene scelta una categoria, aggiornerà il checkbox delle sotto categorie utilizzando AJAX.

# apps/frontend/modules/product/templates/newSuccess.php

// ...
<script type="text/javascript">
  $(function() {

    $('.category').change(function(){
      var chosenoption = this.options[this.selectedIndex];
      var conditionUrl = "/category/"+ chosenoption.value +"/subcategory.json";
     
      $.ajax({
        url: conditionUrl,
        dataType: "json",        
        success: function(choices) {
          if(choices) {
            $("#subcategory").hide();
            $("#subcategory").html('');
            var $subcategories = '';
            $.each(choices, function(index) {  
              $subcategories = $subcategories +
                "<li>\n\
                 <input id=\""
+ chosenoption.value + "_" + index + "\" type=\"checkbox\" multiple=\"multiple\" value=\"" + index + "\" name=\"product[subcategory][]\">\n\
                 <label for=\"product_subcategory_"
+ index + "\">" + this.title + "</label>\n\
               </li>"
;
            });
            $("#subcategory").html($subcategories);
            $("#subcategory").show();
          }
        }
      });
    });
  });  
</script>

Link

Symfony
jQuery

Related Posts

jQuery Text Slider Plugin

Portfolio con jQuery

Questo post è stato categorizzato in Software Development, Web Solutions e taggato , , . Segnati il permalink.

Lascia un Commento

+ 8 = quattordici