Elgg  Version master
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 
28  public const RELATIONSHIP_COLUMN_LENGTH = 255;
29 
30  public const TABLE_NAME = 'entity_relationships';
31 
32  public const DEFAULT_JOIN_ALIAS = 'r';
33 
42  public function __construct(
43  protected Database $db,
44  protected EntityTable $entities,
45  protected MetadataTable $metadata,
46  protected EventsService $events
47  ) {
48  }
49 
57  public function get(int $id): ?\ElggRelationship {
58  $select = Select::fromTable(self::TABLE_NAME);
59  $select->select('*')
60  ->where($select->compare('id', '=', $id, ELGG_VALUE_ID));
61 
62  return $this->db->getDataRow($select, [$this, 'rowToElggRelationship']) ?: null;
63  }
64 
73  public function delete(int $id, bool $call_event = true): bool {
74  $relationship = $this->get($id);
75  if (!$relationship instanceof \ElggRelationship) {
76  return false;
77  }
78 
79  if ($call_event && !$this->events->trigger('delete', 'relationship', $relationship)) {
80  return false;
81  }
82 
83  $delete = Delete::fromTable(self::TABLE_NAME);
84  $delete->where($delete->compare('id', '=', $id, ELGG_VALUE_ID));
85 
86  return (bool) $this->db->deleteData($delete);
87  }
88 
103  public function add(int $guid_one, string $relationship, int $guid_two, bool $return_id = false): bool|int {
104  if (strlen($relationship) > self::RELATIONSHIP_COLUMN_LENGTH) {
105  throw new LengthException('Relationship name cannot be longer than ' . self::RELATIONSHIP_COLUMN_LENGTH);
106  }
107 
108  // Check for duplicates
109  // note: escape $relationship after this call, we don't want to double-escape
110  if ($this->check($guid_one, $relationship, $guid_two)) {
111  return false;
112  }
113 
114  // Check if the related entities exist
115  if (!$this->entities->exists($guid_one) || !$this->entities->exists($guid_two)) {
116  // one or both of the guids doesn't exist
117  return false;
118  }
119 
120  $insert = Insert::intoTable(self::TABLE_NAME);
121  $insert->values([
122  'guid_one' => $insert->param($guid_one, ELGG_VALUE_GUID),
123  'relationship' => $insert->param($relationship, ELGG_VALUE_STRING),
124  'guid_two' => $insert->param($guid_two, ELGG_VALUE_GUID),
125  'time_created' => $insert->param($this->getCurrentTime()->getTimestamp(), ELGG_VALUE_TIMESTAMP),
126  ]);
127 
128  try {
129  $id = $this->db->insertData($insert);
130  if (!$id) {
131  return false;
132  }
133  } catch (DatabaseException $e) {
134  $prev = $e->getPrevious();
135  if ($prev instanceof UniqueConstraintViolationException) {
136  // duplicate key error see https://github.com/Elgg/Elgg/issues/9179
137  return false;
138  }
139 
140  throw $e;
141  }
142 
143  $obj = $this->get($id);
144 
145  $result = $this->events->trigger('create', 'relationship', $obj);
146  if (!$result) {
147  $this->delete($id, false);
148  return false;
149  }
150 
151  return $return_id ? $obj->id : true;
152  }
153 
165  public function check(int $guid_one, string $relationship, int $guid_two) {
166  $select = Select::fromTable(self::TABLE_NAME);
167  $select->select('*')
168  ->where($select->compare('guid_one', '=', $guid_one, ELGG_VALUE_GUID))
169  ->andWhere($select->compare('relationship', '=', $relationship, ELGG_VALUE_STRING))
170  ->andWhere($select->compare('guid_two', '=', $guid_two, ELGG_VALUE_GUID))
171  ->setMaxResults(1);
172 
173  $row = $this->db->getDataRow($select, [$this, 'rowToElggRelationship']);
174  return $row instanceof \ElggRelationship ? $row : false;
175  }
176 
188  public function remove(int $guid_one, string $relationship, int $guid_two): bool {
189  $obj = $this->check($guid_one, $relationship, $guid_two);
190  if (!$obj instanceof \ElggRelationship) {
191  return false;
192  }
193 
194  return $this->delete($obj->id);
195  }
196 
209  public function removeAll(int $guid, string $relationship = '', bool $inverse_relationship = false, string $type = '', bool $trigger_events = true): bool {
210  if ($trigger_events) {
211  return $this->removeAllWithEvents($guid, $relationship, $inverse_relationship, $type);
212  }
213 
214  return $this->removeAllWithoutEvents($guid, $relationship, $inverse_relationship, $type);
215  }
216 
230  protected function removeAllWithoutEvents(int $guid, string $relationship = '', bool $inverse_relationship = false, string $type = ''): bool {
231  $delete = Delete::fromTable(self::TABLE_NAME);
232 
233  if ($inverse_relationship) {
234  $delete->where($delete->compare('guid_two', '=', $guid, ELGG_VALUE_GUID));
235  } else {
236  $delete->where($delete->compare('guid_one', '=', $guid, ELGG_VALUE_GUID));
237  }
238 
239  if (!empty($relationship)) {
240  $delete->andWhere($delete->compare('relationship', '=', $relationship, ELGG_VALUE_STRING));
241  }
242 
243  if (!empty($type)) {
244  $entity_sub = $delete->subquery(EntityTable::TABLE_NAME);
245  $entity_sub->select('guid')
246  ->where($delete->compare('type', '=', $type, ELGG_VALUE_STRING));
247 
248  if (!$inverse_relationship) {
249  $delete->andWhere($delete->compare('guid_two', 'in', $entity_sub->getSQL()));
250  } else {
251  $delete->andWhere($delete->compare('guid_one', 'in', $entity_sub->getSQL()));
252  }
253  }
254 
255  $this->db->deleteData($delete);
256 
257  return true;
258  }
259 
273  protected function removeAllWithEvents(int $guid, string $relationship = '', bool $inverse_relationship = false, string $type = ''): bool {
274  $select = Select::fromTable(self::TABLE_NAME);
275  $select->select('*');
276 
277  if ($inverse_relationship) {
278  $select->where($select->compare('guid_two', '=', $guid, ELGG_VALUE_GUID));
279  } else {
280  $select->where($select->compare('guid_one', '=', $guid, ELGG_VALUE_GUID));
281  }
282 
283  if (!empty($relationship)) {
284  $select->andWhere($select->compare('relationship', '=', $relationship, ELGG_VALUE_STRING));
285  }
286 
287  if (!empty($type)) {
288  $entity_sub = $select->subquery(EntityTable::TABLE_NAME);
289  $entity_sub->select('guid')
290  ->where($select->compare('type', '=', $type, ELGG_VALUE_STRING));
291 
292  if (!$inverse_relationship) {
293  $select->andWhere($select->compare('guid_two', 'in', $entity_sub->getSQL()));
294  } else {
295  $select->andWhere($select->compare('guid_one', 'in', $entity_sub->getSQL()));
296  }
297  }
298 
299  $remove_ids = [];
300 
301  $relationships = $this->db->getData($select, [$this, 'rowToElggRelationship']);
302 
303  /* @var $rel \ElggRelationship */
304  foreach ($relationships as $rel) {
305  if (!$this->events->trigger('delete', 'relationship', $rel)) {
306  continue;
307  }
308 
309  $remove_ids[] = $rel->id;
310  }
311 
312  // to prevent MySQL query length issues
313  $chunks = array_chunk($remove_ids, 250);
314  foreach ($chunks as $chunk) {
315  if (empty($chunk)) {
316  continue;
317  }
318 
319  $delete = Delete::fromTable(self::TABLE_NAME);
320  $delete->where($delete->compare('id', 'in', $chunk));
321 
322  $this->db->deleteData($delete);
323  }
324 
325  return true;
326  }
327 
337  public function getEntitiesFromCount(array $options = []) {
338  $options['selects'][] = new SelectClause('COUNT(' . EntityTable::DEFAULT_JOIN_ALIAS . '.guid) AS total');
339  $options['group_by'][] = new GroupByClause(self::DEFAULT_JOIN_ALIAS . '.guid_two');
340  $options['order_by'][] = new OrderByClause('total', 'desc');
341 
342  return Entities::find($options);
343  }
344 
352  public function rowToElggRelationship(\stdClass $row): \ElggRelationship {
353  return new \ElggRelationship($row);
354  }
355 }
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.
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.
__construct(protected Database $db, protected EntityTable $entities, protected MetadataTable $metadata, protected EventsService $events)
Constructor.
getCurrentTime($modifier= '')
Get the (cloned) time.
Definition: TimeUsing.php:25
if($who_can_change_language=== 'nobody') elseif($who_can_change_language=== 'admin_only'&&!elgg_is_admin_logged_in()) $options
Definition: language.php:20
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.
foreach($recommendedExtensions as $extension) if(empty(ini_get('session.gc_probability'))||empty(ini_get('session.gc_divisor'))) $db
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...
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
$metadata
Output annotation metadata.
Definition: metadata.php:9
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