Create dynamic action in controller file

In admin i have manage cms pages module, where admin can manage page’s seo data, page url and page content. All info is stored in database.

Now in front side i have SiteController.php, front side urls can be as below based on admin’s data.

i.e. http://mysite/site/home

or http://mysite/site/about-us

or http://mysite/site/contact-us

or http://mysite/site/products so on.

My issue is how can i create dynamic actions on the fly for each different pages.

you cannot create dynamic actions.

you can create single action which shows data from database depending on parameter in GET request.

you can then create mapping in route config which hides action name and maps url part to the named param.

Thanks redguy for quick reply.

During R&D i have checked beforeAction function in controller. Is it useful in my case?

Also how can i manage route config? Can you just point me some link or example?

u should have to create widgets , thats the easy way…refer some cms created in YII… GXC http://www.yiiframework.com/forum/index.php/topic/28652-open-source-yii-cms-gxc-cms/page__p__137836__hl__gxc#entry137836

I think you should do like this:

1-create action that show content:




class ContentController extends Controller

  public function actionView($page) {

    $content = FETCH CONTENT FROM DB WHERE CONTENT NAME = $page;

    DISPLAY $content;

  }

}



then you just add routing in main.php:




'components'=>array(

	...

	'urlManager'=>array(

		...

		'rules'=>array(

			'site/<page:[\w\-]+>'=>'content/view',

			...

			//default rules go here

		),

	),



there is one drawback - with rules like this you cannot have ‘site’ controller because every request to ‘site/XXXXXX’ will be redirected to ‘content/view’ controller/action. You can change SiteController to IndexController or StartController, or change the rules to (for example) ‘cms/<page:[\w\-]+>’=>‘content/view’ which will map every request to ‘cms/XXXXXX’ to content/view and XXXXXX will be passed as ‘page’ param.

you can also create exceptional routes for specific site controller actions like this:




'components'=>array(

        ...

        'urlManager'=>array(

                ...

                'rules'=>array(

                        'site/index'=>'site/index',

                        'site/login'=>'site/login',

                        'site/logout'=>'site/logout',

                        'site/<page:[\w\-]+>'=>'content/view',

                        ...

                        //default rules go here

                ),

        ),



order of url rules is important - first match wins when processing request.

Sorry for late reply.

Thanks redguy, it’s done. Here how it works:

Config:




'urlManager'=>array(

				'urlFormat'=>'path',

				'rules'=>array(

					 'site/<page:\w+>'=>'site/view',

				),

				'showScriptName'=>false,

				),



SiteController.php




public function actionView($page)

	{

		$model=Page::model()->findByAttributes(array('pageLink'=>$page));

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

	}



view.php


<?php $this->pageTitle = $model->metaTitle; ?>

<div class="title" align="center"><?php echo $model->pageName;?></div>

<p align="center">

<?php echo $model->pageDescription;?>

</p>

Before url rewrite: http://domain-name/site/view/page/about

After these code: http://domain-name/site/about

But one issue i am facing:

If page link contain dash i.e. about-us, it is not working.

I think dash is not part of \w characters set. You should change regexp to something like this:

<page:[\w-_. ]+>

Great!

I am afraid from regex, i tried to learn but each time it comes as different syntax, so its confusing me.

Thanks for your help :)

You said ,

class ContentController extends Controller

public function actionView($page) {

&#036;content = FETCH CONTENT FROM DB WHERE CONTENT NAME = &#036;page;


DISPLAY &#036;content;

}

}

but $page is what? it comes from where??

$page would be something that is identified in the url GET. So if you went to the url http://localhost/content/view/3, the controller action would use the 3 to fetch the desired content from db.

I know this thread was started some time ago but it could still prove useful for someone.

This is a valid question!

This doesn’t really make sense! $page should not carry the id whatsoever!

The setup should not be so different from normal CRUD except there is only 1 particular controller handling more than just 1 page/table/object with all their actions.

  • controller->bikes->index

  • controller->bikes->create

  • controller->bikes->view->id

  • controller->cars->index

  • controller->cars->create

  • controller->cars->view->id

  • controller->dynPage->dynAction->dynID

  • controller->dynPage->dynAction

  • controller->dynPage

So lets get started shall we?

[list=1]

[*]/protected/config/main.php




