Complex code reuse

Hi,

My site is divided into three sections [ Groups (like google groups), Pages, Companies ].

I created 3 modules one for each section:

/modules/groups

/modules/pages

/modules/companies

Now comes my problem. Each section has a photo gallery, then i created one GaleryController.php in each module:

/modules/groups/controllers/GaleryController.php

/modules/pages/controllers/GaleryController.php

/modules/companies/controllers/GaleryController.php

The Gallery model is the same, the gallery views, components and controllers are very similar and each module has its own security to access the gallery.

How I can reuse the gallery code in this situation?

Thanks.

/modules/gallery ? :)

But how I can handle small changes in the actions and views depending on the module to which the gallery?

You need to sit down and flesh out those differences - maybe you can handle it through role based permissions?

Or hardcoded logic ((if module->id === ‘groups’) dosomething))

Do you want it to look different or behave differently?

Take CMenu, for instance - it’s the same code, but you can make it act and look differently by passing templates and options and using callbacks.

So, maybe you could create a widget/extension which can be customised?

I am not sure - just throwing you some ideas. :)

As jacmoe said you need to have clear what are the diferences between the logic you need in each separate module, and abstract the general logic, remember you can use CAction, or CExtController and have the controller (or actions) as components.

Ok. Thanks for the replies. But I dont know how abstract the general logic, I need a little push:). Suppose I have the following:

/modules/groups/

/modules/pages

and a module gallery for manager the groups and page’s gallery

/modules/gallery

Now, i use routes:




config/main.php


'urlManager'=>array(

        'urlFormat'=>'path',

	'showScriptName' => false,

	'rules'=>array(

	'groups/<idgroup:\d+>/gallery'=>'gallery/default/index?source=group',

        'pages/<idpage:\d+>/gallery'=>'gallery/default/index?source=page'

    )

)



In /modules/gallery/controllers




DefaultController.php


...

public function actionIndex()

{

        /*Suppose that the gallery belongs to the group

          -I need check if user belongs to the group and has access to gallery index.

          -I need add feed activity to the group (Eg. <Username> is viewing the gallery in the group <groupname>)

          -And finally I need to show the view with the head of the group and after the gallery index view.

        */


        Here is where I do not know what to do.

...



any suggestions?

The simplest would probably be to let it be a regular Yii widget.

Then, to save yourself a lot of time, let that widget choose a view file depending on the currently active module.

Like this - in widget::run:


$this->render('gallery_' . Yii::app()->module->id);

Then you just manually create three (you have three modules, right?) different view files for the widget.

If that’s what you need.

The gallery itself has three galleries, one for each group.

Keep in mind that your modules still needs each their own ‘gallery’ action, but you don’t need to do any duplication (not much) besides rendering the widget.

All group specific stuff would of course happen in each modules ‘gallery’ index view.

That would probably do the trick, I think.

Otherwise, you need explain exactly what your requirements are. :)

<edit>

So I suggest using a widget instead of a module, for the gallery.

</edit>

It is possible to do with inheriting controllers?

e.g.

protected/components/forum/BaseTopicController.php

and within the module

protected/modules/groups/controllers/TopicController.php (extends BaseTopicController)

My guess is that if a module should be independent, with this solution breaks the rule.

use CAction to share the actions

here is a nice manual on how to do it

http://www.yiiframework.com/wiki/170/actions-code-reuse-with-caction/

each controller pass its own parameters to the class, allowing full modification

Thanks for replies. I’ve read the wiki, but in my case, some actions have small changes in the logic. Also would have to create too CAction.

I’m really confused. I think this should be easy to implement.

e.g

/groups/1234/news

/pages/1344/news

How to implement the news in two sections, allowing code reuse and some changes in the logic?

Maybe look at defining routes via the urlManager where each (/groups/1234/news & /pages/1234/news) resolve to the same controller/action…if I understand your query correctly

If not CActions, then using a shared parent controller should do it.

@jackmoe: do you like this? Is it correct?

I have the following:

/ modules / groups / controllers / NewsController.php

/ modules / pages / controllers / NewsController.php




/modules/groups/NewsController.php

...

public function actionCreate()

	{

		

		if (!Yii::app()->user->checkAccess("groups.createNews"))

			$this->accessDenied();

			

		$news=new NewsGroup();

		if(isset($_POST['NewsGroup']))

		{

			$news->attributes=$_POST['NewsGroup'];

			if($this->group->addNews($news))

				$this->redirect($news->getUrl($this->group));

		}


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

			'model'=>$news,

		));

		

	}

...






/modules/groups/views/create.php


<h1><?php echo Yii::t('groups','Create news')?></h1>


