In CakePHP, if I want to check for something from a particular model on every page, where does the logic go?

StackOverflow https://stackoverflow.com/questions/23620334

  •  21-07-2023
  •  | 
  •  

Question

Simple enough problem, but seemingly infinite possibilities. Which one is true to MVC?

Let's say I have some logic that checks for new (unread) messages from the Message model. Assume that the logic is more than a few lines long and wouldn't be DRY to repeat. I want to check for new messages on every single page load, and also from a particular action in the MessageController. My assumption is it will be called in the beforeFilter of AppController... but the question is where does the actual logic for searching go?

Possible solutions:

  • Does it belong in the MessageModel?
  • Perhaps the UserModel since it's specific to the current user?
  • The MessageController?
  • A component?
  • In the AppController?
  • Somewhere I haven't thought of?
Was it helpful?

Solution

I have a similar scenario in a project I have worked on where we had to check on every page for something similar to your messages. I put the logic into the relevant model (e.g. Message model for messages), and called a function from that model (which would gather and sort the data) from the beforeFilter, which would then set it to the view to be accessible anywhere.

I haven't found anywhere outside of this scenario where I've needed it to be used anywhere else as this data can then be accessed from the view (because beforeFilter sets it) and from any controller (because it's a model) that uses that model.

With regards to your question about whether it should belong to a User or a Message, it boils down (to me) that it is a message, so the logic for getting messages should belong to the Message model. Of course, your User presumably hasMany Messages, so you would give those two models that association, and could create a wrapper function in your User model which would get the data from Message, or rely on the model associations to get that data without the need to make a custom query.

Example:

  • User model hasMany Messages
  • beforeFilter of AppController or relevant controllers calls something like $this->User->getMessages($user_id); or $this->Messages->getByUserID($user_id);
  • beforeFilter sets the variable to be accessible in the view

Now all views can access the messages, and of course, you can call that function manually from another action whenever.

Problems may occur if you decide you need to access that data from another model. Workarounds include a temporary association to the Message model without a foreign key so you can query it as you would in a controller, but true MVC structure would probably suggest that the message data would be passed in to the other model's function.

OTHER TIPS

I would create a component to keep the code clean

I would put the logic in the Message Model and then call something like

function initialize(&$controller, $settings = array()) {
    $Message = ClassRegistry::init('Message');
    $messages = $Message->listByUser($user_id);
}

in the component initialize method

so the $messages would be available only in the controllers that actually use the component.

you could also make a more granular configuration deciding wich action you want to list the messages and wich not avoiding queries that you don't need.

public $components = array(
    'CheckMessages' => array('avoid_actions' => array('edit', 'admin_index'))
);

and in your component

function initialize(&$controller, $settings = array()) {
    $action = $controller->request->params['action'];
    if(isset($action) && in_array($action, $this->settings['avoid_actions']))
    {
        $messages =  array();
    }
    else
    {
        $Message = ClassRegistry::init('Message');
        $messages = $Message->listByUser($user_id);
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top