Multiple 'sites'? Controller subfolders?

My test case for the Yii framework involves migrating a fairly large Prado based site with a console and multiple front ends.

I'm still familiarising myself with the way MVC is implemented in Yii, but it strikes me that my controllers folder is going to blow massively out of control. I think I'd like to delineate my controllers by the area of the site to which they're related, and then by the model to which they relate.

In the case of the console, I'm structuring it thusly:

For the console, i have ArtistConsoleController, with views for browsing and editing the artists, RegionConsoleController, with views for browsing and editing the regions, etc etc. There will be dozens of these controllers, each of which will have at least four or five reasonably complex actions.

I would like to put these in a subfolder of the /controllers folder, for EG /controllers/console/ArtistConsoleController.php and have the views in /views/console/artist/edit.php, /views/console/artist/browse.php, etc.

I know this isn't possible with the current implementation, but it is feasible from a technical perspective to implement it. The thing that I'm interested in though is whether it's a good idea to implement it from a design perspective.

Once again, I went hacking and got something together which seems to work but once again, I'm sure has a few gremlins - I don't know how it impacts theming because I haven't started to seriously play with that yet. CTheme::getViewFile() uses the controller id though, so as long as the controller id contains the subfolders (which the below changes ensure), it seems like it should work OK to me.

CWebApplication::createController($id) changes to:



<?php


	public function createController($id)


	{


		if($id==='')


			$id=$this->defaultController;


		


		$path='';


		$fullId = $id;


		if(($pos=strrpos($id,"/")))


		{


			$path = substr($id, 0, $pos);


			$id = substr($id, $pos+1);


		}


		if(preg_match('/^w+$/',$id))


		{


			if(isset($this->controllerMap[$id]))


				return CConfiguration::createObject($this->controllerMap[$id],$id);


			else


				return $this->createControllerIn($this->getControllerPath($path),ucfirst($id).'Controller',$fullId);


		}


	}


?>


CWebApplication::getControllerPath() changes to:



<?php


	public function getControllerPath($path=null)


	{


		if($this->_controllerPath!==null)


			return $this->_controllerPath;


		else


			return $this->_controllerPath=$this->getBasePath().DIRECTORY_SEPARATOR.'controllers'.($path!=null ? DIRECTORY_SEPARATOR . $path : '');


	}


?>


How many controllers do you have for all your subapplications? Did you attempt to write individual action class files ?

It's something like modules in symfony. i like the idea. Following this i can have:

protected

|

±- controllers

        |

        ±- module1

        ±- module2

And reuse some controler from module1 in module2

Unless the modules in symfony can be nested, it is not much different from Yii.

In Yii, you can create a controller class, and create individual controller action classes in a subdirectory. So you end up with:



protected/


       controllers/


              SiteController.php


              site/


                      action1.php


                      action2.php


              PostController.php


              post/


                      EditAction.php


                      CreateAction.php


Unless you have hundreds of controllers (which should be very rare), the current structure should be fine.

Are these action classes automatically mapped or do you need to include them in the controller class (by overriding the actions() function) ? (Could this directory structure be added to the "Definitive Guide" ?)

Do you have any suggestions on where widget action classes should be placed (classes like CCaptchaAction) ?

Thanks

NZ

Yes, you have to override actions() to indicate which classes to be used.

There is not strict rule on where widget classes should be placed under. I tend to put them under /protected/components, but you should adapt it according to your need.

Quote

How many controllers do you have for all your subapplications? Did you attempt to write individual action class files ?

I guess I'm not quite certain how many controllers i should have. When I was initially tinkering, I had set my controllers up to be a BrowseController and an EditController, and had an action for each model object i wanted to be browsed or edited, but it didn't take long to get catastrophically unwieldy - I've got at least fifty objects in the model that need to be browseable or editable or viewable, or whatever-able, and that number is only going to grow as the application takes on new functionality. So I thought it might be better if I inverted it, and had an ArtistController with a browse action, an edit action and a view action. It's much neater this way, I have more controllers but the code is easier to navigate and the purposes feel more cleanly delineated.

The issue I'm having is that I don't really want to have actions for the public site mixed in to the same controllers as the console. I can already expect at least fifty controllers for the console given the way I've been organising them, and then the different public site sections can expect many more. This is why I want to organise it with subfolders - just to give me the flexibility to organise where my classes reside.

How could I go about organising the code inside of the constraints placed on me by the framework?

Hey

I am pretty much in the same boat, I was also looking at CWebApplication->createController method and pondering if a different implementation would be better suited basically I prefer a structure like



protected/


  controllers/


    user/


       UserBrowseController.php


       UserEditController.php


        model/


              UserModel.php


        views/


              UserView.php


         actions/


                EditAction.php


                DeleteAction.php              