<?php $this->widget('application.components.pack.news.CCreateFormNewsWidget', array(

	'model'=>$model)

);?>







/components/pack/news/CCreateFormNewsWidget.php


class CCreateFormNewsWidget extends CWidget{

	

	public $model;

	

	public function run(){

		

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

	}

	

}






/components/pack/news/views/_form.php


<?php $form=$this->beginWidget('CActiveForm', array(

		'htmlOptions'=>array('enctype'=>'multipart/form-data'),

));?>


     <div class="row">

        <?php echo $form->labelEx($model,'title'); ?>

        <?php echo $form->textField($model,'title',array('size'=>50,'maxlength'=>150)); ?>

        <?php echo $form->error($model,'title'); ?>

    </div>

    

    <div class="row">

        <?php echo $form->labelEx($model,'entry'); ?>

        <?php echo $form->textField($model,'entry', array('size'=>50,'maxlength'=>150)); ?>

        <?php echo $form->error($model,'entry'); ?>

    </div>

    

    <?php if ($this->controller->module->id == 'groups'):?>

     <div class="row">

     	<?php echo $form->labelEx($model,'access_flag'); ?>

		<?php echo $form->checkBox($model,'access_flag'); ?> <?php  echo Yii::t('groups','Only members.')?>

	</div>

	<?php endif;?>

    

    <div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? Yii::t('news','Create') : Yii::t('news','Save')); ?>

	</div>

    

<?php $this->endWidget(); ?>



Now i can reuse CCreateFormNewsWidget in other views.

Thanks

It looks like something I’d put together myself.

So thumbs up.:)

The only thing I wouldn’t do is check permissions in the controller.

Let an access filter take care of that.

But besides that I think it’s a clean solution with little to no duplication.

Of course, you can always go crazy with abstract actions etc, but in the long run it’s best to stay as simple as possible. ;)

Hi again X’D, i’ve been thinking about how to save more code. This is another possible solution using CAction and widget as action provider.




/modules/groups/controllers/ForumController.php


class ForumController extends BaseGroupController {


	public $forum;

	public $owner;


	public function init(){


		parent::init();

		

		//Set group forum

		$this->forum = $this->group->forum;

		

		if ($this->forum == null)

			throw new CHttpException(404, Yii::app('groups','Forum not found.'));

			

		//Forum owner

		$this->owner = $this->group;

	}

	

	/*

	 * Event for new post

	 */

	public function onNewPost($event){

		

		//Activiy stream groups

		$post = $event->sender;

		

	}

	

	public function actions(){

		

		return array(

			'forum.'=> array(

				'class' => 'application.components.forum.ForumActionWidget',

		));

	}

}




/application/components/forum/ForumActionWidget.php


class ForumActionWidget extends CWidget{

	

	public static function actions(){

		return array(

			'index'=>'application.components.forum.actions.Index',

			'indexcategory'=>'application.components.forum.actions.IndexCategory',

			'createboard'=>'application.components.forum.actions.CreateBoard',

			'createcategory'=>'application.components.forum.actions.CreateCategory',

			'editcategory'=>'application.components.forum.actions.EditCategory',

			'deletecategory'=>'application.components.forum.actions.DeleteCategory',

			'createtopic'=>'application.components.forum.actions.CreateTopic',

			'viewtopic'=>'application.components.forum.actions.ViewTopic',

			'editpost'=>'application.components.forum.actions.EditPost',

			'replypost'=>'application.components.forum.actions.ReplyPost',

			'deletepost'=>'application.components.forum.actions.DeletePost',

			'subscribepost'=>'application.components.forum.actions.SubscribePost',

			'unsubscribepost'=>'application.components.forum.actions.UnsubscribePost',

		);

    }


}






One example CAction




class CreateTopic extends CAction{

	

	public function run($idcat){

		

		$category = $this->getController()->forum->getCategory($idcat);

		

		if ($category === null)

			throw new CHttpException(404,Yii::t('forum','Category not found'));

		

		$topic = new ForumMessage('newpost');

		//Event handler

		$topic->attachEventHandler('onNewPost', array($this->getController(), 'onNewPost'));

		

		if (isset($_POST['ForumMessage'])){

				

			$topic->attributes = $_POST['ForumMessage'];

			$topic->files = CUploadedFile::getInstancesByName('files');

			

			if ($category->addTopic($topic)){

				

				$this->getController()->redirect($topic->url);

			}

		}

			

		$this->getController()->render('createTopic', array('model'=>$topic, 'board' => $category));

	}

}



It works fine and i can reuse all code (actions and views) in other modules.

do you like?

Thanks.