118 lines
4.4 KiB
PHP
118 lines
4.4 KiB
PHP
<?php
|
||
/**
|
||
* @package Bicycle
|
||
* @author Egor Isaev
|
||
* @description CategoryRepository.php
|
||
* @copyright (c) 11/08/2026
|
||
*/
|
||
|
||
namespace App\Repositories;
|
||
|
||
use System\Classes\Repository;
|
||
|
||
/**
|
||
* Репозиторий таблицы categories (статьи income/expense; группа — отдельная сущность,
|
||
* см. App\Repositories\CategoryGroupRepository, статья ссылается на неё через group_id).
|
||
*/
|
||
class CategoryRepository extends Repository
|
||
{
|
||
/**
|
||
* @param string|null $connection Имя подключения (Database::instance($connection)); null — по умолчанию
|
||
*/
|
||
public function __construct(?string $connection = null)
|
||
{
|
||
parent::__construct('categories', connection: $connection);
|
||
}
|
||
|
||
/**
|
||
* Активная (is_delete=0) статья с тем же названием у пользователя, в рамках type+group_id.
|
||
*
|
||
* @param int $user_id
|
||
* @param string $type income|expense
|
||
* @param int|null $group_id null — без группы
|
||
* @param string $title
|
||
* @param int|null $exclude_id Исключить этот id (правка своего же названия — не дубль)
|
||
* @return object|false
|
||
*/
|
||
public function findActiveDuplicate(int $user_id, string $type, ?int $group_id, string $title, ?int $exclude_id = null): mixed
|
||
{
|
||
return $this->findDuplicate($user_id, $type, $group_id, $title, false, $exclude_id);
|
||
}
|
||
|
||
/**
|
||
* Архивная (is_delete=1) статья с тем же названием у пользователя.
|
||
*
|
||
* @param int $user_id
|
||
* @param string $type income|expense
|
||
* @param int|null $group_id
|
||
* @param string $title
|
||
* @param int|null $exclude_id
|
||
* @return object|false
|
||
*/
|
||
public function findArchivedDuplicate(int $user_id, string $type, ?int $group_id, string $title, ?int $exclude_id = null): mixed
|
||
{
|
||
return $this->findDuplicate($user_id, $type, $group_id, $title, true, $exclude_id);
|
||
}
|
||
|
||
/**
|
||
* @param int $user_id
|
||
* @param string $type
|
||
* @param int|null $group_id
|
||
* @param string $title
|
||
* @param bool $is_delete
|
||
* @param int|null $exclude_id
|
||
* @return object|false
|
||
*/
|
||
protected function findDuplicate(int $user_id, string $type, ?int $group_id, string $title, bool $is_delete, ?int $exclude_id): mixed
|
||
{
|
||
$pdo = $this->pdo->pdo();
|
||
|
||
$where = "user_id = $user_id"
|
||
. ' AND type = ' . $pdo->quote($type)
|
||
. ' AND is_delete = ' . ($is_delete ? 1 : 0)
|
||
. ' AND title = ' . $pdo->quote($title)
|
||
. ' AND group_id ' . ($group_id === null ? 'IS NULL' : "= $group_id");
|
||
|
||
if ($exclude_id !== null) {
|
||
$where .= " AND id != $exclude_id";
|
||
}
|
||
|
||
return $this->getItemWhere($where);
|
||
}
|
||
|
||
/**
|
||
* Активные статьи пользователя, сгруппированные по group_id (0 — без группы, вместо null,
|
||
* чтобы использовать как ключ массива) — для рендера списка статей по группам.
|
||
*
|
||
* @param int $user_id
|
||
* @return array<int,object[]>
|
||
*/
|
||
public function getTree(int $user_id): array
|
||
{
|
||
$categories = $this->getList('*', ['user_id' => $user_id, 'is_delete' => 0], '`order`');
|
||
$tree = [];
|
||
|
||
foreach ($categories ?: [] as $category) {
|
||
$key = $category->group_id === null ? 0 : (int)$category->group_id;
|
||
$tree[$key][] = $category;
|
||
}
|
||
|
||
return $tree;
|
||
}
|
||
|
||
/**
|
||
* Отвязать все статьи пользователя от группы (group_id = NULL) — используется при архивации
|
||
* группы (App\Controller\CategoryGroupsController::deleteAction), не при её физическом
|
||
* удалении (то делает fk_categories_group ... ON DELETE SET NULL само).
|
||
*
|
||
* @param int $group_id
|
||
* @param int $user_id
|
||
* @return void
|
||
*/
|
||
public function clearGroup(int $group_id, int $user_id): void
|
||
{
|
||
$this->pdo->prepare("UPDATE $this->table SET group_id = NULL WHERE group_id = :group_id AND user_id = :user_id")
|
||
->execute(['group_id' => $group_id, 'user_id' => $user_id]);
|
||
}
|
||
}
|