private function collection_from_response($type, $res, $parent_attrs)
 {
     # check if JSON response root node contains a property (either plural or singular) for what we're trying to fetch
     # if found, reassign $res to this property and continue creating collection
     $singular_type = strtolower(str_replace('Roomstyler', '', $type));
     $plural_type = parent::to_plural($singular_type);
     $out = [];
     $errors = [];
     $status = $res['status'];
     # some results have an 'errors' hash, others have a single 'error' hash
     # this is an attempt to gather errors in a consistent way
     if (isset($res['errors'])) {
         if (is_array($res['errors'])) {
             $errors = $res['errors'];
         } else {
             if (is_object($res['errors'])) {
                 $errors = get_object_vars($res['errors']);
             } else {
                 if (is_string($res['errors'])) {
                     $errors[] = $res['errors'];
                 }
             }
         }
     }
     if (isset($res['error'])) {
         array_push($errors, $res['error']);
     }
     # wrap errors in RoomstylerError object, if an array is returned, we don't want
     # to create a new instance with the same errors.
     $errors = new RoomstylerError($errors, ['http_status' => $status, 'custom_http_errors_for' => $singular_type]);
     $res = $res['body'];
     # some results have a singular name root node, others may have a plural root node
     # this is another attempt to make the returned results consistent
     if (is_object($res)) {
         if (property_exists($res, $plural_type)) {
             $res = $res->{$plural_type};
         } else {
             if (property_exists($res, $singular_type)) {
                 $res = $res->{$singular_type};
             }
         }
     }
     # SearchMeta request is different since it fetches multiple nested resources
     # therefore we wrap the nested resources within the class instead
     if ($singular_type == 'searchmeta') {
         return new RoomstylerSearchMeta($res, $errors, $status);
     }
     # if result is an array then we want to return an array of wrapped objects
     if (is_array($res)) {
         # if the count is only one, there's probably a root node wrapping the data
         if (count($res) == 1) {
             $out = new $type(array_shift($res), $this->_settings, $this->_whitelabeled, $errors, $status, $parent_attrs);
         } else {
             foreach ($res as $_ => $obj) {
                 $out[] = new $type($obj, $this->_settings, $this->_whitelabeled, $errors, $status, $parent_attrs);
             }
         }
     } else {
         $out = new $type($res, $this->_settings, $this->_whitelabeled, $errors, $status, $parent_attrs);
     }
     return $out;
 }
 public function __get($prop)
 {
     switch ($prop) {
         case 'wl':
             $this->_whitelabeled = true;
             $this->set_user_agent();
             $out = $this;
             break;
         case 'user':
             $out = $this->_current_user;
             break;
         case 'editor':
             $out = new RoomstylerEditor($this->_settings, $this->_whitelabeled);
             break;
         default:
             # no scope, no authentication
             $class_name = parent::method_class_name($prop);
             $out = new $class_name($this->_settings, $this->_whitelabeled);
             break;
     }
     if ($prop != 'wl') {
         $this->_whitelabeled = false;
         $this->set_user_agent();
     }
     return $out;
 }