cache = new DbStatementCache($dbConn); } public function getFields(?iterable $fieldValueInfos = null): iterable { $hasFieldValueInfos = $fieldValueInfos !== null; if($hasFieldValueInfos) { if(!is_array($fieldValueInfos)) $fieldValueInfos = iterator_to_array($fieldValueInfos); if(empty($fieldValueInfos)) return []; } $query = 'SELECT field_id, field_order, field_key, field_title, field_regex FROM msz_profile_fields'; if($hasFieldValueInfos) $query .= sprintf(' WHERE field_id IN (%s)', DbTools::prepareListString($fieldValueInfos)); $query .= ' ORDER BY field_order ASC'; $stmt = $this->cache->get($query); $args = 0; if($hasFieldValueInfos) foreach($fieldValueInfos as $fieldValueInfo) { if(!($fieldValueInfo instanceof ProfileFieldValueInfo)) throw new InvalidArgumentException('All values in $fieldValueInfos must be of ProfileFieldValueInfo type.'); $stmt->addParameter(++$args, $fieldValueInfo->getFieldId()); } $stmt->execute(); return $stmt->getResult()->getIterator(ProfileFieldInfo::fromResult(...)); } public function getField(string $fieldId): ProfileFieldInfo { $stmt = $this->cache->get('SELECT field_id, field_order, field_key, field_title, field_regex FROM msz_profile_fields WHERE field_id = ?'); $stmt->addParameter(1, $fieldId); $stmt->execute(); $result = $stmt->getResult(); if(!$result->next()) throw new RuntimeException('No field found with the provided field id.'); return ProfileFieldInfo::fromResult($result); } public function getFieldFormats( ?iterable $fieldInfos = null, ?iterable $fieldValueInfos = null ): iterable { $hasFieldInfos = $fieldInfos !== null; $hasFieldValueInfos = $fieldValueInfos !== null; if($hasFieldInfos) { if(!is_array($fieldInfos)) $fieldInfos = iterator_to_array($fieldInfos); if(empty($fieldInfos)) return []; } if($hasFieldValueInfos) { if(!is_array($fieldValueInfos)) $fieldValueInfos = iterator_to_array($fieldValueInfos); if(empty($fieldValueInfos)) return []; } $args = 0; $query = 'SELECT format_id, field_id, format_regex, format_link, format_display FROM msz_profile_fields_formats'; if($hasFieldInfos) { ++$args; $query .= sprintf(' WHERE field_id IN (%s)', DbTools::prepareListString($fieldInfos)); } if($hasFieldValueInfos) $query .= sprintf(' %s format_id IN (%s)', (++$args > 1 ? 'AND' : 'WHERE'), DbTools::prepareListString($fieldValueInfos) ); $stmt = $this->cache->get($query); $args = 0; if($hasFieldInfos) foreach($fieldInfos as $fieldInfo) { if(!($fieldInfo instanceof ProfileFieldInfo)) throw new InvalidArgumentException('All values in $fieldInfos must be of ProfileFieldInfo type.'); $stmt->addParameter(++$args, $fieldInfo->getId()); } if($hasFieldValueInfos) foreach($fieldValueInfos as $fieldValueInfo) { if(!($fieldValueInfo instanceof ProfileFieldValueInfo)) throw new InvalidArgumentException('All values in $fieldValueInfos must be of ProfileFieldValueInfo type.'); $stmt->addParameter(++$args, $fieldValueInfo->getFormatId()); } $stmt->execute(); return $stmt->getResult()->getIterator(ProfileFieldFormatInfo::fromResult(...)); } public function getFieldFormat(string $formatId): ProfileFieldFormatInfo { $stmt = $this->cache->get('SELECT format_id, field_id, format_regex, format_link, format_display FROM msz_profile_fields_formats WHERE format_id = ?'); $stmt->addParameter(1, $formatId); $stmt->execute(); $result = $stmt->getResult(); if(!$result->next()) throw new RuntimeException('No format found with the provided format id.'); return ProfileFieldFormatInfo::fromResult($result); } public function selectFieldFormat( ProfileFieldInfo|string $fieldInfo, string $value ): ProfileFieldFormatInfo { if($fieldInfo instanceof ProfileFieldInfo) $fieldInfo = $fieldInfo->getId(); $stmt = $this->cache->get('SELECT format_id, field_id, format_regex, format_link, format_display FROM msz_profile_fields_formats WHERE field_id = ? AND (format_regex IS NULL OR ? REGEXP format_regex) ORDER BY format_regex IS NULL ASC'); $stmt->addParameter(1, $fieldInfo); $stmt->addParameter(2, $value); $stmt->execute(); $result = $stmt->getResult(); if(!$result->next()) throw new RuntimeException('Could not determine an appropriate format for this field (missing default formatting)'); return ProfileFieldFormatInfo::fromResult($result); } public function getFieldValues(UserInfo|string $userInfo): iterable { if($userInfo instanceof UserInfo) $userInfo = $userInfo->getId(); // i don't really want to bother with the join for the ordering so i'll just do that somewhere in PHP for now // will probably add the ability for people to order them in whatever way they want, as well as visibility controls $stmt = $this->cache->get('SELECT field_id, user_id, format_id, field_value FROM msz_profile_fields_values WHERE user_id = ?'); $stmt->addParameter(1, $userInfo); $stmt->execute(); return $stmt->getResult()->getIterator(ProfileFieldValueInfo::fromResult(...)); } public function getFieldValue( ProfileFieldInfo|string $fieldInfo, UserInfo|string $userInfo ): ProfileFieldValueInfo { if($fieldInfo instanceof ProfileFieldInfo) $fieldInfo = $fieldInfo->getId(); if($userInfo instanceof UserInfo) $userInfo = $userInfo->getId(); $stmt = $this->cache->get('SELECT field_id, user_id, format_id, field_value FROM msz_profile_fields_values WHERE field_id = ? AND user_id = ?'); $stmt->addParameter(1, $fieldInfo); $stmt->addParameter(2, $userInfo); $stmt->execute(); $result = $stmt->getResult(); if(!$result->next()) throw new RuntimeException('No value for this field and user combination found.'); return ProfileFieldValueInfo::fromResult($result); } public function setFieldValues( UserInfo|string $userInfo, ProfileFieldInfo|string|array $fieldInfos, string|array $values ): void { if(empty($fieldInfos)) return; if(!is_array($fieldInfos)) { if(is_array($values)) throw new InvalidArgumentException('If $fieldInfos is not an array, $values may not be either'); $fieldInfos = [$fieldInfos]; $values = [$values]; } elseif(!is_array($values)) throw new InvalidArgumentException('If $fieldInfos is an array, $values must be as well.'); $fieldsCount = count($fieldInfos); if($fieldsCount !== count($values)) throw new InvalidArgumentException('$fieldsInfos and $values have the same amount of values and be in the same order.'); if($userInfo instanceof UserInfo) $userInfo = $userInfo->getId(); $rows = []; foreach($fieldInfos as $key => $fieldInfo) { if(is_string($fieldInfo)) $fieldInfo = $this->getField($fieldInfo); elseif(!($fieldInfo instanceof ProfileFieldInfo)) throw new InvalidArgumentException('Entries of $fieldInfos must either be field IDs or instances of ProfileFieldInfo.'); $value = $fieldInfo->matchValue($values[$key]); if($value === false) throw new InvalidArgumentException('One of the values in $values is not correct formatted.'); $rows[] = [ $fieldInfo->getId(), $this->selectFieldFormat($fieldInfo, $value)->getId(), $value, ]; } $args = 0; $stmt = $this->cache->get( 'REPLACE INTO msz_profile_fields_values (field_id, user_id, format_id, field_value) VALUES ' . DbTools::prepareListString($rows, '(?, ?, ?, ?)') ); foreach($rows as $row) { $stmt->addParameter(++$args, $row[0]); $stmt->addParameter(++$args, $userInfo); $stmt->addParameter(++$args, $row[1]); $stmt->addParameter(++$args, $row[2]); } $stmt->execute(); } public function removeFieldValues( UserInfo|string $userInfo, ProfileFieldInfo|string|array $fieldInfos ): void { if(empty($fieldInfos)) return; if($userInfo instanceof UserInfo) $userInfo = $userInfo->getId(); if(!is_array($fieldInfos)) $fieldInfos = [$fieldInfos]; foreach($fieldInfos as $key => $value) { if($value instanceof ProfileFieldInfo) $fieldInfos[$key] = $value->getId(); elseif(is_string($value)) throw new InvalidArgumentException('$fieldInfos array may only contain string IDs or instances of ProfileFieldInfo'); } $args = 0; $stmt = $this->cache->get(sprintf( 'DELETE FROM msz_profile_fields_values WHERE user_id = ? AND field_id IN (%s)', DbTools::prepareListString($fieldInfos) )); $stmt->addParameter(++$args, $userInfo); foreach($fieldInfos as $value) $stmt->addParameter(++$args, $value); $stmt->execute(); } }