'urlManager'=>array(

    'urlFormat'=>'path',

    'showScriptName'=>false,

    'rules'=>array(

        //all non-dynamic "site" actions need to come first, eg

        'site/index'=>'site/index',

        'site/login'=>'site/login',

        'site/logout'=>'site/logout',


        'site/<page:[\w\-]+>/<action:[\w\-]+>/<id:\d+>'=>'content/index', //dynamic params: $page $action $id

	'site/<page:[\w\-]+>/<action:[\w\-]+>'=>'content/index', //dynamic params: $page $action

	'site/<page:[\w\-]+>'=>'content/index', //dynamic params: $page 

        

        //more rules

        //...

    ),

),



To avoid hiccups with the SiteController that comes with Yii by default, it would be much easier to call the new controller that handles the dynamic content something else but “Site” unless you really want “site” to be part of the URL. I would call this controller simply “ContentController”, “PageController” or “ViewController”. Anything but Sue Site! :) However, lets stick to SiteController.php just for now.

[*]/protected/components/Controller.php




class Controller extends CController

{

    public $layout=...

    public $menu=...

    ...


    /**

     * @var string dynamic params

     * assign these params in the global action and have them

     * easily accessible throughout the controller, all layouts and views

     * and there's no more need to pass them from action to action, and view to view

     */

    public $dynPage='';

    public $dynAction='';

    public $dynID='';

}



[*]/protected/controllers/SiteController.php




public function actionIndex($page, $action='index', $id=null)

{	

    $this->dynPage=$page;

    $this->dynAction=$action;

    $this->dynID=$id;


    //do some exciting stuff here

    //you can easily call different actions based on params (use switch/case) and keep your code structured

				

    $this->render('view', array(

    ));

}



[/list]

Here’s an example to demonstrate how useful these global vars can be.

Keep in mind Yii’s controller id will always be “site” in this scenario but every once in a while you still need to know where you exactly are.

/protected/views/site/view.php




<?php

/* dynamic view */

/* @var $this SiteController */


/* easily display breadcrumbs */

$this->pageTitle=Yii::app()->name

    .($this->dynPage!=null?' - '.ucfirst($this->dynPage):'')

    .($this->dynAction!=null?' - '.ucfirst($this->dynAction):'')

    .($this->dynID!=null?' # '.$this->dynID:'')

;

?>


<h1>Dynamic Index View</h1>


<section>

    <h4>You are here:</h4>

    <p><?='$page: ' . ($this->dynPage===null?"null":$this->dynPage);?></p>

    <p><?='$action: ' . ($this->dynAction===null?"null":$this->dynAction);?></p>

    <p><?='$id: ' . ($this->dynID===null?"null":$this->dynID);?></p>

</section>



Another situation where these global vars are useful is in the navigation menu when you need to know which link is active or not. This way it’s really easy to have parent links active as well.

Cheers.

Two problem:

But if i have "sub view" with other "action name" the code is not working


<?php

$this->widget('zii.widgets.CMenu', array(

'items' => array(

      array('label' => 'Home', 'url' => array('/site/home'), 'items' => array(

      array('label' => 'Homesub1', 'url' => array('/site/home', 'view' => 'homesub1')),

      array('label' => 'Homesub2', 'url' => array('/site/home', 'view' => 'homesub2')),

      )),

      array('label' => 'News', 'url' => array('/site/news')),

      array('label' => 'Content', 'url' => array('/site/content'), 'items' => array(

      array('label' => 'Content1', 'url' => array('/site/content', 'view' => 'content1')),

      array('label' => 'Content2', 'url' => array('/site/content', 'view' => 'content2')),

      array('label' => 'Content3', 'url' => array('/site/content', 'view' => 'content3')),

      )),

),

));

?>

domain/site/home OK

domain/site/home/view/homesub1 OK

domain/site/home/view/homesub2 OK

domain/site/news OK

domain/site/content OK

domain/site/content/view/content1 NO

domain/site/content/view/content2 NO

domain/site/content/view/content3 NO

I do not want to use controller name and view. How can I do this?

domain/content ?

domain/content/content1 ?


'<action:[\w\-]+>' => 'index/<action>',


'site/<page:[\w\-]+>'=>'content/index',

  • sub view beauty url :smiley: ???