category-behavior

Contains popular methods for searching and listing plain and hierarchical categories in Yii

  • Owner: ElisDN/category-behavior
  • Platform:
  • License::
  • Category::
  • Topic:
  • Like:
    0
      Compare:

Github stars Tracking Chart

Category ActiveRecord Behavior for Yii

Contains popular methods for work with plain and hierarchical categories in Yii

Readme

README RUS

Installation

Extract to protected/components.

Usage example

Attach any from this behaviors to your model. Use DCategoryBehavior for plain models and DCategoryTreeBehavior for hierarchical models.

[php]
class Tag extends CActiveRecord
{
    // ...
    
    public function behaviors()
    {
        return array(
            'CategoryBehavior'=>array(
                'class'=>'DCategoryBehavior',
                'titleAttribute'=>'title',
                'defaultCriteria'=>array(
                    'order'=>'t.title ASC'
                ),
            ),
        );
    }    
        
    private $_url;

    // Generates URL. Use simple `$model->url` instead of `Yii::app()->createUrl(...)`;
    public function getUrl()
    {
        if ($this->_url === null)
            $this->_url = Yii::app()->createUtl('blog/tag', array('tag'=>$this->title);
        return $this->_url;
    } 
    
    // ...
}

// Static pages
class Page extends CActiveRecord
{
    // ...
    
    public function behaviors()
    {
        return array(
            'CategoryBehavior'=>array(
                'class'=>'DCategoryBehavior',
                'titleAttribute'=>'title',
                'aliasAttribute'=>'alias',
                'urlAttribute'=>'url',
                'requestPathAttribute'=>'alias',
                'defaultCriteria'=>array(
                    'order'=>'t.title ASC'
                ),
            ),
        );
    }
    
    private $_url;

    // Generates URL for every page. Use simple `$model->url` instead of `Yii::app()->createUrl(...)`;
    public function getUrl()
    {
        if ($this->_url === null)
            $this->_url = Yii::app()->request->baseUrl . '/page/' . $this->cache(3600)->getPath() . Yii::app()->urlManager->urlSuffix;
        return $this->_url;
    }  
    
    // ...
}

I recommend to create a base class Category and extend it in all subclasses

[php]
// Base class for all category models.
abstract class Category extends CActiveRecord
{        
    // ...
    
    public function behaviors()
    {
        return array(
            'CategoryTreeBehavior'=>array(
                'class'=>'DCategoryTreeBehavior',
                'titleAttribute'=>'title',
                'aliasAttribute'=>'alias',
                'urlAttribute'=>'url',
                'requestPathAttribute'=>'path',
                'parentAttribute'=>'parent_id',
                'parentRelation'=>'parent',
                'defaultCriteria'=>array(
                    'order'=>'t.position ASC, t.title ASC'
                ),
            ),
        );
    }     
}

/* 
 * Existing of redeclared a custom field `urlPrefix` in all subclasses allows simple 
 * generate URL in a base class without overriding of `getUrl()` method in childs
 */
class BlogCategory extends Category
{
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
	} 
    
    public function tableName()
    {
		return '{{blog_category}}';
	}

    public function relations()
	{
		return array_merge(parent::relations(), array(
            'parent' => array(self::BELONGS_TO, 'BlogCategory', 'parent_id'),
		));
	} 

    public function getUrl()
    { 
        ...
    }    
}

After attaching of this Behavior to your model you can use this public methods:

Specification

DCategoryBehavior

Common parameters:

Common methods:

DCategoryTreeBehavior (extends DCategoryBehavior)

Content DCategoryBehavior specification and addons:

Additional parameters:

Additional and overrided methods:

* Argument $parent may contain a number, a model object or array of numbers. You may use:

  • Model::model()->getChildsArray();
  • Model::model()->getChildsArray(5);
  • Model::model()->getChildsArray(array(1, 3, 5));
  • Model::model()->getChildsArray($model) or $model->getChildsArray().

Using for the dropDownList() method:

[php]
<div class="row">
    <?php echo $form->labelEx($model, 'category_id'); ?><br />
    <?php echo $form->dropDownList(
        $model,
        'category_id',
        array_merge(
            array(''=>'[None]'), 
            BlogCategory::model()->published()->getTabList()
        )
    ); ?><br />
    <?php echo $form->error($model, 'category_id'); ?>
</div>

Using for CMenu widget (with caching):

[php]
<h2>All categories:</h2>
<?php $this->widget('zii.widgets.CMenu', array(
    'items'=>BlogCategory::model()->cache(3600)->getMenuList(10))
); ?>

<h2>Subcategories of <?php echo $category->title; ?>:</h2>
<?php $this->widget('zii.widgets.CMenu', array(
    'items'=>$category->cache(3600)->getMenuList())
); ?>

Usage sample in E-shop

Configuration file config/main.php:

[php]
return array(
    'components'=>array(
		'urlManager'=>array(
			'urlFormat'=>'path',
			'showScriptName'=>false,
			'rules'=>array(
                // ...
                
                'shop/<action:cart, order>'=>'shop/<action>',
                
                // http://site.com/shop/printers/home/laser/15
                'shop/<path:.+>/<id:\d+>'=>'shop/view',
                
                // http://site.com/shop/printers/home/laser
                'shop/<path:.+>'=>'shop/category',
                
                'shop'=>'shop/index',
                
                // ...
            ),
        ),
    ),
)

Base category model:

[php]
abstract class Category extends CActiveRecord
{    
    protected $urlPrefix = '';
    
    // ...
    
