89 lines
3.0 KiB
PHP
89 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use CodeIgniter\Model;
|
|
|
|
class CategoryModel extends Model
|
|
{
|
|
protected $table = 'categories';
|
|
protected $primaryKey = 'id';
|
|
protected $returnType = 'array';
|
|
protected $useTimestamps = true;
|
|
protected $allowedFields = ['name', 'type', 'parent_id'];
|
|
|
|
protected $validationRules = [
|
|
'name' => 'required|max_length[100]',
|
|
'type' => 'required|in_list[income,expense]',
|
|
'parent_id' => 'permit_empty|is_natural_no_zero',
|
|
];
|
|
|
|
/**
|
|
* Plaska lista kategorii, opcjonalnie filtrowana typem.
|
|
*/
|
|
public function flatList(?string $type = null): array
|
|
{
|
|
$builder = $this->orderBy('type', 'ASC')->orderBy('name', 'ASC');
|
|
if ($type !== null && $type !== '') {
|
|
$builder = $builder->where('type', $type);
|
|
}
|
|
|
|
return $builder->findAll();
|
|
}
|
|
|
|
/**
|
|
* Czy kategoria jest uzywana przez jakies operacje.
|
|
*/
|
|
public function usedByOperations(int $id): bool
|
|
{
|
|
return (bool) model(OperationModel::class)->where('category_id', $id)->countAllResults();
|
|
}
|
|
|
|
/**
|
|
* Kategorie w porzadku drzewa, posortowane po nazwie na kazdym poziomie.
|
|
* Kazdy element dostaje 'depth' oraz 'indent_label' (nazwa z wcieciem) do list i selectow.
|
|
*/
|
|
public function tree(?string $type = null): array
|
|
{
|
|
$builder = $this->orderBy('name', 'ASC');
|
|
if ($type !== null && $type !== '') {
|
|
$builder->where('type', $type);
|
|
}
|
|
$all = $builder->findAll();
|
|
|
|
// Grupowanie dzieci wg rodzica (0 = korzen)
|
|
$childrenByParent = [];
|
|
foreach ($all as $c) {
|
|
$childrenByParent[(int) ($c['parent_id'] ?: 0)][] = $c;
|
|
}
|
|
|
|
$indent = "\u{00A0}\u{00A0}\u{00A0}\u{00A0}"; // 4x twarda spacja na poziom
|
|
$out = [];
|
|
|
|
$walk = static function (int $parentId, int $depth, string $parentPath) use (&$walk, &$out, $childrenByParent, $indent): void {
|
|
foreach ($childrenByParent[$parentId] ?? [] as $c) {
|
|
$c['depth'] = $depth;
|
|
$c['indent_label'] = str_repeat($indent, $depth) . $c['name'];
|
|
// Pelna sciezka "Nadrzedna → Podrzedna" — jednoznaczna nawet gdy JS uciasnie wciecia.
|
|
$c['path_label'] = $parentPath === '' ? $c['name'] : $parentPath . ' → ' . $c['name'];
|
|
$out[] = $c;
|
|
$walk((int) $c['id'], $depth + 1, $c['path_label']);
|
|
}
|
|
};
|
|
$walk(0, 0, '');
|
|
|
|
// Sieroty (np. rodzic odfiltrowany typem) — dopnij na koncu bez wciecia
|
|
$seen = array_column($out, null, 'id');
|
|
foreach ($all as $c) {
|
|
if (! isset($seen[$c['id']])) {
|
|
$c['depth'] = 0;
|
|
$c['indent_label'] = $c['name'];
|
|
$c['path_label'] = $c['name'];
|
|
$out[] = $c;
|
|
}
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
}
|