zmiana wiersza w gridView po kliknięciu przycisku

Cześć, chcę zrobić coś takiego, aby po kliknięciu przycisku dany wiersz w gridView przemieścił się w górę lub w dół. Postanowiłem więc zrobić w bazie dodatkową kolumnę, gdzie będę trzymał wartości, po których będę sortował. Kolumnę nazwałem ‘sortOrder’. Tak więc wziąłem się za pisanie funkcji, która będzie mi dodawała lub odejmowała wartości do tej kolumny:


 public function actionSortUp($id){

        $model = $this->findModel($id);

        $model->sortOrder+=1;

        $model->save();


        return $this->redirect('index.php?r=categories%2Findex');

    }

i chciałem dodać w gridView przycisk gdzie wywołuję tę funkcję:


'buttons' => [

    'my_button' => function ($url, $model, $key) {

        return Html::a('up', ['sort-up', 'id'=>$model->id]);

    },

]

Po $model->save() musisz znaleźć wszystkie inne modele z takim samym sortOrder i zmniejszyć im go o 1.

Poza tym twój algorytm jest kulawy bo nieprawidłowo obsługuje luki w numeracji. Jeśli masz modele o kolejności:

id => sortOrder

1 => 7,

2 => 6,

3 => 5,

4 => 2,

5 => 2,

6 => 2,

7 => 1,

to przesunięcie modelu o id 3 w dół nie zmieni kolejności. Nie powinieneś na sztywno zmieniać wartości o 1, tylko o różnicę pomiędzy elementami które chcesz zamienić (w tej sytuacji musisz id:3 przesunąć o -3 a wszystkie modele z sortOrder = 2 o +3).

Ale nawet wtedy możesz mieć problemy przesuwając elementy które mają sortOrder taki sam jak inne modele. Np przesuwając id:4 w górę możesz uzyskać kolejność:

1 => 7,

2 => 6,

4 => 5,

5 => 2,

6 => 2,

3 => 2,

7 => 1,

(id:3 przeskoczyła o 3 miejsca w dół zamiast o 1). Musisz mieć jakiś mechanizm, który będzie pilnował, żeby te same pozycje się nie duplikowały dla różnych modeli.

Korzystam w tej chwili z takiego rozwiązania (podaje w uproszczeniu):

W modelu atrybut int ‘order’ przetrzymuje kolejność sortowania.

W kontrolerze akcja do zmiany w górę:




    public function actionMoveUp($id)

    {

        $model = Model::findOne($id);

        

        $previous = model::find()->andWhere(['<', 'order', $model->order])->orderBy(['order' => SORT_DESC])->limit(1)->one();

        if (empty($previous)) {

            // info, ze model juz jest na gorze listy

        } else {

            if ($model->switchOrderWith($previous)) {

                // info, ze kolejnosc zostala zmieniona

            } else {

                // blad

            }

        }

        

        return $this->redirect(/* powrot */);

    }



Akcja do zmiany w dół:




    public function actionMoveDown($id)

    {

        $model = Model::findOne($id);

        

        $next = Model::find()->andWhere(['>', 'order', $model->order])->orderBy(['order' => SORT_ASC])->limit(1)->one();

        if (empty($next)) {

            // info, ze model juz jest na dole listy

        } else {

            if ($model->switchOrderWith($next)) {

                // info, ze kolejnosc zostala zmieniona

            } else {

                // blad

            }

        }

        

        return $this->redirect(/* powrot */);

    }



Metoda do zamiany kolejności w modelu:




    public function switchOrderWith(Model $model)

    {

        $transaction = Yii::$app->db->beginTransaction();

        try {

            $changed_order = $this->order;

            $this->order   = $model->order;

            $model->order  = $changed_order;

            if (!$this->save() || !$model->save()) {

                throw new Exception(/* blad zapisu */);

            }

            $transaction->commit();

            return true;

        } catch (Exception $e) {

            $transaction->rollBack();

            Yii::error($e->getMessage());

        }

        return false;

    }



Jak widać, pozwalam na taką samą wartość order i nie przejmuję się zachowaniem ciągłości kolejnych wartości.

pytanko, co masz wpisany w kolumnie order? Czy są to odpowiednikk Id modelu? I co tam wpisujesz przy utworzeniu modelu. Aktualnie coś ruszyło ale trochę to działa u mnie jak chce a właściwie to zmienia mi sie tylko o 1 do gory i gdy znow klikne w gore to wraca z powrotem a w sort order mam int i liczby po kolei od 1 do 6

Przy tworzeniu modelu w ‘order’ możesz podać dowolną wartość typu int, domyślnie może być 0 albo pobrane z bazy “największa wartość order + 1”.

W gridzie sortuj sobie po tym sortOrder właśnie.

Poczytaj sobie o sortowaniu w ActiveDataProvider.

Dzięki za pomoc :)

Jest jeszcze taka opcja, że gdy usunie się dany model to sortowanie w tej luce przestaje działać np mamy model 1,2,3 i usuniemy 2 to nie da się przejść z 1 do 3.

Nie wiem jak to zrobiłeś dokładnie, ale oryginalna funkcjonalność działa wg schematu:

Sort po order ASC:

$model1 => order1

$model2 => order2

Przeniesienie w górę $model2:

$model1->order = order2

$model2->order = order1

Czyli po prostu zamiana wartości order dla dwóch kolejnych modeli. Nie ma znaczenia czy $model1 ma order = 1, a $model2 ma order = 378.