    public function behaviors()
    {
        return array(
            'CategoryTreeBehavior'=>array(
                'class'=>'DCategoryTreeBehavior',
                'titleAttribute'=>'title',
                'aliasAttribute'=>'alias',
                'urlAttribute'=>'url',
                'requestPathAttribute'=>'path',
                'parentAttribute'=>'parent_id',
                'parentRelation'=>'parent',
                'defaultCriteria'=>array(
                    'order'=>'t.title ASC'
                ),
            ),
        );
    } 
    
    public function rules(){
        return array(
            array('title, alias', 'required'),
            array('title, alias', 'length', 'max'=>255),
            array('parent_id', 'numerical', 'integerOnly'=>true),
        );
    }
    
    public function attributeLabels(){
        // ...
    }  
    
    private $_url;

    // Generates URL. Use simple `$model->url` instead of `Yii::app()->createUrl(...)`;
    public function getUrl()
    {
        if ($this->_url === null)
            $this->_url = Yii::app()->request->baseUrl . '/' . $this->urlPrefix . $this->cache(3600)->getPath() . Yii::app()->urlManager->urlSuffix;
        return $this->_url;
    }   
    
    // ...
}

ShopCategory model:

[php]
class ShopCategory extends Category
{
    protected $urlPrefix = 'shop/';
    
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
	}  
    
    public function tableName()
    {
		return '{{blog_category}}';
	} 
    
    public function relations()
	{
		return array_merge(parent::relations(), array(
            'parent' => array(self::BELONGS_TO, 'ShopCategory', 'parent_id'),
		));
	}  
}

Product model:

[php]
class ShopProduct extends CActiveRecord
{  
    // ...

	public function relations()
	{
		return array(
            'category' => array(self::BELONGS_TO, 'ShopCategory', 'category_id'),
		);
	}

    private $_url;

    public function getUrl(){
        if ($this->_url === null)
            $this->_url = Yii::app()->request->baseUrl . '/shop/' . $this->category->path . '/' . $this->id;
        return $this->_url;
    }
}

Controller:

[php]
class ShopController extends Controller
{
    public function actionIndex()
    {
        $criteria = new CDbCriteria;
        $criteria->order = 't.id DESC';
        
        $dataProvider = new CActiveDataProvider(
            ShopProduct::model()->cache(300),
            array(
                'criteria'=>$criteria,
                'pagination'=>array(
                    'pageSize'=>20,
                    'pageVar'=>'page',
                )
            )
        );

        $this->render('index', array(
            'dataProvider'=>$dataProvider,
        ));
    }
    

    public function actionCategory($path)
    {
        $category = ShopCategory::model()->findByPath($path);
        if (!$category)
            throw new CHttpException(404, 'Category not found');

        $criteria = new CDbCriteria;
        $criteria->order = 't.id DESC';
        
        $criteria->addInCondition('t.category_id', array_merge(
            array($category->id), $category->getChildsArray()
        ));
        
        $dataProvider = new CActiveDataProvider(
            ShopProduct::model()->cache(300),
            array(
                'criteria'=>$criteria,
                'pagination'=>array(
                    'pageSize'=>20,
                    'pageVar'=>'page',
                )
            )
        );

        $this->render('category', array(
            'dataProvider'=>$dataProvider,
            'category' => $category,
        ));
    }
    
    public function actionView($id)
    {
        $product = ShopProduct::model()->with('category')->findByPk($id);

        // Mirrors protection) 
        if (Yii::app()->request->requestUri != $product->url) 
            $this->redirect($product->url);
        
        if (!$product) 
            throw new CHttpException(404, 'Not found');

        $this->render('view', array(
            'product'=>$product,
        ));
    }
}

View shop/index.php:

[php]
<?php
$this->pageTitle = 'Catalog';
$this->breadcrumbs array('Catalog');
?>

<h1>Catalog</h1>

<p>Categories:</p>
<?php $this->widget('zii.widgets.CMenu', array('items' => ShopCategory::model()->getMenuList()));?>

<?php echo $this->renderPartial('_loop', array('dataProvider'=>$dataProvider)); ?>

View shop/category.php:

[php]
<?php
$this->pageTitle = 'Catalog - ' . $category->getFullTitle();
$this->breadcrumbs = array_merge(
    array(
        'Catalog'=>$this->createUrl('shop/index'),
    ), 
    $category->getBreadcrumbs()
);
?>

<h1><?php echo CHtml::encode($category->title); ?></h1>

<p>Subcategories:</p>
<?php $this->widget('zii.widgets.CMenu', array('items' => $category->getMenuList()));?>

<?php echo $this->renderPartial('_loop', array('dataProvider'=>$dataProvider)); ?>

View shop/view.php:

[php]
<?php
$this->pageTitle = $product->title;
$this->breadcrumbs=array(
    'Catalog'=>$this->createUrl('shop/index'),
);

if ($product->category)
    $this->breadcrumbs = array_merge($this->breadcrumbs, $product->category->getBreadcrumbs(true));

$this->breadcrumbs[]= $product->title;
?>

<h1><?php echo CHtml::encode($product->title); ?></h1>

<?php if ($product->category): ?>
    <p>Category: <?php echo CHtml::link($product->category->title, $product->category->url); ?></p>
<?php endif; ?>

<p>Price: <?php echo $product->price; ?></p>

Main metrics

Overview
Name With OwnerElisDN/category-behavior
Primary LanguagePHP
Program languagePHP (Language Count: 1)
Platform
License:
所有者活动
Created At2012-10-29 12:49:25
Pushed At2013-11-01 08:59:51
Last Commit At2013-11-01 12:59:28
Release Count5
Last Release Namev1.4 (Posted on )
First Release Namev1.0 (Posted on )
用户参与
Stargazers Count7
Watchers Count5
Fork Count5
Commits Count47
Has Issues Enabled
Issues Count1
Issue Open Count1
Pull Requests Count0
Pull Requests Open Count0
Pull Requests Close Count0
项目设置
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private