Bulk operations

Ce inseamna

Inseamna posibilitatea de a selecta mai multe item-uri dintr-un listing si de a face chestii pe ele. De exemplu posibilitatea de a selecta mai multe facturi si de a downloada PDF-urile ca zip. Sau de a le sterge.

Exista doua moduri de a specifica pe care item-uri se executa actiunea:

  1. bifand check-box-urile de pe linii : caz in care actiunea se aplica numai pe item-urile (in acest exemplu pe facturile) check-uite
  2. ne bifand check-box-urile de pe linii : caz in care actiunea se aplica pe toate item-urile din listing, din toate paginile (adica conform cautarii curente)

Implementare

In index.php

In index.php -ul in care avem nevoie de aceasta functionalitate adaugam urmatoarea coloana (exemplu din facturi) :

['class' => 'common\components\CfoCheckboxColumn',
'options' => ['class' => 'width-1p'],
'filterOptions' => ['class' => 'empty'],
'headerOptions' => ['class' => 'check-line'],
'contentOptions' => ['class' => 'check-line'],
'menuActions' => [
Html::a('<i class="icon fa-send color-primary"></i> ' . Yii::t('app', 'Send by email'), ['invoice/bulk-send-mail'], [
'data-action-confirm' => Yii::t("app", "Are you sure you want to send by mail {xxx} documents?")]),
Html::a('<i class="max-file-pdf color-primary"></i> ' . Yii::t('app', 'Download PDFs'), ['invoice/bulk-view'], [
'data-action-confirm' => Yii::t("app", "Are you sure you want to download the PDFs for {xxx} documents?"),
'data-override-href' => 1,
'data-item-limit' => 100,
]),
CfoCheckboxColumn::MENUITEM_SEPARATOR,
Html::a('<i class="icon md-delete color-danger"></i> ' . Yii::t('app', 'Delete'), ['invoice/bulk-delete'], [
'data-action-confirm' => Yii::t("app", "Are you sure you want to delete {xxx} documents?"),
'data-action-only-with-checkbox' => 1]),
],
'specificItemsMessage' => Yii::t("app", "You have to specifically select invoices for this bulk operation!"),
'moreThanOneItemMessage' => Yii::t("app", "Please select at least two invoices for bulk operation!"),
'moreThanXItemsMessage' => \Yii::t("app","This operation can be performed on a maximum number if {xxx} invoices!"),
]

Remarcam urmatoarele:

  1. coloana este de tip CfoCheckboxColumn
  2. coloana are menuActions : array de bucati HTML care vor fi inserate in meniul de bulk actions; in cazul de fata, link-uri
  3. cand vrem sa introducem separator, folosim CfoCheckboxColumn::MENUITEM_SEPARATOR
  4. daca actiunea respectiva are nevoie de mesaj de confirmare, il intorducem ca data-action-confirm
  5. daca actiunea respectiva trebuie sa poata fi executata NUMAI cand selectam IN CLAR item-urile atunci introducem data-action-only-with-checkbox=1 (de exemplu pentru DELETE, vrem sa permitem user-ului sa stearga numai item-urile pe care le-a bifat)
  6. daca actiunea poate avea loc numai pentru un numar maxim de item-uri, atunci putem seta acest numar maxim ca data-item-limit
  7. daca actiunea trebuie executata prin window.top.location atunci se seteaza data-override-href pe 1; ne ajuta sa evitam executarea prin ajax (pentru ca meniul nostru i.e. link-urile pot fi in PJAX…)
  8. poti seta specificItemsMessage cu mesajul care va fi afisat daca exista o operatiune ce necesita selectarea specifica a item-urilor din checkbox-uri (eg. pentru Delete)
  9. poti seta moreThanOneItemMessage cu mesajul care va fi afisat daca user-ul a selectat doar un item
  10. poti seta moreThanXItemsMessage cu mesajul care va fi afisat atunci cand operatiunea are o limita de item-uri si aceasta limita este depasita
  11. la runtime, tag-ul {xxx} se inlocuieste cu numarul real de intem-uri pe care se va aplica actiunea
  12. nu uitati sa puneti in link-uri toti parametrii de care aveti nevoie in action; de exemplu la invoice, trebuie sa nu uitam sa adaugam records

 

