359 lines
14 KiB
PHP
359 lines
14 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace mirzaev\surikovlib\models;
|
||
|
||
use mirzaev\surikovlib\models\accounts_model as accounts;
|
||
|
||
use pdo;
|
||
use exception;
|
||
|
||
/**
|
||
* Модель книг
|
||
*
|
||
* @package mirzaev\surikovlib\models
|
||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||
*/
|
||
final class books_model extends core
|
||
{
|
||
/**
|
||
* Запись в базу данных
|
||
*
|
||
* @param string $title Название
|
||
* @param string|null $description Описание
|
||
* @param int|null $account Аккаунт (идентификатор)
|
||
* @param array &$errors Журнал ошибок
|
||
*
|
||
* @return int|null Идентификатор записанной книги
|
||
*/
|
||
public static function write(string $title, ?string $description = null, ?int $account = null, array &$errors = []): ?int
|
||
{
|
||
// Инициализация журнала ошибок
|
||
$errors['books'] ?? $errors['books'] = [];
|
||
|
||
try {
|
||
// Инициализация аккаунта
|
||
$account = accounts::init($account, $errors);
|
||
|
||
// Инициализация запроса
|
||
$request = static::$db->prepare("INSERT INTO `books` (`account`, `title`, `description`) VALUES (:account, :title, :description)");
|
||
|
||
// Инициализация параметров
|
||
$params = [
|
||
':account' => $account->id,
|
||
':title' => $title,
|
||
':description' => $description
|
||
];
|
||
|
||
// Отправка запроса
|
||
$request->execute($params);
|
||
|
||
if ($id = static::$db->lastInsertId()) {
|
||
// Получен идентификатор загруженной книги (подразумевается)
|
||
|
||
return (int) $id;
|
||
}
|
||
} catch (exception $e) {
|
||
// Запись в журнал ошибок
|
||
$errors['books'][] = [
|
||
'text' => $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine(),
|
||
'stack' => $e->getTrace()
|
||
];
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Чтение
|
||
*
|
||
* @param array $expression Выражение поиска
|
||
* @param int $limit Ограничение по количеству
|
||
* @param int $page Страница (для списка книг)
|
||
* @param array &$errors Журнал ошибок
|
||
*
|
||
* @return array Книги
|
||
*/
|
||
public static function read(array $expression = [], int $limit = 1, int $page = 1, array &$errors = []): array
|
||
{
|
||
// Инициализация журнала ошибок
|
||
$errors['books'] ?? $errors['books'] = [];
|
||
|
||
try {
|
||
// Инициализация выражения поиска
|
||
$where = 'WHERE ';
|
||
|
||
// Инициализация параметров запроса
|
||
$params = [];
|
||
|
||
foreach ($expression as $parameter => $value) {
|
||
// Перебор выражения поиска
|
||
|
||
// Запись в строку запроса
|
||
$where .= "`$parameter` = :$parameter &&";
|
||
|
||
// Запись параметров запроса
|
||
$params[":$parameter"] = $value;
|
||
}
|
||
|
||
// Очистка или реинициализация выражения поиска
|
||
$where = empty($expression) ? '' : trim(trim($where, '&&'));
|
||
|
||
// Инициализация страницы
|
||
$page = $limit * --$page;
|
||
|
||
// Инициализация запроса
|
||
$request = static::$db->prepare("SELECT * FROM `books` $where LIMIT $page, $limit");
|
||
|
||
// Отправка запроса
|
||
$request->execute($params);
|
||
|
||
return (array) $request->fetchAll(pdo::FETCH_ASSOC);
|
||
} catch (exception $e) {
|
||
// Запись в журнал ошибок
|
||
$errors['books'][] = [
|
||
'text' => $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine(),
|
||
'stack' => $e->getTrace()
|
||
];
|
||
}
|
||
|
||
return [];
|
||
}
|
||
|
||
/**
|
||
* Удаление из базы данных
|
||
*
|
||
* @param int $id Идентификатор
|
||
* @param array &$errors Журнал ошибок
|
||
*
|
||
* @return bool Статус выполнения
|
||
*/
|
||
public static function delete(int $id, array &$errors = []): bool
|
||
{
|
||
// Инициализация журнала ошибок
|
||
$errors['books'] ?? $errors['books'] = [];
|
||
|
||
try {
|
||
// Инициализация запроса
|
||
$request = static::$db->prepare("DELETE FROM `books` WHERE `id` = :id LIMIT 1");
|
||
|
||
// Отправка запроса
|
||
$request->execute([':id' => $id]);
|
||
|
||
return true;
|
||
} catch (exception $e) {
|
||
// Запись в журнал ошибок
|
||
$errors['books'][] = [
|
||
'text' => $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine(),
|
||
'stack' => $e->getTrace()
|
||
];
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Импорт
|
||
*
|
||
* @param array $books Книги (файлы)
|
||
* @param int|null $account Аккаунт (идентификатор)
|
||
* @param array &$errors Журнал ошибок
|
||
*
|
||
* @return array Записанные книги
|
||
*/
|
||
public static function import(array $books, ?int $account = null, array &$errors = []): array
|
||
{
|
||
// Инициализация журнала ошибок
|
||
$errors['books'] ?? $errors['books'] = [];
|
||
|
||
try {
|
||
if (empty($books)) {
|
||
// Не найдены книги
|
||
|
||
throw new exception('Не найдены книги для записи');
|
||
}
|
||
|
||
// Инициализация аккаунта
|
||
$account = accounts::init($account, $errors);
|
||
|
||
// Инициализация буфера инициализированных книг
|
||
$initialized = [];
|
||
|
||
for ($i = -1; count($books['name']) > ++$i;) {
|
||
// Перебор загруженных книг
|
||
|
||
// Генерация хеша файла
|
||
$hash = hash_file('md5', $books['tmp_name'][$i]) ?? 0;
|
||
|
||
if (move_uploaded_file($books['tmp_name'][$i], \STORAGE . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $hash . '_' . $books['name'][$i])) {
|
||
// Загружен и перемещён из временной папки файл с книгой
|
||
|
||
// Извлечение имени файла
|
||
|
||
// Запись в буфер инициализированных книг
|
||
$initialized[] = [
|
||
'name' => preg_replace('/\.pdf/', '', $books['name'])[0],
|
||
'file' => $hash . '_' . $books['name'][$i]
|
||
];
|
||
}
|
||
}
|
||
|
||
// Инициализация буфера записанных книг
|
||
$writed = [];
|
||
|
||
foreach ($initialized as $book) {
|
||
// Перебор инициализированных книг
|
||
|
||
try {
|
||
if ($id = static::write($book['name'], 'Без описания', $account->id ?? null, $errors)) {
|
||
// Записана в базу данных книга
|
||
|
||
// Инициализация пути до хранилища
|
||
$directory = \STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . $id . DIRECTORY_SEPARATOR;
|
||
|
||
// Инициализация директории
|
||
if (!file_exists($directory)) if (!mkdir($directory, 0755, true)) throw new exception('Не удалось записать директорию для книги');
|
||
|
||
// Инициализация пути до временного файла
|
||
$file = \STORAGE . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $book['file'];
|
||
|
||
// Извлечение изображений из PDF-документа
|
||
exec("pdfimages -j '$file' '$directory'");
|
||
|
||
// Переименование файлов в необходимый формат
|
||
exec("echo 'export j=-1; for i in $directory*.jpg; do let j+=1; mv \$i $directory\$j.jpg; done' | bash");
|
||
|
||
// Запись в буфер записанных книг
|
||
$writed[] = $id;
|
||
} else {
|
||
// Не записана в базу данных книга
|
||
|
||
throw new exception('Не удалось записать книгу в базу данных', 500);
|
||
}
|
||
} catch (exception $e) {
|
||
// Запись в журнал ошибок
|
||
$errors['books'][] = [
|
||
'text' => $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine(),
|
||
'stack' => $e->getTrace()
|
||
];
|
||
}
|
||
}
|
||
} catch (exception $e) {
|
||
// Запись в журнал ошибок
|
||
$errors['books'][] = [
|
||
'text' => $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine(),
|
||
'stack' => $e->getTrace()
|
||
];
|
||
}
|
||
|
||
return $writed ?? [];
|
||
}
|
||
|
||
/**
|
||
* Чтение
|
||
*
|
||
* @param int $id Идентификатор
|
||
* @param int $page Страница (сдвиг)
|
||
* @param array &$errors Журнал ошибок
|
||
*
|
||
* @return bool Статус выполнения
|
||
*/
|
||
public static function rotate(int $id, int $page = 1, array &$errors = []): bool
|
||
{
|
||
// Инициализация журнала ошибок
|
||
$errors['books'] ?? $errors['books'] = [];
|
||
|
||
try {
|
||
// Инициализация запроса
|
||
$request = static::$db->prepare("SELECT EXISTS (SELECT * FROM `books` WHERE `id` = :id LIMIT 1)");
|
||
|
||
// Отправка запроса
|
||
$request->execute([':id' => $id]);
|
||
|
||
if ($request->fetch(pdo::FETCH_NUM)[0] === 1) {
|
||
// Найдена книга
|
||
|
||
// Инициализация пути книги
|
||
$book = \STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . $id;
|
||
|
||
// Инициализация пути до файла
|
||
$file = $book . DIRECTORY_SEPARATOR . "$page.jpg";
|
||
|
||
// Инициализация пути до нового местоположения файла
|
||
$old = $book . DIRECTORY_SEPARATOR . 'old' . DIRECTORY_SEPARATOR . "$page.jpg";
|
||
|
||
// Инициализация директории
|
||
if (!file_exists($book)) throw new exception('Не удалось найти директорию книги');
|
||
|
||
// Инициализация директории оригинальных изображений
|
||
if (!file_exists($book . DIRECTORY_SEPARATOR . 'old')) if (!mkdir($book . DIRECTORY_SEPARATOR . 'old', 0755, true)) throw new exception('Не удалось записать директорию для оригинальных страниц книги');
|
||
|
||
// Перемещение страницы в директорию оригинальных страниц
|
||
exec("mv $file $old");
|
||
|
||
// Переворачивание файла и возвращение на нужное местоположение
|
||
exec("jpegtran -rotate 90 $old > $file");
|
||
|
||
return true;
|
||
}
|
||
} catch (exception $e) {
|
||
// Запись в журнал ошибок
|
||
$errors['books'][] = [
|
||
'text' => $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine(),
|
||
'stack' => $e->getTrace()
|
||
];
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Подсчёт количества страниц
|
||
*
|
||
* @param int $id Идентификатор
|
||
* @param array &$errors Журнал ошибок
|
||
*
|
||
* @return int|null Количество страниц
|
||
*/
|
||
public static function amount(int $id, array &$errors = []): ?int
|
||
{
|
||
// Инициализация журнала ошибок
|
||
$errors['books'] ?? $errors['books'] = [];
|
||
|
||
try {
|
||
// Инициализация счётчика
|
||
$amount = -1;
|
||
|
||
while (true) {
|
||
// Перебор директорий (!!! Рекурсия !!!)
|
||
|
||
// Перебор изображений по возрастанию (от 0.jpg до 999.jpg и т.д.)
|
||
if (!file_exists(\STORAGE . DIRECTORY_SEPARATOR . 'books' . DIRECTORY_SEPARATOR . $id . DIRECTORY_SEPARATOR . ++$amount . '.jpg')) return $amount;
|
||
}
|
||
} catch (exception $e) {
|
||
// Запись в журнал ошибок
|
||
$errors['books'][] = [
|
||
'text' => $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine(),
|
||
'stack' => $e->getTrace()
|
||
];
|
||
}
|
||
|
||
return null;
|
||
}
|
||
}
|