views:

48

answers:

3

So, I'm not quite sure how I should structure this in CakePHP to work correctly in the proper MVC form.

Let's, for argument sake, say I have the following data structure which are related in various ways:

  • Team
  • Task
  • Equipment

This is generally how sites are and is quite easy to structure and make in Cake. For example, I would have the a model, controller and view for each item set.

My problem (and I'm sure countless others have had it and already solved it) is that I have a level above the item sets. So, for example:

  • Department
    • Team
    • Task
    • Equipment
  • Department
    • Team
    • Task
    • Equipment
  • Department
    • Team
    • Task
    • Equipment

In my site, I need the ability for someone to view the site at an individual group level as well as move to view it all together (ie, ignore the groups).

So, I have models, views and controls for Depart, Team, Task and Equipment.

How do I structure my site so that from the Department view, someone can select a Department then move around the site to the different views for Team/Task/Equipment showing only those that belong to that particular Department.

In this same format, is there a way to also move around ignoring the department associations?

Hopefully the following example URLs clarifies anything that was unclear:

// View items while disregarding which group-set record they belong to
http://www.example.com/Team/action/id
http://www.example.com/Task/action/id
http://www.example.com/Equipment/action/id

http://www.example.com/Departments

// View items as if only those associated with the selected group-set record exist
http://www.example.com/Department/HR/Team/action/id
http://www.example.com/Department/HR/Task/action/id
http://www.example.com/Department/HR/Equipment/action/id

Can I get the controllers to function in this manner? Is there someone to read so I can figure this out?

Thanks to those that read all this :)

A: 

SESSIONS Since web is stateless, you will need to use sessions (or cookies). The question you will need to ask yourself is how to reflect the selection (or not) of a specific department. It could be as simple as putting a drop down selection in the upper right that reflects ALL, HR, Sales, etc. When the drop down changes, it will set (or clear) the Group session variable.

As for the functionality in the controllers, you just check for the Session. If it is there, you limit the data by the select group. So you would use the same URLs, but the controller or model would manage how the data gets displayed.

// for all functionality use:
http://www.example.com/Team/action/id
http://www.example.com/Task/action/id
http://www.example.com/Equipment/action/id

You don't change the URL to accommodate for the functionality. That would be like using a different URL for every USER wanting to see their ADDRESS, PHONE NUMBER, or BILLING INFO. Where USER would be the group and ADDRESS, PHONE NUMBER< and BILLING INFO would be the item sets.

WITHOUT SESSIONS

The other option would be to put the Group filter on each page. So for example on Team/index view you would have a group drop down to filter the data. It would accomplish the same thing without having to set and clear session variables.

The conclusion is and the key thing to remember is that the functionality does not change nor does the URLs. The only thing that changes is that you will be working with filtered data sets.

Does that make sense?

cdburgess
Yup, makes sense, but I don't think it really works with this situation. In department example provided, each department would have an address they could type in to access just they department stuff (such as /department/HR). This means sessions are out since the user needs to be able to come to the site directly into a department centric view. As for the without sessions, a drop down filter on each page isn't appropriate. It should appear as if it is it's own page, unassociated with the other departments.
Dan McGrath
So can HR people see the SALES people stuff? Or is the data mutually exclusive to the department the user belongs in?
cdburgess
This will be for a public site, everything will be viewable (apart from the admin side, of course). when someone clicks on a "group" everything should appear as if it is its own self-contained site. There will be no public sign-in, etc.
Dan McGrath
Maybe one way to conceptual think about is: Think of the site not as a square grid of data (columns [team,tasks,equipments] and row[records]), but as a cube. (as per the square, but with the department dimension). So when I view the data as a it could be just from the department/records face of the cube, just the tasks/records or picking out a single point of line of results with department/tasks/records.From what I can tell, Cake doesn't appear to be able to handle this cleanly. Hope I haven't lost you with this. (ps, thanks for the effort)
Dan McGrath
+1  A: 

If this is a public site, you may want to consider using named variables. This will allow you to define the group on the URL still, but without additional functionality requirements.

http://example.com/team/group:hr http://example.com/team/action/group:hr/other:var

It may require custom routes too... but it should do the job.

http://book.cakephp.org/view/541/Named-parameters http://book.cakephp.org/view/542/Defining-Routes

cdburgess
Thanks it might be an acceptable method, I'll have a dig at it tonight.
Dan McGrath
A: 

I think I know what you're trying to do. Correct me if I'm wrong:

I built a project manager for myself in which I wanted the URLs to be more logical, so instead of using something like

http://domain.com/project/milestones/add/MyProjectName I could use http://domain.com/project/MyProjectName/milestones/add

I added a custom route to the end (!important) of my routes so that it catches anything that's not already a route and treats it as a "variable route".

Router::connect('/project/:project/:controller/:action/*', array(), array('project' => '[a-zA-Z0-9\-]+'));

Whatever route you put means that you can't already (or ever) have a controller by that name, for that reason I consider it a good practice to use a singular word instead of a plural. (I have a Projects Controller, so I use "project" to avoid conflicting with it.)

Now, to access the :project parameter anywhere in my app, I use this function in my AppController:

function __currentProject(){
// Finding the current Project's Info
    if(isset($this->params['project'])){
        App::import('Model', 'Project');
        $projectNames = new Project;
        $projectNames->contain();
        $projectInfo = $projectNames->find('first', array('conditions' => array('Project.slug' => $this->params['project'])));
        $project_id = $projectInfo['Project']['id'];
        $this->set('project_name_for_layout', $projectInfo['Project']['name']);
        return $project_id;
    }
}

And I utilize it in my other controllers:

function overview(){
    $this->layout = 'project';

    // Getting currentProject id from App Controller
    $project_id = parent::__currentProject();

    // Finding out what time it is and performing queries based on time.
    $nowStamp = time();

    $nowDate = date('Y-m-d H:i:s' , $nowStamp);
    $twoWeeksFromNow = $nowDate + 1209600;

    $lateMilestones = $this->Project->Milestone->find('all', array('conditions'=>array('Milestone.project_id' => $project_id, 'Milestone.complete'=> 0, 'Milestone.duedate <'=> $nowDate)));
    $this->set(compact('lateMilestones'));

    $currentProject = $this->Project->find('all', array('conditions'=>array('Project.slug' => $this->params['project'])));
    $this->set(compact('currentProject'));
}

For your project you can try using a route like this at the end of your routes.php file:

Router::connect('/:groupname/:controller/:action/*', array(), array('groupname' => '[a-zA-Z0-9\-]+'));

// Notice I removed "/project" from the beginning. If you put the :groupname first, as I've done in the last example, then you only have one option for these custom url routes.

Then modify the other code to your needs.

Marshall Thompson