In controller, in actionBulkXxxx()

Ei bine, ideea generala este aceea ca trebuie sa putem face actiuni bulk (mai putin delete 🙂 ) si cand user-ul a selectat linii, si cand nu a selectat. Avem deci doua scenarii:

  1. cand user-ul a selectat linii: inseamna ca trebuie sa executam actiunea numai pentru liniile selectate
  2. cand user-ul nu a selectat nici o linie: inseamna ca trebuie sa executam actiunea pentru toate liniile, conform cu criteriile de cautare din actionIndex.

Scenariul #1 este clar – in acest caz ne vin ID-urile pe $_REQUEST[‘theIds’] , concatenate cu virgula, si pentru ele facem operatiunea.

Pentru scenariul #2 este insa mai complicat putin. Trebuie sa selectam liniile exact cum se face in actionIndex, si sa avem grija ca toti parametri de filtru sa fie trimisi si in actionBulkXxxx()-ul nostru.

Mai jos este exemplul din invoice pentru download-ul PDF-urilor in bulk. Am omis partea efectiva de generare si arhivare a PDF-urilor pentru a evidentia clar mecanismul celor doua scenarii. Ar trebuie sa se clarifice ceea ce zic destul de mult:

public function actionBulkView() {

  // trying to discern the intended items for the operation
  $theIds = trim(isset($_REQUEST['theIds']) ?trim($_REQUEST['theIds']) :"");
  $specificIds = $theIds != "" ?explode(",", $theIds) :array();

  if (is_array($specificIds) && count($specificIds)) {
    // checkboxes were checked
    $items = Invoice::find()->where(["in", "id", $specificIds])->all();
  }
  else {
    // no checkbox was cheched, so this operation will be done
    // on all items according to the current search criteria
    // We do this by replicating the db read from actionIndex() :
    if (($records = $this->getRecordsRequest()) === FALSE) {
      return $this->redirect(['index', 'records' => $this->getPreviousRecordsVisited()]);
    }
    $searchModel = new InvoiceSearch();
    $searchModel->companyId = Company::currentId();
    $searchModel->records = Invoice::getRecordsId($records);
    if (strlen(@$_REQUEST['InvoiceSearch']['total']) > 0) {
      @$_REQUEST['InvoiceSearch']['total'] = (int)$_REQUEST['InvoiceSearch']['total'];
    }
    $dataProvider = $searchModel->search($_REQUEST);
    $dataProvider->setPagination(false);
    $items = $dataProvider->getModels();
  }

  // at this point we should have the correct items in $items,
  // and so we can do what we need to, with them, like generating
  // and archiving the PDFs :)
  if (is_array($items) && count($items)) {
    foreach ($items as $item) {

    }
  }
}

Remarcam urmatoarele:

  1. facem disable la paginare ca sa fim siguri ca ne sunt intoarse toate liniile : $dataProvider->setPagination(false);

In cazul in care trebuie sa putem face actiunea bulk numai pentru linii clar selectate de user, treaba este mai simpla. Trebuie doar sa nu uiti sa validezi $theIds! Mai jos exemplul de la bulk delete (din nou, am omis partea efectiva de delete pentru a evidentia mecanismul de selectie si validare) .

public function actionBulkDelete() {

  // trying to discern the intended items for the operation
  $theIds = trim(isset($_REQUEST['theIds']) ?trim($_REQUEST['theIds']) :"");
  $specificIds = $theIds != "" ?explode(",", $theIds) :array();

  if (is_array($specificIds) && count($specificIds)) {
    // checkboxes were checked
    $items = Invoice::find()->where(["in", "id", $specificIds])->all();
    // and then we delete $items ...
    
  }
}

 

Nota: pentru ultima versiune de cod cauta in invoice (controller si respectiv /index.php). 

 

 

About

Software Development Manager, Architect

Leave a Reply

Your email address will not be published. Required fields are marked *

[TOP]