Elgg  Version 3.0
MetadataTable.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
6 use Elgg\Database;
10 use ElggMetadata;
12 
21 
22  use TimeUsing;
23 
27  protected $metadata_cache;
28 
32  protected $db;
33 
37  protected $events;
38 
42  protected $tag_names = [];
43 
44  const MYSQL_TEXT_BYTE_LIMIT = 65535;
45 
53  public function __construct(
55  Database $db,
57  ) {
58  $this->metadata_cache = $metadata_cache;
59  $this->db = $db;
60  $this->events = $events;
61  }
62 
70  public function registerTagName($name) {
71  if (!in_array($name, $this->tag_names)) {
72  $this->tag_names[] = $name;
73  }
74 
75  return true;
76  }
77 
85  public function unregisterTagName($name) {
86  $index = array_search($name, $this->tag_names);
87  if ($index >= 0) {
88  unset($this->tag_names[$index]);
89 
90  return true;
91  }
92 
93  return false;
94  }
95 
101  public function getTagNames() {
102  return $this->tag_names;
103  }
104 
124  public function getTags(array $options = []) {
125  $defaults = [
126  'threshold' => 1,
127  'tag_names' => [],
128  ];
129 
130  $options = array_merge($defaults, $options);
131 
132  $singulars = ['tag_name'];
133  $options = LegacyQueryOptionsAdapter::normalizePluralOptions($options, $singulars);
134 
135  $tag_names = elgg_extract('tag_names', $options);
136  if (empty($tag_names)) {
138  }
139 
140  $threshold = elgg_extract('threshold', $options, 1, false);
141 
142  unset($options['tag_names']);
143  unset($options['threshold']);
144 
145  // custom selects
146  $options['selects'] = [
147  function(QueryBuilder $qb, $main_alias) {
148  return "{$main_alias}.value AS tag";
149  },
150  function(QueryBuilder $qb, $main_alias) {
151  return "COUNT({$main_alias}.id) AS total";
152  },
153  ];
154 
155  // additional wheres
156  $wheres = (array) elgg_extract('wheres', $options, []);
157  $wheres[] = function(QueryBuilder $qb, $main_alias) use ($tag_names) {
158  return $qb->compare("{$main_alias}.name", 'in', $tag_names, ELGG_VALUE_STRING);
159  };
160  $wheres[] = function(QueryBuilder $qb, $main_alias) {
161  return $qb->compare("{$main_alias}.value", '!=', '', ELGG_VALUE_STRING);
162  };
163  $options['wheres'] = $wheres;
164 
165  // custom group by
166  $options['group_by'] = [
167  function(QueryBuilder $qb, $main_alias) {
168  return "{$main_alias}.value";
169  },
170  ];
171 
172  // having
173  $having = (array) elgg_extract('having', $options, []);
174  $having[] = function(QueryBuilder $qb, $main_alias) use ($threshold) {
175  return $qb->compare('total', '>=', $threshold, ELGG_VALUE_INTEGER);
176  };
177  $options['having'] = $having;
178 
179  // order by
180  $options['order_by'] = [
181  new OrderByClause('total', 'desc'),
182  ];
183 
184  // custom callback
185  $options['callback'] = function($row) {
186  $result = new \stdClass();
187  $result->tag = $row->tag;
188  $result->total = (int) $row->total;
189 
190  return $result;
191  };
192 
193  return $this->getAll($options);
194  }
195 
206  public function get($id) {
207  $qb = Select::fromTable('metadata');
208  $qb->select('*');
209 
210  $where = new MetadataWhereClause();
211  $where->ids = $id;
212  $qb->addClause($where);
213 
214  $row = $this->db->getDataRow($qb);
215  if (!empty($row)) {
216  return new ElggMetadata($row);
217  }
218 
219  return false;
220  }
221 
230  public function delete(ElggMetadata $metadata) {
231  if (!$metadata->id) {
232  return false;
233  }
234 
235  if (!_elgg_services()->events->trigger('delete', 'metadata', $metadata)) {
236  return false;
237  }
238 
239  $qb = Delete::fromTable('metadata');
240  $qb->where($qb->compare('id', '=', $metadata->id, ELGG_VALUE_INTEGER));
241 
242  $deleted = $this->db->deleteData($qb);
243 
244  if ($deleted) {
245  $this->metadata_cache->clear($metadata->entity_guid);
246  }
247 
248  return $deleted !== false;
249  }
250 
263  public function create(ElggMetadata $metadata, $allow_multiple = false) {
264  if (!isset($metadata->value) || !isset($metadata->entity_guid)) {
265  elgg_log("Metadata must have a value and entity guid", 'ERROR');
266  return false;
267  }
268 
269  if (!is_scalar($metadata->value)) {
270  elgg_log("To set multiple metadata values use ElggEntity::setMetadata", 'ERROR');
271  return false;
272  }
273 
274  if ($metadata->id) {
275  if ($this->update($metadata)) {
276  return $metadata->id;
277  }
278  }
279 
280  if (strlen($metadata->value) > self::MYSQL_TEXT_BYTE_LIMIT) {
281  elgg_log("Metadata '{$metadata->name}' is above the MySQL TEXT size limit and may be truncated.", 'WARNING');
282  }
283 
284  if (!$allow_multiple) {
285  $id = $this->getIdsByName($metadata->entity_guid, $metadata->name);
286 
287  if (is_array($id)) {
288  throw new \LogicException("
289  Multiple '{$metadata->name}' metadata values exist for entity [guid: {$metadata->entity_guid}].
290  Use ElggEntity::setMetadata()
291  ");
292  }
293 
294  if ($id > 0) {
295  $metadata->id = $id;
296 
297  if ($this->update($metadata)) {
298  return $metadata->id;
299  }
300  }
301  }
302 
303  if (!$this->events->triggerBefore('create', 'metadata', $metadata)) {
304  return false;
305  }
306 
307  $time_created = $this->getCurrentTime()->getTimestamp();
308 
309  $qb = Insert::intoTable('metadata');
310  $qb->values([
311  'name' => $qb->param($metadata->name, ELGG_VALUE_STRING),
312  'entity_guid' => $qb->param($metadata->entity_guid, ELGG_VALUE_INTEGER),
313  'value' => $qb->param($metadata->value, $metadata->value_type === 'integer' ? ELGG_VALUE_INTEGER : ELGG_VALUE_STRING),
314  'value_type' => $qb->param($metadata->value_type, ELGG_VALUE_STRING),
315  'time_created' => $qb->param($time_created, ELGG_VALUE_INTEGER),
316  ]);
317 
318  $id = $this->db->insertData($qb);
319 
320  if ($id === false) {
321  return false;
322  }
323 
324  $metadata->id = (int) $id;
325  $metadata->time_created = $time_created;
326 
327  if ($this->events->trigger('create', 'metadata', $metadata)) {
328  $this->metadata_cache->clear($metadata->entity_guid);
329 
330  $this->events->triggerAfter('create', 'metadata', $metadata);
331 
332  return $id;
333  } else {
334  $this->delete($metadata);
335 
336  return false;
337  }
338  }
339 
348  public function update(ElggMetadata $metadata) {
349 
350  if (!$this->events->triggerBefore('update', 'metadata', $metadata)) {
351  return false;
352  }
353 
354  if (strlen($metadata->value) > self::MYSQL_TEXT_BYTE_LIMIT) {
355  elgg_log("Metadata '{$metadata->name}' is above the MySQL TEXT size limit and may be truncated.", 'WARNING');
356  }
357 
358  $qb = Update::table('metadata');
359  $qb->set('name', $qb->param($metadata->name, ELGG_VALUE_STRING))
360  ->set('value', $qb->param($metadata->value, $metadata->value_type === 'integer' ? ELGG_VALUE_INTEGER : ELGG_VALUE_STRING))
361  ->set('value_type', $qb->param($metadata->value_type, ELGG_VALUE_STRING))
362  ->where($qb->compare('id', '=', $metadata->id, ELGG_VALUE_INTEGER));
363 
364  $result = $this->db->updateData($qb);
365 
366  if ($result === false) {
367  return false;
368  }
369 
370  $this->metadata_cache->clear($metadata->entity_guid);
371 
372  $this->events->trigger('update', 'metadata', $metadata);
373  $this->events->triggerAfter('update', 'metadata', $metadata);
374 
375  return true;
376  }
377 
389  public function getAll(array $options = []) {
390 
391  $options['metastring_type'] = 'metadata';
392  $options = LegacyQueryOptionsAdapter::normalizeMetastringOptions($options);
393 
394  return Metadata::find($options);
395  }
396 
408  public function getRowsForGuids(array $guids) {
409 
410  $qb = Select::fromTable('metadata');
411  $qb->select('*')
412  ->where($qb->compare('entity_guid', 'IN', $guids, ELGG_VALUE_GUID))
413  ->orderBy('entity_guid', 'asc')
414  ->orderBy('time_created', 'asc')
415  ->orderBy('id', 'asc');
416 
417  return $qb->execute()->fetchAll();
418  }
419 
434  public function deleteAll(array $options) {
435  if (!_elgg_is_valid_options_for_batch_operation($options, 'metadata')) {
436  return false;
437  }
438 
439  // This moved last in case an object's constructor sets metadata. Currently the batch
440  // delete process has to create the entity to delete its metadata. See #5214
441  $this->metadata_cache->invalidateByOptions($options);
442 
443  $options['batch'] = true;
444  $options['batch_size'] = 50;
445  $options['batch_inc_offset'] = false;
446 
447  $metadata = Metadata::find($options);
448  $count = $metadata->count();
449 
450  if (!$count) {
451  return;
452  }
453 
454  $success = 0;
455  /* @var $md \ElggMetadata */
456  foreach ($metadata as $md) {
457  if ($md->delete()) {
458  $success++;
459  }
460  }
461 
462  return $success == $count;
463  }
464 
473  public function getIdsByName($entity_guid, $name) {
474  if ($this->metadata_cache->isLoaded($entity_guid)) {
475  $ids = $this->metadata_cache->getSingleId($entity_guid, $name);
476  } else {
477  $qb = Select::fromTable('metadata');
478  $qb->select('id')
479  ->where($qb->compare('entity_guid', '=', $entity_guid, ELGG_VALUE_INTEGER))
480  ->andWhere($qb->compare('name', '=', $name, ELGG_VALUE_STRING));
481 
482  $callback = function (\stdClass $row) {
483  return (int) $row->id;
484  };
485 
486  $ids = $this->db->getData($qb, $callback);
487  }
488 
489  if (empty($ids)) {
490  return null;
491  }
492 
493  if (is_array($ids) && count($ids) === 1) {
494  return array_shift($ids);
495  }
496 
497  return $ids;
498  }
499 }
$deleted
Definition: delete.php:25
elgg_get_registered_tag_metadata_names()
Returns an array of valid metadata names for tags.
Definition: tags.php:90
if(!$user||!$user->canDelete()) $name
Definition: delete.php:22
_elgg_is_valid_options_for_batch_operation($options, $type)
Checks if there are some constraints on the options array for potentially dangerous operations...
Definition: elgglib.php:1401
update(ElggMetadata $metadata)
Update a specific piece of metadata.
unregisterTagName($name)
Unregisters a metadata tag name.
static find(array $options=[])
Build and execute a new query from an array of legacy options.
Definition: Repository.php:85
getAll(array $options=[])
Returns metadata.
static table($table, $alias=null)
{}
Definition: Update.php:13
$defaults
const ELGG_VALUE_INTEGER
Value types.
Definition: constants.php:138
const ELGG_VALUE_GUID
Definition: constants.php:140
Database abstraction query builder.
getCurrentTime($modifier= '')
Get the (cloned) time.
Definition: TimeUsing.php:27
$options
Elgg admin footer.
Definition: footer.php:6
$entity_guid
Definition: save.php:9
getIdsByName($entity_guid, $name)
Returns ID(s) of metadata with a particular name attached to an entity.
$metadata
Outputs object metadata $vars[&#39;metadata&#39;] Metadata/menu $vars[&#39;show_entity_menu&#39;] Show the entity m...
Definition: metadata.php:10
deleteAll(array $options)
Deletes metadata based on $options.
static intoTable($table)
{}
Definition: Insert.php:13
$id
River item delete action.
Definition: delete.php:6
elgg_log($message, $level=\Psr\Log\LogLevel::NOTICE)
Log a message.
Definition: elgglib.php:786
__construct(MetadataCache $metadata_cache, Database $db, Events $events)
Constructor.
$time_created
Definition: online.php:19
compare($x, $comparison, $y=null, $type=null, $case_sensitive=null)
Build value comparison clause.
registerTagName($name)
Registers a metadata name as containing tags for an entity.
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1131
Extends QueryBuilder with ORDER BY clauses.
static fromTable($table, $alias=null)
{}
Definition: Select.php:13
getTags(array $options=[])
Get popular tags and their frequencies.
$guids
Activates all specified installed and inactive plugins.
Definition: activate_all.php:9
const ELGG_VALUE_STRING
Definition: constants.php:139
In memory cache of known metadata values stored by entity.
if(elgg_in_context('widget')) $count
Definition: pagination.php:21
getTagNames()
Returns an array of valid metadata names for tags.
_elgg_services()
Get the global service provider.
Definition: elgglib.php:1292
$index
Definition: gallery.php:47
trait TimeUsing
Adds methods for setting the current time (for testing)
Definition: TimeUsing.php:12
create(ElggMetadata $metadata, $allow_multiple=false)
Create a new metadata object, or update an existing one (if multiple is allowed)
Builds clauses for filtering entities by properties in metadata table.
static fromTable($table, $alias=null)
{}
Definition: Delete.php:13
This class interfaces with the database to perform CRUD operations on metadata.
getRowsForGuids(array $guids)
Returns metadata rows.