Improved link() funktion in CSort

I extended CSort with the following function thus only presenting a clickable link if a column is in fact sortable. Maybe a candidate for the framework?



<?php


class MySort extends CSort


{


  public function sortLink($attribute, $label=NULL, $htmlOptions=array())


  {


    if (array_key_exists($attribute, $this->attributes))


      return $this->link($attribute, $label, $htmlOptions);


    else


      return $this->link($attribute, $label, array_merge(array('class'=>'nolink'), $htmlOptions));


  }


}


?>


Css example:



a.nolink, a:link.nolink, a:visited.nolink


{


  cursor:default;


  text-decoration:none;


  border-bottom: 0 none; /* reset my 1px dotted */


}


/Tommy

Edit: I noticed a check is also needed for $this->attributes to be set. Also the href should probably be set to '#'.

Also it would be great if sorting link would get css class name in the core.

There's a way to do it in app:

http://www.yiiframew…87.html#msg7887

but I think it's pretty common functionality.

Hi tri,

I've tried your code above and tested if $this->attributes isset. Nothing gets returned, because $this->attributes is obviously not set.

Here's my code (Don't know if it is right)

        


if(isset($this->attributes))


{


    if(array_key_exists($attribute, $this->attributes))


        return $this->link($attribute, $label, $htmlOptions);


    else


        return $this->link($attribute, $label, array_merge(array('class'=>'nolink')), $htmlOptions);


}


Any ideas ???

You need to set 'attributes' property with proper mappings.

A little help that maybe  :P Please qiang

My bad. I didn't read your code carefully. But why are you checking isset($this->attributes)?

At the same time, you may be interested in the following post:

http://www.yiiframew…opic,540.0.html

From tri's original post

Quote

Edit: I noticed a check is also needed for $this->attributes to be set. Also the href should probably be set to '#'.

This is why I check with isset, following tri's post. Don't know if this is the right way or not. Still learning alot about Yii and PHP

I've used this piece of code now qiang

 


<?php





class MySort extends CSort


{


    public function sortLink($attribute, $label=NULL, $htmlOptions=array())


    {


        if(array_key_exists($attribute, $this->attributes))


            return $this->link($attribute, $label, $htmlOptions);


        else


            return $this->link($attribute, $label, array_merge(array('class'=>'nolink')), $htmlOptions);


    }


}





?>


I've set 'attributes' in my controller



<?php





        $sort=new CSort('Bundle');


        $sort->attributes=array(


            'bundle.id'=>'Id',


            'bundle.quantityOnHand'=>'QOH',


            'bundle.sizeId'=>'Size',


            'bundle.lengthId'=>'Length',


            'bundle.bundlePrice'=>'Price',


            'bundle.totalPrice'=>'Total Value',


            'bundle.createTime'=>'Created'


        );


        $sort->applyOrder($criteria);





?>





and finally in my view



  <tr>


    <th><?php echo MySort::sortLink('bundle.id'); ?></th>


    <th><?php echo $sort->link('bundle.quantityOnHand'); ?></th>


    <th><?php echo $sort->link('bundle.sizeId'); ?></th>


    <th><?php echo $sort->link('bundle.lengthId'); ?></th>


    <th><?php echo $sort->link('bundle.bundlePrice'); ?></th>


    <th><?php echo $sort->link('bundle.totalPrice'); ?></th>


    <th><?php echo $sort->link('bundle.createTime'); ?></th>


    <th>Actions</th>


  </tr>


The bundle.id passed to MySort::sortLink() function raises the following exception



Description





Property "BundleController.attributes" is not defined.





Source File





C:Apache2.2htdocsprojectprotectedcomponentsMySort.php(7)





00001: <?php


00002: 


00003: class MySort extends CSort


00004: {


00005:     public function sortLink($attribute, $label=NULL, $htmlOptions=array())


00006:     {


00007:         if(array_key_exists($attribute, $this->attributes))


00008:             return $this->link($attribute, $label, $htmlOptions);


00009:         else


00010:             return $this->link($attribute, $label, array_merge(array('class'=>'nolink')), $htmlOptions);


00011:     }


00012: }


00013: 


00014: ?>


Stack Trace





#0 C:Apache2.2htdocsprojectprotectedcomponentsMySort.php(7): CComponent->__get('attributes')


#1 C:Apache2.2htdocsprojectprotectedviewsbundlelist.php(10): MySort->sortLink('bundle.id')


#2 C:Apache2.2yii_1.0.4frameworkwebCBaseController.php(119): require('C:Apache2.2ht...')


#3 C:Apache2.2yii_1.0.4frameworkwebCBaseController.php(88): CBaseController->renderInternal('C:Apache2.2ht...', Array, true)


#4 C:Apache2.2yii_1.0.4frameworkwebCController.php(681): CBaseController->renderFile('C:Apache2.2ht...', Array, true)


