Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
When related result doesn't exist ..
  • Hi guys, I'm running into a problem with the ORM package, in the case that a related result doesn't exist. Let me explain. I have "users" and "projects". A user can start a project and is thus the owner of the project. That led me to make the following models:
    class Model_User extends Orm\Model {
     static $_has_many = array( 'projects' );    
    }
    
    class Model_Project extends Orm\Model {
     static $_belongs_to = array( 'user' );
    }
    

    Now I want to find the projects owned by a certain user by using:
    $projects = Model_User::find($user_id)->projects;
    

    This works find in the case there is a matching user_id in the projects table. But in the case the user hasn't created a project yet (i.e. no row with that user_id exists yet in the projects table), this results in an error: ErrorException [ Error ]: Call to a member function to_array() on a non-object
    PKGPATH/orm/classes/model.php @ line 1162 Am I doing something wrong here or is this a bug?
  • It's a known bug in RC2(.1), has already been fixed in the repo and will be in RC3 which should be released tonight (EST).
  • Ok, awesome! I'll await the RC3 release then. Just to know: what would be the returned result in a case like this? When no matching results can be found?
  • Actually I should have been more complete. Your code, directly chaining from the find() method isn't really good practice. find($id) retuns null when nothing is found, which would throw an error in your code. I'd suggest first assigning the object and checking if it's really there. Now onto why the problem arose: you were probably passing the project objects to a view and when anything gets passed to a view output encoding ensues. Which used to need to_array() in the case of models to function, that had some serious problems regretably though as you noticed as there was a bug in to_array().
  • Ok I see what you mean. Thanks for the clarification. With respect to the best-practice, just to make sure. The recommended method would be something like this:
    $user = Model_User::find()->where('id', $user_id) ;
    if($user)
    {
      $projects = $user->related('projects')->get_one();
      if($projects)
      {
        // return the project details
      }
      else {
       // return false
      }
    }
    

    Correct?
  • No, not at all, there's a lot wrong with that. I'll just rewrite it to something that will work:
    $user = Model_User::find()->where('id', $user_id)->related('projects')->get_one();
    if ( ! empty($user))
    {
     return $user->projects; // can also be an empty array
    }
    else
    {
     return false; // $user_id was not found
    }
    
    Now if you're completely, 100% sure, no user could ever change the URL or $_POST values for another $user_id input. Then you don't need this, in all other cases you do. In your code the $user object will be an Orm\Query object and thus always be true. Which is why you need to retrieve the object first before you check if it exists. If it doesn't exist I made it return false, otherwise it returns the projects array (which could possibly still be empty, but is valid).
  • Hi Jelmer, Thanks for your feedback. This makes thing clearer. One thing I'm not really getting yet is how to return only one specific project based on a project-id, but off course for the specific user. Where would the where()-statement go in that case. Also, I was wondering if it's a valid approach to define the user in a base controller, as such:
    $user = Auth::instance()->get_user_id(); 
    $this->user =  Model_User::find()->where('id', $user[1]) ;
    

    And then use this user object for further queries in an extended class or specific funtions:
    if ( ! empty($this->user))
    { 
       return $this->user->related('projects')->get_one();
      
    } 
    else
    {
      return false; // $user_id was not found
    }
    

    Edited the code above, since it didn't work. Now it works (well, not really the way I wanted to), but I don't know if this again is not according to the best practices. Thanks in advance for your feedback! :)
  • Take a look at this line:
    $this->user = Model_User::find()->where('id', $user[1]); At which point do you expect it to execute and fetch the user from the DB? To translate what it says to normal english: "I want you to find a user with the id $user[1] and..." but you don't say to go and do that. Which is why you must add either ->get() to get all or ->get_one() to get a specific object. You make the same mistake again by assigning a non-finished query (there's no get/get_one at the end) to the $user variable. If you check if that's empty it will always return false because it isn't empty: it has the non finished query assigned to it. Once you have the Model_User object you can just request the relations on it whether you got them already or not. When you didn't fetch them already (eager-load them) they will be queried from the DB on demand (lazy-loaded). Just try my code instead of yours and it will work, because all you did in your reply was taking my code and made the exact same mistake as you made before a second time. That and read the docs, that's what they're for.
  • Ok Jelmer, I now got the point that you we're making before, you always have to execute and fetch the result first. Clear. What isn't clear to me yet is how I can do a subsequent query on the Model_User object. For example when I don't want all projects for this user, but when I want to check if a specific user has access to a project (based on the project-id). Then I'd like to do a where-query or something on the projects, right? As for the docs: yes I'm reading them extensively, and trying my best to understand everything, but it's difficult at times ..

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion