views:

548

answers:

3

I'm moving some of my find code inside models.

Previously in my controller I had

$this->Book->Review->find('first', array(
    'conditions' => array(
        'Review.book_id' => $id,
        'Review.user_id' => $this->Auth->user('id')
    )
));

so in my Review model I put something like

function own($id) {
    $this->contain();
    $review = $this->find('first', array(
        'conditions' => array(
            'Review.book_id' => $id,
            'Review.user_id' => AuthComponent::user('id')
        )
    ));
    return $review;
}

So I'm calling AuthComponent statically from the Model. I know I can do this for the method AuthComponent::password(), which is useful for validation. But I'm getting errors using the method AuthComponent::user(), in particular

Fatal error: Call to a member function check() on a non-object in /var/www/MathOnline/cake/libs/controller/components/auth.php on line 663

Is there a way to get the info about the currently logged user from a model?

A: 

Dirtiest way would be to just access the user information in the Session. Least amount of overhead associated with that.

The "proper" way would probably be to instantiate the AuthComponent object, so that it does all the stuff it needs to be fully operational. Much like a death star, the AuthComponent doesn't really work well when not fully setup.

To get a new AC object, in the model:

App::import( 'Component', 'Auth' );
$this->Auth = new AuthComponent();

Now you can use $this->Auth in the model, same as you would in the controller.

Travis Leleu
Notice (8): Undefined property: AuthComponent::$Session [CORE/cake/libs/controller/components/auth.php, line 663]Fatal error: Call to a member function check() on a non-object in /var/www/MathOnline/cake/libs/controller/components/auth.php on line 663
Andrea
Instantiating models yourself, especially Controllers or Components, is tricky business. Better use `ClassRegistry::getInstance()` or `ClassRegistry::init()` (off the top of my head).
deceze
@deceze would you recommend using those functions (instead) always rather than App::import? I've used App::import for models inside AppController quite a few times, never had any probs. But I'm willing to change if you think it's a better way of doing it -- do you recommend it?
Travis Leleu
The thing is, `App::import()` just `imports` the .php file which contains the Class, it doesn't instantiate anything. Some Classes depend on certain arguments to their constructor, Components for example expect a reference to a Controller. The best way to construct classes correctly is to just get an already existing instance from the ClassRegistry (saves memory, too). Otherwise, leave it to the ClassRegistry to instantiate the Class (not sure if it does it for Components that easily).
deceze
+2  A: 

There is a nice solution by Matt Curry. You store the data of the current logged user in the app_controller using the beforeFilter callback and access it later using static calls. A description can be found here: http://www.pseudocoder.com/archives/2008/10/06/accessing-user-sessions-from-models-or-anywhere-in-cakephp-revealed/

harpax
Thank you, this seems the best solution!
Andrea
+1  A: 

I think the code is fine as it is and belongs in the Controller, or at the very least it needs to receive the ids from the Controller and not try to get them itself. The Model should only be concerned with fetching data from a data store and returning it. It must not be concerned with how the data is handled in the rest of the application or where the parameters to its request are coming from. Otherwise you paint yourself into a corner where the ReviewModel can only retrieve data for logged in users, which might not always be what you want.

As such, I'd use a function signature like this:

function findByBookAndUserId($book_id, $user_id) {
    …
}

$this->Review->findByBookAndUserId($id, $this->Auth->user('id'));
deceze
I have many request that only fetch data linked to the current user. Just trying to simplify the code without having to pass the user id everywhere.
Andrea
Just to add a few examples, by having your model method expect a controller (with session data), you can no longer easily use that model method in shells or test cases (in which there is no controller or session).
deizel
@deizel Good point, too. This move can create unnecessary dependencies just for the sake of saving ~20 characters of code...
deceze