GridView отсортированный список parent/child

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
stanislav-loe
Сообщения: 1
Зарегистрирован: 2018.10.23, 20:20

GridView отсортированный список parent/child

Сообщение stanislav-loe »

Добрый день. Есть таблица со списком элементов. Каждый элемент может иметь родителя из этой же таблицы.
в таблице
id name parent
1 Child1ofParent0 0
2 Child1ofParent1 1
3 Child2ofParent1 1
4 Child2ofParent0 1
5 Child1ofParent2 4
...
То есть простейшая структура - где parent - id родительского элемента

Подскажите пожалуйста, как вывести GridView в виде
Parent1
Child1ofParent1
Child2ofParent1
Child3ofParent1
Parent2
Child1ofParent2
Child2ofParent2
Child3ofParent2
....
То есть сначала выводится родитель, затем все дочерние элементы (у которых paren = id родителя), потом следующий. Вложеность только на 1 ребенка. Дальше подкатегорий нет
Аватара пользователя
proctoleha
Сообщения: 298
Зарегистрирован: 2016.07.10, 19:00

Re: GridView отсортированный список parent/child

Сообщение proctoleha »

Вообще проще один раз изучить Nested Sets (Вложенные множества), и не париться с детьми и родителями.
Но ситуации бывают разные, и в одном из проектов, мне потребовалось решить такую же задачу - вывести упорядоченный плоский список в GridView, ориентируясь на id и pid

Написал небольшой костыль, вспомнив, при этом, как можно нестандартно объявлять переменные в php и присваивание значений по ссылке.

1. У нас есть объект класса ActiveDataProvider, причем сортируем данные сначала по pid ASC, это важно. В выборке у нас сначала идут строки с pid = null, например метод класса OrdersSearch, который ищет отгруженные заказы:

Код: Выделить всё

public function searchShipped($params)
    {
        $query = Order::find()
            ->where(['user_id' => $this->user_id])
            ->andWhere(['>=', 'status', Order::STATUS['part_shipped']['value']]);
        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'sort' => [
                'defaultOrder' => ['pid' => SORT_ASC, 'created_at' => SORT_DESC],
            ]
        ]);
        $this->load($params);
        if (!$this->validate()) {
            return $dataProvider;
        }
        $query->andFilterWhere(['status' => $this->status]);
        $query->andFilterWhere(['like', 'number', $this->number]);

        return $dataProvider;
    }
2. В контроллере почти всё стандартно

Код: Выделить всё

    public function actionShipped($user_id)
    {
        $searchModel = new OrdersSearch($user_id);
        $dataProvider = $searchModel->searchShipped(\Yii::$app->request->queryParams);
        $searchModel->changeShippedModels($dataProvider);

        return $this->render('shipped', [
            'dataProvider' => $dataProvider,
            'searchModel' => $searchModel,
        ]);
    }
Только появился метод changeShippedModels.

3. В этом методе мы вытаскиваем массив моделей из объекта класса ActiveDataProvider, и приводим этот массив к нужному нам виду. Внимание! По условию задачи у нас только один уровень вложенности!

Код: Выделить всё

    public function changeShippedModels(ActiveDataProvider $dataProvider)
    {
        /** @var Order[] $models */
        $models = $dataProvider->getModels();
        $modelsNew = [];
        $data = [];
        foreach ($models as $model) {
            if (!$model->pid) {
                ${'parent' . $model->id}[] = $model;
                $data[] = &${'parent' . $model->id};
            } else {
                ${'parent' . $model->pid}[] = $model;
            }
        }
        foreach ($data as $item) {
            foreach ($item as $i) {
                $modelsNew[] = $i;
            }
        }
        $dataProvider->setKeys(ArrayHelper::getColumn($modelsNew, 'id'));
        $dataProvider->setModels($modelsNew);
    }
Код прозрачный, если что-то непонятно - медитируем. Но, вообще это костыль. Гораздо проще работать с nested sets. Даже если всего один ребенок.
Вот за что я не люблю линукс, так это за свои кривые, временами, руки
Ответить