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:
- bifand check-box-urile de pe linii : caz in care actiunea se aplica numai pe item-urile (in acest exemplu pe facturile) check-uite
- 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:
- coloana este de tip CfoCheckboxColumn
- coloana are menuActions : array de bucati HTML care vor fi inserate in meniul de bulk actions; in cazul de fata, link-uri
- cand vrem sa introducem separator, folosim CfoCheckboxColumn::MENUITEM_SEPARATOR
- daca actiunea respectiva are nevoie de mesaj de confirmare, il intorducem ca data-action-confirm
- 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)
- daca actiunea poate avea loc numai pentru un numar maxim de item-uri, atunci putem seta acest numar maxim ca data-item-limit
- 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…)
- 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)
- poti seta moreThanOneItemMessage cu mesajul care va fi afisat daca user-ul a selectat doar un item
- poti seta moreThanXItemsMessage cu mesajul care va fi afisat atunci cand operatiunea are o limita de item-uri si aceasta limita este depasita
- la runtime, tag-ul {xxx} se inlocuieste cu numarul real de intem-uri pe care se va aplica actiunea
- 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:
- cand user-ul a selectat linii: inseamna ca trebuie sa executam actiunea numai pentru liniile selectate
- 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:
- 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).