continue
This commit is contained in:
+77
-366
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
// models/Book.php
|
||||
require_once __DIR__ . '/../includes/parsedown/ParsedownExtra.php';
|
||||
class Book {
|
||||
private $pdo;
|
||||
|
||||
@@ -41,11 +40,10 @@ class Book {
|
||||
public function create($data) {
|
||||
$share_token = bin2hex(random_bytes(16));
|
||||
$published = isset($data['published']) ? (int)$data['published'] : 0;
|
||||
$editor_type = $data['editor_type'] ?? 'markdown';
|
||||
|
||||
$stmt = $this->pdo->prepare("
|
||||
INSERT INTO books (title, description, genre, user_id, series_id, sort_order_in_series, share_token, published, editor_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO books (title, description, genre, user_id, series_id, sort_order_in_series, share_token, published)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
return $stmt->execute([
|
||||
$data['title'],
|
||||
@@ -55,14 +53,12 @@ class Book {
|
||||
$data['series_id'] ?? null,
|
||||
$data['sort_order_in_series'] ?? null,
|
||||
$share_token,
|
||||
$published,
|
||||
$editor_type
|
||||
$published
|
||||
]);
|
||||
}
|
||||
|
||||
public function update($id, $data) {
|
||||
$published = isset($data['published']) ? (int)$data['published'] : 0;
|
||||
$editor_type = $data['editor_type'] ?? 'markdown';
|
||||
|
||||
// Преобразуем пустые строки в NULL для integer полей
|
||||
$series_id = !empty($data['series_id']) ? (int)$data['series_id'] : null;
|
||||
@@ -70,7 +66,7 @@ class Book {
|
||||
|
||||
$stmt = $this->pdo->prepare("
|
||||
UPDATE books
|
||||
SET title = ?, description = ?, genre = ?, series_id = ?, sort_order_in_series = ?, published = ?, editor_type = ?
|
||||
SET title = ?, description = ?, genre = ?, series_id = ?, sort_order_in_series = ?, published = ?
|
||||
WHERE id = ? AND user_id = ?
|
||||
");
|
||||
return $stmt->execute([
|
||||
@@ -80,7 +76,6 @@ class Book {
|
||||
$series_id, // Теперь это либо integer, либо NULL
|
||||
$sort_order_in_series, // Теперь это либо integer, либо NULL
|
||||
$published,
|
||||
$editor_type,
|
||||
$id,
|
||||
$data['user_id']
|
||||
]);
|
||||
@@ -106,6 +101,39 @@ class Book {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteAllByUser($user_id) {
|
||||
try {
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
// Получаем ID всех книг пользователя
|
||||
$stmt = $this->pdo->prepare("SELECT id FROM books WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$book_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
if (empty($book_ids)) {
|
||||
$this->pdo->commit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Удаляем главы всех книг пользователя (одним запросом)
|
||||
$placeholders = implode(',', array_fill(0, count($book_ids), '?'));
|
||||
$stmt = $this->pdo->prepare("DELETE FROM chapters WHERE book_id IN ($placeholders)");
|
||||
$stmt->execute($book_ids);
|
||||
|
||||
// Удаляем все книги пользователя (одним запросом)
|
||||
$stmt = $this->pdo->prepare("DELETE FROM books WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
|
||||
$deleted_count = $stmt->rowCount();
|
||||
$this->pdo->commit();
|
||||
|
||||
return $deleted_count;
|
||||
} catch (Exception $e) {
|
||||
$this->pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function userOwnsBook($book_id, $user_id) {
|
||||
$stmt = $this->pdo->prepare("SELECT id FROM books WHERE id = ? AND user_id = ?");
|
||||
@@ -172,24 +200,6 @@ class Book {
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function reorderSeriesBooks($series_id, $new_order) {
|
||||
try {
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
foreach ($new_order as $order => $book_id) {
|
||||
$stmt = $this->pdo->prepare("UPDATE books SET sort_order_in_series = ? WHERE id = ? AND series_id = ?");
|
||||
$stmt->execute([$order + 1, $book_id, $series_id]);
|
||||
}
|
||||
|
||||
$this->pdo->commit();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getBookStats($book_id, $only_published_chapters = false) {
|
||||
$sql = "
|
||||
SELECT
|
||||
@@ -209,74 +219,49 @@ class Book {
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function convertChaptersContent($book_id, $from_editor, $to_editor) {
|
||||
try {
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
// Получаем все главы книги
|
||||
$chapters = $this->getAllChapters($book_id);
|
||||
|
||||
foreach ($chapters as $chapter) {
|
||||
$converted_content = $this->convertContent(
|
||||
$chapter['content'],
|
||||
$from_editor,
|
||||
$to_editor
|
||||
);
|
||||
|
||||
|
||||
private function getAllChapters($book_id) {
|
||||
$stmt = $this->pdo->prepare("SELECT id, content FROM chapters WHERE book_id = ?");
|
||||
$stmt->execute([$book_id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
private function updateChapterContent($chapter_id, $content) {
|
||||
$word_count = $this->countWords($content);
|
||||
$stmt = $this->pdo->prepare("
|
||||
UPDATE chapters
|
||||
SET content = ?, word_count = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
");
|
||||
return $stmt->execute([$content, $word_count, $chapter_id]);
|
||||
}
|
||||
|
||||
public function getBooksNotInSeries($user_id, $series_id = null) {
|
||||
$sql = "SELECT * FROM books WHERE user_id = ? AND (series_id IS NULL OR series_id = ?)";
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute([$user_id, $series_id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function reorderSeriesBooks($series_id, $new_order) {
|
||||
try {
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
// Обновляем контент главы
|
||||
$this->updateChapterContent($chapter['id'], $converted_content);
|
||||
foreach ($new_order as $order => $book_id) {
|
||||
$stmt = $this->pdo->prepare("UPDATE books SET sort_order_in_series = ? WHERE id = ? AND series_id = ?");
|
||||
$stmt->execute([$order + 1, $book_id, $series_id]);
|
||||
}
|
||||
|
||||
$this->pdo->commit();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$this->pdo->rollBack();
|
||||
error_log("Ошибка при обновлении порядка книг: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pdo->commit();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$this->pdo->rollBack();
|
||||
error_log("Error converting chapters: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function getAllChapters($book_id) {
|
||||
$stmt = $this->pdo->prepare("SELECT id, content FROM chapters WHERE book_id = ?");
|
||||
$stmt->execute([$book_id]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
private function updateChapterContent($chapter_id, $content) {
|
||||
$word_count = $this->countWords($content);
|
||||
$stmt = $this->pdo->prepare("
|
||||
UPDATE chapters
|
||||
SET content = ?, word_count = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
");
|
||||
return $stmt->execute([$content, $word_count, $chapter_id]);
|
||||
}
|
||||
|
||||
private function convertContent($content, $from_editor, $to_editor) {
|
||||
if ($from_editor === $to_editor) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../includes/parsedown/ParsedownExtra.php';
|
||||
|
||||
try {
|
||||
if ($from_editor === 'markdown' && $to_editor === 'html') {
|
||||
// Markdown to HTML
|
||||
$parsedown = new ParsedownExtra();
|
||||
return $parsedown->text($content);
|
||||
} elseif ($from_editor === 'html' && $to_editor === 'markdown') {
|
||||
// HTML to Markdown (упрощенная версия)
|
||||
return $this->htmlToMarkdown($content);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("Error converting content from {$from_editor} to {$to_editor}: " . $e->getMessage());
|
||||
return $content;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
private function countWords($text) {
|
||||
$text = strip_tags($text);
|
||||
$text = preg_replace('/[^\p{L}\p{N}\s]/u', ' ', $text);
|
||||
@@ -284,280 +269,6 @@ private function convertContent($content, $from_editor, $to_editor) {
|
||||
$words = array_filter($words);
|
||||
return count($words);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function markdownToHtmlWithParagraphs($markdown) {
|
||||
$parsedown = new ParsedownExtra();
|
||||
|
||||
// Включаем разметку строк для лучшей обработки абзацев
|
||||
$parsedown->setBreaksEnabled(true);
|
||||
|
||||
// Обрабатываем Markdown
|
||||
$html = $parsedown->text($markdown);
|
||||
|
||||
// Дополнительная обработка для обеспечения правильной структуры абзацев
|
||||
$html = $this->ensureParagraphStructure($html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function ensureParagraphStructure($html) {
|
||||
// Если HTML не содержит тегов абзацев или div'ов, оборачиваем в <p>
|
||||
if (!preg_match('/<(p|div|h[1-6]|blockquote|pre|ul|ol|li)/i', $html)) {
|
||||
// Разбиваем на строки и оборачиваем каждую непустую строку в <p>
|
||||
$lines = explode("\n", trim($html));
|
||||
$wrappedLines = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (!empty($line)) {
|
||||
// Пропускаем уже обернутые строки
|
||||
if (!preg_match('/^<[^>]+>/', $line) || preg_match('/^<(p|div|h[1-6])/i', $line)) {
|
||||
$wrappedLines[] = $line;
|
||||
} else {
|
||||
$wrappedLines[] = "<p>{$line}</p>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$html = implode("\n", $wrappedLines);
|
||||
}
|
||||
|
||||
// Убеждаемся, что теги правильно закрыты
|
||||
$html = $this->balanceTags($html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function balanceTags($html) {
|
||||
// Простая балансировка тегов - в реальном проекте лучше использовать DOMDocument
|
||||
$tags = [
|
||||
'p' => 0,
|
||||
'div' => 0,
|
||||
'span' => 0,
|
||||
'strong' => 0,
|
||||
'em' => 0,
|
||||
];
|
||||
|
||||
// Счетчик открывающих и закрывающих тегов
|
||||
foreach ($tags as $tag => &$count) {
|
||||
$open = substr_count($html, "<{$tag}>") + substr_count($html, "<{$tag} ");
|
||||
$close = substr_count($html, "</{$tag}>");
|
||||
$count = $open - $close;
|
||||
}
|
||||
|
||||
// Добавляем недостающие закрывающие теги
|
||||
foreach ($tags as $tag => $count) {
|
||||
if ($count > 0) {
|
||||
$html .= str_repeat("</{$tag}>", $count);
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
private function htmlToMarkdown($html) {
|
||||
// Сначала нормализуем HTML структуру
|
||||
$html = $this->normalizeHtml($html);
|
||||
|
||||
// Базовая конвертация HTML в Markdown
|
||||
$markdown = $html;
|
||||
|
||||
// 1. Сначала обрабатываем абзацы - заменяем на двойные переносы строк
|
||||
$markdown = preg_replace_callback('/<p[^>]*>(.*?)<\/p>/is', function($matches) {
|
||||
$content = trim($matches[1]);
|
||||
if (!empty($content)) {
|
||||
return $content . "\n\n";
|
||||
}
|
||||
return '';
|
||||
}, $markdown);
|
||||
|
||||
// 2. Обрабатываем разрывы строк
|
||||
$markdown = preg_replace('/<br[^>]*>\s*<\/br[^>]*>/i', "\n", $markdown);
|
||||
$markdown = preg_replace('/<br[^>]*>/i', " \n", $markdown); // Два пробела для Markdown разрыва
|
||||
|
||||
// 3. Заголовки
|
||||
$markdown = preg_replace('/<h1[^>]*>(.*?)<\/h1>/is', "# $1\n\n", $markdown);
|
||||
$markdown = preg_replace('/<h2[^>]*>(.*?)<\/h2>/is', "## $1\n\n", $markdown);
|
||||
$markdown = preg_replace('/<h3[^>]*>(.*?)<\/h3>/is', "### $1\n\n", $markdown);
|
||||
$markdown = preg_replace('/<h4[^>]*>(.*?)<\/h4>/is', "#### $1\n\n", $markdown);
|
||||
$markdown = preg_replace('/<h5[^>]*>(.*?)<\/h5>/is', "##### $1\n\n", $markdown);
|
||||
$markdown = preg_replace('/<h6[^>]*>(.*?)<\/h6>/is', "###### $1\n\n", $markdown);
|
||||
|
||||
// 4. Жирный текст
|
||||
$markdown = preg_replace('/<strong[^>]*>(.*?)<\/strong>/is', '**$1**', $markdown);
|
||||
$markdown = preg_replace('/<b[^>]*>(.*?)<\/b>/is', '**$1**', $markdown);
|
||||
|
||||
// 5. Курсив
|
||||
$markdown = preg_replace('/<em[^>]*>(.*?)<\/em>/is', '*$1*', $markdown);
|
||||
$markdown = preg_replace('/<i[^>]*>(.*?)<\/i>/is', '*$1*', $markdown);
|
||||
|
||||
// 6. Зачеркивание
|
||||
$markdown = preg_replace('/<s[^>]*>(.*?)<\/s>/is', '~~$1~~', $markdown);
|
||||
$markdown = preg_replace('/<strike[^>]*>(.*?)<\/strike>/is', '~~$1~~', $markdown);
|
||||
$markdown = preg_replace('/<del[^>]*>(.*?)<\/del>/is', '~~$1~~', $markdown);
|
||||
|
||||
// 7. Списки
|
||||
$markdown = preg_replace('/<li[^>]*>(.*?)<\/li>/is', "- $1\n", $markdown);
|
||||
|
||||
// Обработка вложенных списков
|
||||
$markdown = preg_replace('/<ul[^>]*>(.*?)<\/ul>/is', "\n$1\n", $markdown);
|
||||
$markdown = preg_replace('/<ol[^>]*>(.*?)<\/ol>/is', "\n$1\n", $markdown);
|
||||
|
||||
// 8. Блочные цитаты
|
||||
$markdown = preg_replace('/<blockquote[^>]*>(.*?)<\/blockquote>/is', "> $1\n\n", $markdown);
|
||||
|
||||
// 9. Код
|
||||
$markdown = preg_replace('/<code[^>]*>(.*?)<\/code>/is', '`$1`', $markdown);
|
||||
$markdown = preg_replace('/<pre[^>]*><code[^>]*>(.*?)<\/code><\/pre>/is', "```\n$1\n```", $markdown);
|
||||
$markdown = preg_replace('/<pre[^>]*>(.*?)<\/pre>/is', "```\n$1\n```", $markdown);
|
||||
|
||||
// 10. Ссылки
|
||||
$markdown = preg_replace('/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/is', '[$2]($1)', $markdown);
|
||||
|
||||
// 11. Изображения
|
||||
$markdown = preg_replace('/<img[^>]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*>/is', '', $markdown);
|
||||
|
||||
// 12. Таблицы
|
||||
$markdown = preg_replace_callback('/<table[^>]*>(.*?)<\/table>/is', function($matches) {
|
||||
$tableContent = $matches[1];
|
||||
// Простое преобразование таблицы в Markdown
|
||||
$tableContent = preg_replace('/<th[^>]*>(.*?)<\/th>/i', "| **$1** ", $tableContent);
|
||||
$tableContent = preg_replace('/<td[^>]*>(.*?)<\/td>/i', "| $1 ", $tableContent);
|
||||
$tableContent = preg_replace('/<tr[^>]*>(.*?)<\/tr>/i', "$1|\n", $tableContent);
|
||||
$tableContent = preg_replace('/<thead[^>]*>(.*?)<\/thead>/i', "$1", $tableContent);
|
||||
$tableContent = preg_replace('/<tbody[^>]*>(.*?)<\/tbody>/i', "$1", $tableContent);
|
||||
|
||||
// Добавляем разделитель для заголовков таблицы
|
||||
$tableContent = preg_replace('/\| \*\*[^\|]+\*\* [^\n]*?\|\n/', "$0| --- |\n", $tableContent, 1);
|
||||
|
||||
return "\n" . $tableContent . "\n";
|
||||
}, $markdown);
|
||||
|
||||
// 13. Удаляем все остальные HTML-теги
|
||||
$markdown = strip_tags($markdown);
|
||||
|
||||
// 14. Чистим лишние пробелы и переносы
|
||||
$markdown = preg_replace('/\n{3,}/', "\n\n", $markdown); // Более двух переносов заменяем на два
|
||||
$markdown = preg_replace('/^\s+|\s+$/m', '', $markdown); // Trim каждой строки
|
||||
$markdown = preg_replace('/\n\s*\n/', "\n\n", $markdown); // Чистим пустые строки
|
||||
$markdown = preg_replace('/^ +/m', '', $markdown); // Убираем отступы в начале строк
|
||||
|
||||
$markdown = trim($markdown);
|
||||
|
||||
// 15. Дополнительная нормализация - убеждаемся, что есть пустые строки между абзацами
|
||||
$lines = explode("\n", $markdown);
|
||||
$normalized = [];
|
||||
$inParagraph = false;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$trimmed = trim($line);
|
||||
|
||||
if (empty($trimmed)) {
|
||||
// Пустая строка - конец абзаца
|
||||
if ($inParagraph) {
|
||||
$normalized[] = '';
|
||||
$inParagraph = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Непустая строка
|
||||
if (!$inParagraph && !empty($normalized) && end($normalized) !== '') {
|
||||
// Добавляем пустую строку перед новым абзацем
|
||||
$normalized[] = '';
|
||||
}
|
||||
|
||||
$normalized[] = $trimmed;
|
||||
$inParagraph = true;
|
||||
}
|
||||
|
||||
return implode("\n", $normalized);
|
||||
}
|
||||
|
||||
private function normalizeHtml($html) {
|
||||
// Нормализуем HTML структуру перед конвертацией
|
||||
$html = preg_replace('/<div[^>]*>(.*?)<\/div>/is', "<p>$1</p>", $html);
|
||||
|
||||
// Убираем лишние пробелы
|
||||
$html = preg_replace('/\s+/', ' ', $html);
|
||||
|
||||
// Восстанавливаем структуру абзацев
|
||||
$html = preg_replace('/([^>])\s*<\/(p|div)>\s*([^<])/', "$1</$2>\n\n$3", $html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function normalizeBookContent($book_id) {
|
||||
try {
|
||||
$chapters = $this->getAllChapters($book_id);
|
||||
$book = $this->findById($book_id);
|
||||
|
||||
foreach ($chapters as $chapter) {
|
||||
$normalized_content = '';
|
||||
|
||||
if ($book['editor_type'] == 'html') {
|
||||
// Нормализуем HTML контент
|
||||
$normalized_content = $this->normalizeHtmlContent($chapter['content']);
|
||||
} else {
|
||||
// Нормализуем Markdown контент
|
||||
$normalized_content = $this->normalizeMarkdownContent($chapter['content']);
|
||||
}
|
||||
|
||||
if ($normalized_content !== $chapter['content']) {
|
||||
$this->updateChapterContent($chapter['id'], $normalized_content);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
error_log("Error normalizing book content: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeHtmlContent($html) {
|
||||
// Простая нормализация HTML - оборачиваем текст без тегов в <p>
|
||||
if (!preg_match('/<[^>]+>/', $html) && trim($html) !== '') {
|
||||
// Если нет HTML тегов, оборачиваем в <p>
|
||||
$lines = explode("\n", trim($html));
|
||||
$wrapped = array_map(function($line) {
|
||||
$line = trim($line);
|
||||
return $line ? "<p>{$line}</p>" : '';
|
||||
}, $lines);
|
||||
return implode("\n", array_filter($wrapped));
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function normalizeMarkdownContent($markdown) {
|
||||
// Нормализация Markdown - убеждаемся, что есть пустые строки между абзацами
|
||||
$lines = explode("\n", $markdown);
|
||||
$normalized = [];
|
||||
$inParagraph = false;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$trimmed = trim($line);
|
||||
|
||||
if (empty($trimmed)) {
|
||||
// Пустая строка - конец абзаца
|
||||
if ($inParagraph) {
|
||||
$normalized[] = '';
|
||||
$inParagraph = false;
|
||||
}
|
||||
} else {
|
||||
// Непустая строка
|
||||
if (!$inParagraph && !empty($normalized) && end($normalized) !== '') {
|
||||
// Добавляем пустую строку перед новым абзацем
|
||||
$normalized[] = '';
|
||||
}
|
||||
$normalized[] = $line;
|
||||
$inParagraph = true;
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", $normalized);
|
||||
}
|
||||
}
|
||||
?>
|
||||
Regular → Executable
+5
-5
@@ -66,17 +66,17 @@ class User {
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
return $stmt->execute($params);
|
||||
}
|
||||
|
||||
public function delete($id) {
|
||||
$stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
public function updateStatus($id, $is_active) {
|
||||
$stmt = $this->pdo->prepare("UPDATE users SET is_active = ? WHERE id = ?");
|
||||
return $stmt->execute([$is_active, $id]);
|
||||
}
|
||||
|
||||
public function delete($id) {
|
||||
$stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
public function updateLastLogin($id) {
|
||||
$stmt = $this->pdo->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
|
||||
Regular → Executable
Reference in New Issue
Block a user