Reputation: 2528
I have a table with following structure:
categories
I want to get the tree of categories with single function. I've written something like this in my model, but it doesn't work.
public function getChildren($parent) {
$criteria = new CDbCriteria;
$criteria->condition='parent_id=:id';
$criteria->params=array(':id'=>$parent);
$model = Term::model()->findAll($criteria);
while ($model) {
echo $model->id . "<br />";
$this->getChildren;
}
}
Can someone help me with this? Thanks.
Upvotes: 2
Views: 7619
Reputation: 11
This is better solution. Only just one query per lvl. Other solutions use query per child or query all records (good if quantity is low)
$models=Category::find()->where(['id'=>4])->one()->getRecursiveChildren();
public function getChild()
{
return $this->hasMany(Category::className(), ['parent_id' => 'id']);
}
public function getRecursiveChildren()
{
$models=[];
$childs=$this->child;
Yii::createObject(ActiveQuery::className(), [get_called_class()])->findWith(['child'], $childs);
if(is_array($childs)&&count($childs)>0)
foreach ($childs as $item)
{
$models[]=$item;
$models=array_merge($models,$item->getRecursiveChildren());
}
return $models;
}
Upvotes: 1
Reputation: 1421
May be this one help you...:)
I am copying my controller class for yii2 for category
<?php
namespace api\modules\v1\controllers;
use Yii;
use yii\rest\Controller;
use api\modules\v1\models\MySearializer;
use api\modules\v1\models\search\CategorySearch;
use api\modules\v1\models\Category;
class CategoryController extends Controller
{
public function actionIndex()
{
$searchModel = new CategorySearch();
$status = false;
$message = '';
$data = (object)[];
$dataProvider = $searchModel->search(Yii::$app->request->getBodyParams());
$obj = new MySearializer();
$obj->collectionEnvelope ='pager';
$ser = $obj->serialize($dataProvider);
return ['status' => true, 'message' => 'Category Listing',
'data' => $ser];
}
public function actionView($id)
{
$status = false;
$message = '';
$data = (object)[];
$model = Category::findOne($id);
if($model)
{
return ['status' => true, 'message' => 'Category View',
'data' => $model];
}else
{
return ['status' => false, 'message' => 'Category not found',
'data' => $data];
}
}
public function actionAllParent()
{
$status = false;
$message = '';
$data = (object)[];
$model = Category::find()->where(['parent'=>0])->all();
if($model)
{
return ['status' => true, 'message' => 'All Parent Category Listing',
'data' => $model];
}else
{
return ['status' => false, 'message' => 'Parent Category not found',
'data' => $data];
}
}
public function actionTree()
{
$status = false;
$message = '';
$data = (object)[];
$model = Category::find()->asArray()->all();
$cattree = $this->CreateTree($model);
print_r($cattree); die;
if($model)
{
return ['status' => true, 'message' => 'All Child Category Listing',
'data' => $model];
}else
{
return ['status' => false, 'message' => 'Child Category not found',
'data' => $data];
}
}
public function actionChild($id)
{
$status = false;
$message = '';
$data = (object)[];
$model = Category::find()->where(['parent'=>$id])->all();
if($model)
{
return ['status' => true, 'message' => 'All Child Category Listing',
'data' => $model];
}else
{
return ['status' => false, 'message' => 'Child Category not found',
'data' => $data];
}
}
public function CreateTree($tree,$parentId=0)
{
$branch =[] ;
foreach ($tree as $element)
{
if ($element['parent'] == $parentId)
{
$children = $this->CreateTree($tree, $element['slno']);
if ($children)
{
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
}
Upvotes: 0
Reputation: 161
You can do it with one recursive function in model like:
public function getCategoriesTree(&$tree = array(), $parentID = 0)
{
$criteria = new CDbCriteria;
$criteria->condition='parent_id=:id';
$criteria->params=array(':id'=>$parentID);
$resultNumber = $this->count($criteria);
if($resultNumber > 0)
{
$model = $this->findAll($criteria);
// get category position as order_id
$orderId = 0;
foreach ($model as $key)
{
$tree[$orderId] = array(
'id' => $key->id,
'parent_id' => $key->parent_id,
'name' => $key->name,
);
// get subcategories
$this->getCategoriesTree($tree[$orderId]['subcat'], $key->id);
if(count($tree[$orderId]['subcat']) === 0)
{
unset($tree[$orderId]['subcat']);
}
$orderId++;
}
}
}
And use it in controller as:
$model = new Categories;
$tree = array(
0 => array(
'id' => 0,
'parent_id' => -1,
'name' => 'Main category',
'subcat' => array()
),
);
$model->getCategoriesTree($tree[0]['subcat']);
Upvotes: 0
Reputation: 2528
I've finally solved the issue. If someone interested, here's the code:
public function getChildren($parent, $level=0) {
$criteria = new CDbCriteria;
$criteria->condition='parent_id=:id';
$criteria->params=array(':id'=>$parent);
$model = $this->findAll($criteria);
foreach ($model as $key) {
echo str_repeat(' — ', $level) . $key->name . "<br />";
$this->getChildren($key->id, $level+1);
}
}
public function getChildrenString($parent) {
$criteria = new CDbCriteria;
$criteria->condition='parent_id=:id';
$criteria->params=array(':id'=>$parent);
$model = $this->findAll($criteria);
foreach ($model as $key) {
$storage .= $key->id . ",";
$storage .= $this->getChildrenString($key->id);
}
return $storage;
}
Upvotes: 6