'Receita', self::TYPE_EXPENSE => 'Despesa', self::TYPE_BOTH => 'Ambos', ]; protected $fillable = [ 'user_id', 'parent_id', 'name', 'type', 'description', 'color', 'icon', 'order', 'is_active', 'is_system', ]; protected $casts = [ 'order' => 'integer', 'is_active' => 'boolean', 'is_system' => 'boolean', ]; /** * Relação com o usuário */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * Relação com a categoria pai */ public function parent(): BelongsTo { return $this->belongsTo(Category::class, 'parent_id'); } /** * Relação com as sub-categorias (filhas) */ public function children(): HasMany { return $this->hasMany(Category::class, 'parent_id'); } /** * Alias para children (usado em budgets) */ public function subcategories(): HasMany { return $this->children(); } /** * Relação com as sub-categorias ativas */ public function activeChildren(): HasMany { return $this->hasMany(Category::class, 'parent_id')->where('is_active', true); } /** * Relação com as palavras-chave */ public function keywords(): HasMany { return $this->hasMany(CategoryKeyword::class); } /** * Relação com as palavras-chave ativas */ public function activeKeywords(): HasMany { return $this->hasMany(CategoryKeyword::class)->where('is_active', true); } /** * Scope para categorias ativas */ public function scopeActive($query) { return $query->where('is_active', true); } /** * Scope para categorias de um tipo específico */ public function scopeOfType($query, string $type) { return $query->where('type', $type)->orWhere('type', self::TYPE_BOTH); } /** * Scope para categorias raiz (sem pai) */ public function scopeRoot($query) { return $query->whereNull('parent_id'); } /** * Scope para sub-categorias (com pai) */ public function scopeChildren($query) { return $query->whereNotNull('parent_id'); } /** * Retorna o nome legível do tipo */ public function getTypeNameAttribute(): string { return self::TYPES[$this->type] ?? $this->type; } /** * Verifica se é uma categoria raiz */ public function isRoot(): bool { return $this->parent_id === null; } /** * Retorna o caminho completo da categoria (Pai > Filho) */ public function getFullPathAttribute(): string { if ($this->parent) { return $this->parent->name . ' > ' . $this->name; } return $this->name; } }