Elgg  Version 5.1
RelationshipsTable.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
6 use Elgg\Database;
14 
22 
23  use TimeUsing;
24 
29 
30  protected Database $db;
31 
33 
35 
37 
46  public function __construct(Database $db, EntityTable $entities, MetadataTable $metadata, EventsService $events) {
47  $this->db = $db;
48  $this->entities = $entities;
49  $this->metadata = $metadata;
50  $this->events = $events;
51  }
52 
60  public function get(int $id): ?\ElggRelationship {
61  $select = Select::fromTable('entity_relationships');
62  $select->select('*')
63  ->where($select->compare('id', '=', $id, ELGG_VALUE_ID));
64 
65  return $this->db->getDataRow($select, [$this, 'rowToElggRelationship']) ?: null;
66  }
67 
76  public function delete(int $id, bool $call_event = true): bool {
77  $relationship = $this->get($id);
78  if (!$relationship instanceof \ElggRelationship) {
79  return false;
80  }
81 
82  if ($call_event && !$this->events->trigger('delete', 'relationship', $relationship)) {
83  return false;
84  }
85 
86  $delete = Delete::fromTable('entity_relationships');
87  $delete->where($delete->compare('id', '=', $id, ELGG_VALUE_ID));
88 
89  return (bool) $this->db->deleteData($delete);
90  }
91 
106  public function add(int $guid_one, string $relationship, int $guid_two, bool $return_id = false): bool|int {
107  if (strlen($relationship) > self::RELATIONSHIP_COLUMN_LENGTH) {
108  throw new LengthException('Relationship name cannot be longer than ' . self::RELATIONSHIP_COLUMN_LENGTH);
109  }
110 
111  // Check for duplicates
112  // note: escape $relationship after this call, we don't want to double-escape
113  if ($this->check($guid_one, $relationship, $guid_two)) {
114  return false;
115  }
116 
117  // Check if the related entities exist
118  if (!$this->entities->exists($guid_one) || !$this->entities->exists($guid_two)) {
119  // one or both of the guids doesn't exist
120  return false;
121  }
122 
123  $insert = Insert::intoTable('entity_relationships');
124  $insert->values([
125  'guid_one' => $insert->param($guid_one, ELGG_VALUE_GUID),
126  'relationship' => $insert->param($relationship, ELGG_VALUE_STRING),
127  'guid_two' => $insert->param($guid_two, ELGG_VALUE_GUID),
128  'time_created' => $insert->param($this->getCurrentTime()->getTimestamp(), ELGG_VALUE_TIMESTAMP),
129  ]);
130 
131  try {
132  $id = $this->db->insertData($insert);
133  if (!$id) {
134  return false;
135  }
136  } catch (DatabaseException $e) {
137  $prev = $e->getPrevious();
138  if ($prev instanceof UniqueConstraintViolationException) {
139  // duplicate key error see https://github.com/Elgg/Elgg/issues/9179
140  return false;
141  }
142 
143  throw $e;
144  }
145 
146  $obj = $this->get($id);
147 
148  $result = $this->events->trigger('create', 'relationship', $obj);
149  if (!$result) {
150  $this->delete($id, false);
151  return false;
152  }
153 
154  return $return_id ? $obj->id : true;
155  }
156 
168  public function check(int $guid_one, string $relationship, int $guid_two) {
169  $select = Select::fromTable('entity_relationships');
170  $select->select('*')
171  ->where($select->compare('guid_one', '=', $guid_one, ELGG_VALUE_GUID))
172  ->andWhere($select->compare('relationship', '=', $relationship, ELGG_VALUE_STRING))
173  ->andWhere($select->compare('guid_two', '=', $guid_two, ELGG_VALUE_GUID))
174  ->setMaxResults(1);
175 
176  $row = $this->db->getDataRow($select, [$this, 'rowToElggRelationship']);
177  return $row instanceof \ElggRelationship ? $row : false;
178  }
179 
191  public function remove(int $guid_one, string $relationship, int $guid_two): bool {
192  $obj = $this->check($guid_one, $relationship, $guid_two);
193  if (!$obj instanceof \ElggRelationship) {
194  return false;
195  }
196 
197  return $this->delete($obj->id);
198  }
199 
212  public function removeAll(int $guid, string $relationship = '', bool $inverse_relationship = false, string $type = '', bool $trigger_events = true): bool {
213  if ($trigger_events) {
214  return $this->removeAllWithEvents($guid, $relationship, $inverse_relationship, $type);
215  }
216 
217  return $this->removeAllWithoutEvents($guid, $relationship, $inverse_relationship, $type);
218  }
219 
233  protected function removeAllWithoutEvents(int $guid, string $relationship = '', bool $inverse_relationship = false, string $type = ''): bool {
234  $delete = Delete::fromTable('entity_relationships');
235 
236  if ($inverse_relationship) {
237  $delete->where($delete->compare('guid_two', '=', $guid, ELGG_VALUE_GUID));
238  } else {
239  $delete->where($delete->compare('guid_one', '=', $guid, ELGG_VALUE_GUID));
240  }
241 
242  if (!empty($relationship)) {
243  $delete->andWhere($delete->compare('relationship', '=', $relationship, ELGG_VALUE_STRING));
244  }
245 
246  if (!empty($type)) {
247  $entity_sub = $delete->subquery('entities');
248  $entity_sub->select('guid')
249  ->where($delete->compare('type', '=', $type, ELGG_VALUE_STRING));
250 
251  if (!$inverse_relationship) {
252  $delete->andWhere($delete->compare('guid_two', 'in', $entity_sub->getSQL()));
253  } else {
254  $delete->andWhere($delete->compare('guid_one', 'in', $entity_sub->getSQL()));
255  }
256  }
257 
258  $this->db->deleteData($delete);
259 
260  return true;
261  }
262 
276  protected function removeAllWithEvents(int $guid, string $relationship = '', bool $inverse_relationship = false, string $type = ''): bool {
277  $select = Select::fromTable('entity_relationships');
278  $select->select('*');
279 
280  if ($inverse_relationship) {
281  $select->where($select->compare('guid_two', '=', $guid, ELGG_VALUE_GUID));
282  } else {
283  $select->where($select->compare('guid_one', '=', $guid, ELGG_VALUE_GUID));
284  }
285 
286  if (!empty($relationship)) {
287  $select->andWhere($select->compare('relationship', '=', $relationship, ELGG_VALUE_STRING));
288  }
289 
290  if (!empty($type)) {
291  $entity_sub = $select->subquery('entities');
292  $entity_sub->select('guid')
293  ->where($select->compare('type', '=', $type, ELGG_VALUE_STRING));
294 
295  if (!$inverse_relationship) {
296  $select->andWhere($select->compare('guid_two', 'in', $entity_sub->getSQL()));
297  } else {
298  $select->andWhere($select->compare('guid_one', 'in', $entity_sub->getSQL()));
299  }
300  }
301 
302  $remove_ids = [];
303 
304  $relationships = $this->db->getData($select, [$this, 'rowToElggRelationship']);
305 
306  /* @var $rel \ElggRelationship */
307  foreach ($relationships as $rel) {
308  if (!$this->events->trigger('delete', 'relationship', $rel)) {
309  continue;
310  }
311 
312  $remove_ids[] = $rel->id;
313  }
314 
315  // to prevent MySQL query length issues
316  $chunks = array_chunk($remove_ids, 250);
317  foreach ($chunks as $chunk) {
318  if (empty($chunk)) {
319  continue;
320  }
321 
322  $delete = Delete::fromTable('entity_relationships');
323  $delete->where($delete->compare('id', 'in', $chunk));
324 
325  $this->db->deleteData($delete);
326  }
327 
328  return true;
329  }
330 
340  public function getEntitiesFromCount(array $options = []) {
341  $options['selects'][] = new SelectClause('COUNT(e.guid) AS total');
342  $options['group_by'][] = new GroupByClause('r.guid_two');
343  $options['order_by'][] = new OrderByClause('total', 'desc');
344 
345  return Entities::find($options);
346  }
347 
355  public function rowToElggRelationship(\stdClass $row): \ElggRelationship {
356  return new \ElggRelationship($row);
357  }
358 }
static find(array $options=[])
Build and execute a new query from an array of legacy options.
Definition: Repository.php:110
The Elgg database.
Definition: Database.php:25
removeAllWithoutEvents(int $guid, string $relationship= '', bool $inverse_relationship=false, string $type= '')
Removes all relationships originating from a particular entity.
$relationship
Elgg default relationship view.
Definition: default.php:10
const ELGG_VALUE_GUID
Definition: constants.php:113
Events service.
Extends QueryBuilder with SELECT clauses.
$delete
$type
Definition: delete.php:22
const ELGG_VALUE_ID
Definition: constants.php:114
Extends QueryBuilder with GROUP BY statements.
trait TimeUsing
Adds methods for setting the current time (for testing)
Definition: TimeUsing.php:10
check(int $guid_one, string $relationship, int $guid_two)
Check if a relationship exists between two entities.
$options
Elgg admin footer.
Definition: footer.php:6
removeAllWithEvents(int $guid, string $relationship= '', bool $inverse_relationship=false, string $type= '')
Removes all relationships originating from a particular entity.
Exception thrown if a length is invalid.
getCurrentTime($modifier= '')
Get the (cloned) time.
Definition: TimeUsing.php:25
static intoTable($table)
{}
Definition: Insert.php:13
removeAll(int $guid, string $relationship= '', bool $inverse_relationship=false, string $type= '', bool $trigger_events=true)
Removes all relationships originating from a particular entity.
Relationships table database service.
A generic parent class for database exceptions.
rowToElggRelationship(\stdClass $row)
Convert a database row to a new .
getEntitiesFromCount(array $options=[])
Gets the number of entities by a the number of entities related to them in a particular way...
__construct(Database $db, EntityTable $entities, MetadataTable $metadata, EventsService $events)
Constructor.
Extends QueryBuilder with ORDER BY clauses.
const ELGG_VALUE_TIMESTAMP
Definition: constants.php:115
static fromTable($table, $alias=null)
{}
Definition: Select.php:13
const ELGG_VALUE_STRING
Definition: constants.php:112
add(int $guid_one, string $relationship, int $guid_two, bool $return_id=false)
Create a relationship between two entities.
$id
Generic annotation delete action.
Definition: delete.php:6
static fromTable($table, $alias=null)
{}
Definition: Delete.php:13
This class interfaces with the database to perform CRUD operations on metadata.
Entity table database service.
Definition: EntityTable.php:26
$guid
Reset an ElggUpgrade.
Definition: reset.php:6