Overview

Packages

  • ClipIt
    • clipit
      • api
    • urjc
      • backend
  • Elgg
    • Core
      • Access
      • Authentication
      • Cache
      • Caches
      • Core
      • DataMode
        • Site
      • DataModel
        • Annotations
        • Entities
        • Extender
        • File
        • Importable
        • Loggable
        • Notable
        • Object
        • User
      • DataStorage
      • Exception
      • Exceptions
        • Stub
      • FileStore
        • Disk
      • Groups
      • Helpers
      • HMAC
      • Memcache
      • Metadata
      • Navigation
      • ODD
      • Output
      • Plugins
        • Settings
      • Sessions
      • SocialModel
        • Friendable
        • Locatable
      • WebServicesAPI
      • Widgets
      • XML
      • XMLRPC
    • Exceptions
      • Stub
  • None
  • PHP

Classes

  • ElggAttributeLoader
  • ElggBatch
  • ElggData
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * Loads ElggEntity attributes from DB or validates those passed in via constructor
  5:  *
  6:  * @access private
  7:  * 
  8:  * @package    Elgg.Core
  9:  * @subpackage DataModel
 10:  */
 11: class ElggAttributeLoader {
 12: 
 13:     /**
 14:      * @var array names of attributes in all entities
 15:      */
 16:     protected static $primary_attr_names = array(
 17:         'guid',
 18:         'type',
 19:         'subtype',
 20:         'owner_guid',
 21:         'container_guid',
 22:         'site_guid',
 23:         'access_id',
 24:         'time_created',
 25:         'time_updated',
 26:         'last_action',
 27:         'enabled',
 28:     );
 29: 
 30:     /**
 31:      * @var array names of secondary attributes required for the entity
 32:      */
 33:     protected $secondary_attr_names = array();
 34: 
 35:     /**
 36:      * @var string entity type (not class) required for fetched primaries
 37:      */
 38:     protected $required_type;
 39: 
 40:     /**
 41:      * @var array
 42:      */
 43:     protected $initialized_attributes;
 44: 
 45:     /**
 46:      * @var string class of object being loaded
 47:      */
 48:     protected $class;
 49: 
 50:     /**
 51:      * @var bool should access control be considered when fetching entity?
 52:      */
 53:     public $requires_access_control = true;
 54: 
 55:     /**
 56:      * @var callable function used to load attributes from {prefix}entities table
 57:      */
 58:     public $primary_loader = 'get_entity_as_row';
 59: 
 60:     /**
 61:      * @var callable function used to load attributes from secondary table
 62:      */
 63:     public $secondary_loader = '';
 64: 
 65:     /**
 66:      * @var callable function used to load all necessary attributes
 67:      */
 68:     public $full_loader = '';
 69: 
 70:     /**
 71:      * Constructor
 72:      * 
 73:      * @param string $class             class of object being loaded
 74:      * @param string $required_type     entity type this is being used to populate
 75:      * @param array  $initialized_attrs attributes after initializeAttributes() has been run
 76:      * @throws InvalidArgumentException
 77:      */
 78:     public function __construct($class, $required_type, array $initialized_attrs) {
 79:         if (!is_string($class)) {
 80:             throw new InvalidArgumentException('$class must be a class name.');
 81:         }
 82:         $this->class = $class;
 83: 
 84:         if (!is_string($required_type)) {
 85:             throw new InvalidArgumentException('$requiredType must be a system entity type.');
 86:         }
 87:         $this->required_type = $required_type;
 88: 
 89:         $this->initialized_attributes = $initialized_attrs;
 90:         unset($initialized_attrs['tables_split'], $initialized_attrs['tables_loaded']);
 91:         $all_attr_names = array_keys($initialized_attrs);
 92:         $this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names);
 93:     }
 94: 
 95:     /**
 96:      * Get primary attributes missing that are missing
 97:      * 
 98:      * @param stdClass $row Database row
 99:      * @return array
100:      */
101:     protected function isMissingPrimaries($row) {
102:         return array_diff(self::$primary_attr_names, array_keys($row)) !== array();
103:     }
104: 
105:     /**
106:      * Get secondary attributes that are missing
107:      * 
108:      * @param stdClass $row Database row
109:      * @return array
110:      */
111:     protected function isMissingSecondaries($row) {
112:         return array_diff($this->secondary_attr_names, array_keys($row)) !== array();
113:     }
114: 
115:     /**
116:      * Check that the type is correct
117:      * 
118:      * @param stdClass $row Database row
119:      * @return void
120:      * @throws InvalidClassException
121:      */
122:     protected function checkType($row) {
123:         if ($row['type'] !== $this->required_type) {
124:             $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($row['guid'], $this->class));
125:             throw new InvalidClassException($msg);
126:         }
127:     }
128: 
129:     /**
130:      * Get all required attributes for the entity, validating any that are passed in. Returns empty array
131:      * if can't be loaded (Check $failure_reason).
132:      *
133:      * This function splits loading between "primary" attributes (those in {prefix}entities table) and
134:      * "secondary" attributes (e.g. those in {prefix}objects_entity), but can load all at once if a
135:      * combined loader is available.
136:      *
137:      * @param mixed $row a row loaded from DB (array or stdClass) or a GUID
138:      * @return array will be empty if failed to load all attributes (access control or entity doesn't exist)
139:      *
140:      * @throws InvalidArgumentException|LogicException|IncompleteEntityException
141:      */
142:     public function getRequiredAttributes($row) {
143:         if (!is_array($row) && !($row instanceof stdClass)) {
144:             // assume row is the GUID
145:             $row = array('guid' => $row);
146:         }
147:         $row = (array) $row;
148:         if (empty($row['guid'])) {
149:             throw new InvalidArgumentException('$row must be or contain a GUID');
150:         }
151: 
152:         // these must be present to support isFullyLoaded()
153:         foreach (array('tables_split', 'tables_loaded') as $key) {
154:             if (isset($this->initialized_attributes[$key])) {
155:                 $row[$key] = $this->initialized_attributes[$key];
156:             }
157:         }
158: 
159:         $was_missing_primaries = $this->isMissingPrimaries($row);
160:         $was_missing_secondaries = $this->isMissingSecondaries($row);
161: 
162:         // some types have a function to load all attributes at once, it should be faster
163:         if (($was_missing_primaries || $was_missing_secondaries) && is_callable($this->full_loader)) {
164:             $fetched = (array) call_user_func($this->full_loader, $row['guid']);
165:             if (!$fetched) {
166:                 return array();
167:             }
168:             $row = array_merge($row, $fetched);
169:             $this->checkType($row);
170:         } else {
171:             if ($was_missing_primaries) {
172:                 if (!is_callable($this->primary_loader)) {
173:                     throw new LogicException('Primary attribute loader must be callable');
174:                 }
175:                 if ($this->requires_access_control) {
176:                     $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
177:                 } else {
178:                     $ignoring_access = elgg_set_ignore_access();
179:                     $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
180:                     elgg_set_ignore_access($ignoring_access);
181:                 }
182:                 if (!$fetched) {
183:                     return array();
184:                 }
185:                 $row = array_merge($row, $fetched);
186:             }
187: 
188:             // We must test type before trying to load the secondaries so that InvalidClassException
189:             // gets thrown. Otherwise the secondary loader will fail and return false.
190:             $this->checkType($row);
191: 
192:             if ($was_missing_secondaries) {
193:                 if (!is_callable($this->secondary_loader)) {
194:                     throw new LogicException('Secondary attribute loader must be callable');
195:                 }
196:                 $fetched = (array) call_user_func($this->secondary_loader, $row['guid']);
197:                 if (!$fetched) {
198:                     if ($row['type'] === 'site') {
199:                         // A special case is needed for sites: When vanilla ElggEntities are created and
200:                         // saved, these are stored w/ type "site", but with no sites_entity row. These
201:                         // are probably only created in the unit tests.
202:                         // @todo Don't save vanilla ElggEntities with type "site"
203: 
204:                         $row = $this->filterAddedColumns($row);
205:                         $row['guid'] = (int) $row['guid'];
206:                         return $row;
207:                     }
208:                     throw new IncompleteEntityException("Secondary loader failed to return row for {$row['guid']}");
209:                 }
210:                 $row = array_merge($row, $fetched);
211:             }
212:         }
213: 
214:         $row = $this->filterAddedColumns($row);
215: 
216:         // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let
217:         // this pass so the upgrades can run.
218: 
219:         // guid needs to be an int  http://trac.elgg.org/ticket/4111
220:         $row['guid'] = (int) $row['guid'];
221: 
222:         return $row;
223:     }
224: 
225:     /**
226:      * Filter out keys returned by the query which should not appear in the entity's attributes
227:      *
228:      * @param array $row All columns from the query
229:      * @return array Columns acceptable for the entity's attributes
230:      */
231:     protected function filterAddedColumns($row) {
232:         // make an array with keys as acceptable attribute names
233:         $acceptable_attrs = self::$primary_attr_names;
234:         array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names);
235:         $acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs);
236: 
237:         // @todo remove these when #4584 is in place
238:         $acceptable_attrs['tables_split'] = true;
239:         $acceptable_attrs['tables_loaded'] = true;
240: 
241:         foreach ($row as $key => $val) {
242:             if (!isset($acceptable_attrs[$key])) {
243:                 unset($row[$key]);
244:             }
245:         }
246:         return $row;
247:     }
248: }
249: 
API documentation generated by ApiGen 2.8.0