So every folder is self containing MVC + Actions are organized as one. The trick of course is in the request interpretation if you make a request like r=user.UserBrowser/ you would see a failure saying the requested controller "user.userBrowser" could not be found simply because the ID portion is not a word. But if we changed the CWebApplication->createController method to



	public function createController($id)


	{


		if($id==='')


			$id=$this->defaultController;


        


        $ids = explode(".",$id);


        $id = $ids[count($ids)-1];


        unset($ids[count($ids)-1]);


        $idpath = implode(DIRECTORY_SEPARATOR,$ids);


        


		if(preg_match('/^w+$/',$id))


		{


			if(isset($this->controllerMap[$id]))


				return CConfiguration::createObject($this->controllerMap[$id],$id);


			else


				return $this->createControllerIn($this->getControllerPath().DIRECTORY_SEPARATOR.$idpath,ucfirst($id).'Controller',$id);


		}


	}





Then we can have our controllers in folders nested as deep as we like, the views will also be okay as long as we render them without the "/". Using a "." for the directory folder separator should prevent any hacking attempts.

Of course this is pretty much what you suggested initially :D

Thoughts ?

NZ

I like this approach to have Controllers and Views on the same directory… To be honnest is boring moving from Controllers and Views folders.

I think different people have different tastes. It's difficult to come up with a structure that everyone would like. The point is that the framework should allow customization of the structure to fit for personal taste.

In order to support subdirectories of controllers, you need to override CWebApplication::createController(). And in order to move "views" under the controller directory, you need to override CController::getViewPath().

Your example is actually not quite convincing the necessity of controller subdirectory. Why you would split UserBrowseController from UserEditController? In general, the convention is that each controller represents actions that can be applied to a kind of object. So a UserController is sufficient.

Quote

Quote

How many controllers do you have for all your subapplications? Did you attempt to write individual action class files ?

I guess I'm not quite certain how many controllers i should have. When I was initially tinkering, I had set my controllers up to be a BrowseController and an EditController, and had an action for each model object i wanted to be browsed or edited, but it didn't take long to get catastrophically unwieldy - I've got at least fifty objects in the model that need to be browseable or editable or viewable, or whatever-able, and that number is only going to grow as the application takes on new functionality. So I thought it might be better if I inverted it, and had an ArtistController with a browse action, an edit action and a view action. It's much neater this way, I have more controllers but the code is easier to navigate and the purposes feel more cleanly delineated.

The issue I'm having is that I don't really want to have actions for the public site mixed in to the same controllers as the console. I can already expect at least fifty controllers for the console given the way I've been organising them, and then the different public site sections can expect many more. This is why I want to organise it with subfolders - just to give me the flexibility to organise where my classes reside.

How could I go about organising the code inside of the constraints placed on me by the framework?

If you don't want to mix two applications together, perhaps you should consider using different "protected" folders. You can still reuse classes shared by the two applications by defining appropriate path aliases.

Humm… so, for my company project, i will have the following to each application:

myproject\

|

±- app1.protected\

|

±- app2.protected\

|

±- app3.protected\

I like it. But how to share controllers between app's ? A example of configuration and use of this will be nice.

You can considering declaring those shared controllers by configuring CWebApplication.controllerMap.

Or you can put those shared controllers in a central place and extend them in separate sub-applications.

Any better ideas?

How are the controllers structured for "the definitive guide to yii"?`

The guide only uses one controller with one CViewAction.

How is it all structured? Will the guide eventually be packaged with the distribution as a demo? I learned a lot from tinkering with the quickstart demo that came with PRADO.

As I said, the Guide app itself is very simple. Without URL rewriting, the URL would look like:

/index.php?r=doc/guide&view=basics.first-app

where "basics.first-app" refers to a view file located at: /protected/views/doc/guide/basics/first-app.php

I'm not going to release the Guide application as a demo. Instead, I will re-format the Guide using markdown and make it public available.

Quote

You can considering declaring those shared controllers by configuring CWebApplication.controllerMap.

Or you can put those shared controllers in a central place and extend them in separate sub-applications.

Any better ideas?

Sry for bring this thread up, but how do i configure controllerPath to tell to yiic where i want to create my controller ? Yiic try to create every controller in protected/controller but i want to create 1 controller in app1.protected and another one inside app2.protected.

That depends on how you start "yiic shell". This command requires you to provide the bootstrap file of your application.

So operations to create crud, model … can only be executed using shell ? Or it's possible to use something like:

yiic model User

instead of

YiiRoot/framework/yiic shell WebRoot/testdrive/index.php





>> model user

If the only way is using shell, problem solved :)