#5 C:Apache2.2yii_1.0.4frameworkwebCController.php(620): CController->renderPartial('list', Array, true)


#6 C:Apache2.2htdocsprojectprotectedcontrollersBundleController.php(174): CController->render('list', Array)


#7 C:Apache2.2yii_1.0.4frameworkwebactionsCInlineAction.php(32): BundleController->actionList()


#8 C:Apache2.2yii_1.0.4frameworkwebCController.php(279): CInlineAction->run()


#9 C:Apache2.2yii_1.0.4frameworkwebCController.php(257): CController->runAction(Object(CInlineAction))


#10 C:Apache2.2yii_1.0.4frameworkwebCController.php(236): CController->runActionWithFilters(Object(CInlineAction), NULL)


#11 C:Apache2.2yii_1.0.4frameworkwebCWebApplication.php(332): CController->run('list')


#12 C:Apache2.2yii_1.0.4frameworkwebCWebApplication.php(120): CWebApplication->runController('bundle/list')


#13 C:Apache2.2yii_1.0.4frameworkbaseCApplication.php(133): CWebApplication->processRequest()


#14 C:Apache2.2htdocsprojectindex.php(11): CApplication->run()


#15 {main}


You should not call sortLink() statically. Inside sortLink() you are using $this. If you call the method statically, $this would refer to the calling context, which is the controller.

So do I call sortLink by creating a MySort object first?

Yes, you should create an instance of MySort, not CSort (otherwise why you subclass it?). With a MySort instance, you can call both $this->sortLink and $this->link.

Ok, got that

Here's my view code now



<th><?php $s = new MySort; echo $s->sortLink('bundle.id'); ?></th>


and that raised the following exception



Description





array_key_exists() [<a href='function.array-key-exists'>function.array-key-exists</a>]: The second argument should be either an array or an object





Source File





C:Apache2.2htdocsprojectprotectedcomponentsMySort.php(16)





00004:  * and open the template in the editor.


00005:  */


00006: 


00007: class MySort extends CSort


00008: {


00009:     public function  __construct($className=__CLASS__)


00010:     {


00011:         parent::__construct($className);


00012:     }


00013: 


00014:     public function sortLink($attribute, $label=NULL, $htmlOptions=array())


00015:     {


00016:         if(array_key_exists($attribute, $this->attributes))


00017:             return $this->link($attribute, $label, $htmlOptions);


00018:         else


00019:             return $this->link($attribute, $label, array_merge(array('class'=>'nolink')), $htmlOptions);


00020:     }


00021: }


00022: 


00023: ?>


Now if I understand correctly, $this->attributes refers to the this



<?php





        $sort=new CSort('Bundle');


        $sort->attributes=array(


            'bundle.id'=>'Id',


            'bundle.quantityOnHand'=>'QOH',


            'bundle.sizeId'=>'Size',


            'bundle.lengthId'=>'Length',


            'bundle.bundlePrice'=>'Price',


            'bundle.totalPrice'=>'Total Value',


            'bundle.createTime'=>'Created'


        );


        $sort->applyOrder($criteria);





?>


in my controller

What am I doing wrong  ??? ???

You only need one sort instance (created in your controller, the class should be MySort). You can use $sort->sortLink() if $sort is created as an instance of MySort in your controller.

Gotcha, changed my controller code to



$sort=new MySort('bundle');


and view code to



<th><?php $sort->sortLink('bundle.id'); ?></th>


No id text gets returned, just blank  :(

You forgot echo.

Whoops  ;D

The table header 'Id' is still a link and sortable and not normal text.

One thing I also noticed setting the $sort->attributes is that, attributeLabels overrides my $sort->attributes  :-\

Sorry, I've been away from the forum for a couple of weeks.

Somewhere in another post I came to the conclusion that two functions side by side probably wont be needed. Then nothing needs to be done to the framework, just subclass the CSort and use the link() function of the customized class. Remember to call the parents link() function.

The headers works for me with attributeLabels. I use "generic" attribute names and replace with t() translations in attributeLabels. I use Yii::app()->setLanguage to switch language and I have translated strings for all languages I use (en, sv). The "generic" attribute names goes into the sort->attributes as keys, the values will be used as GET parameters when clicking on a sortable column.

The links are still there but the CSS gives the impression of ordinary text. No hand cursor and no decorations. The remaining problem is how to get rid of the reload, that's why I suggested href="#". Don't know if that's good enough for general use.

And of course only columns you want to be sortable should be present in the sort->attributes. If not present the CSS class (no-link in my example) will change the appearance of the link.

/Tommy

No problem tri, I think I'm going to hard code the non-sortable columns I want in my view instead of going the, extending CSort route. Maybe in the future their will be a Yii release that makes provision for this.

Thanks for your input tri