Skip to content

Commit 409cbe7

Browse files
feat: refactor to configurable model and merged PR #8 (#9)
* fix: getNestedTree with Model::shouldBeStrict() * ci(tests): update test workflow to include coverage and upgrade codecov * refactor(command): extract model class retrieval to method * feat(enums): add documentation and utility methods * feat(taxonomy): make taxonomy model configurable * test: add comprehensive test coverage for taxonomy features * style(tests): remove trailing whitespace in test files * fix(taxonomy): eager load children relation when getting descendants * test: disable lazy loading and add description field to products * test(command): add tests for handle method execution and signature * docs: add codecov badge and reorder existing badges * test(model): add description to fillable fields * test: add unit tests for DuplicateSlugException * test(taxonomy): add unit tests for taxonomy filtering functionality * test: add unit tests for TaxonomyManager * test(taxonomy): add additional unit tests for Taxonomy model * test: eager load nested children in taxonomy descendants test * test: add unit tests for TaxonomyProvider registration * test: remove unused ReflectionClass imports from test files * refactor(taxonomy): make wouldCreateCircularReference method public * test(taxonomy): add circular reference check in move operation test * test: add tests for rebuild nested set command and createOrUpdate method --------- Co-authored-by: HassanDomeDenea <hassan.domedenea@gmail.com>
1 parent aa20f3e commit 409cbe7

24 files changed

+1811
-40
lines changed

.github/workflows/tests.yml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ jobs:
4747
- name: List Installed Dependencies
4848
run: composer show -D
4949

50-
- name: Execute tests
51-
run: php vendor/bin/pest --no-coverage
52-
env:
53-
PEST_NO_COVERAGE: true
50+
- name: Execute tests with coverage
51+
run: php vendor/bin/pest --coverage-clover=coverage.xml
5452

5553
- name: Execute PHPStan
5654
run: vendor/bin/phpstan analyse --memory-limit=2G
@@ -84,9 +82,9 @@ jobs:
8482
fi
8583
8684
- name: Upload coverage reports to Codecov
87-
uses: codecov/codecov-action@v3
85+
uses: codecov/codecov-action@v5
8886
with:
87+
token: ${{ secrets.CODECOV_TOKEN }}
8988
file: ./coverage.xml
9089
flags: unittests
91-
name: codecov-umbrella
92-
fail_ci_if_error: false
90+
name: codecov-umbrella

README.id.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
# Laravel Taxonomy
77

8+
[![codecov](https://codecov.io/gh/aliziodev/laravel-taxonomy/branch/master/graph/badge.svg)](https://codecov.io/gh/aliziodev/laravel-taxonomy)
89
[![Tests](https://github.com/aliziodev/laravel-taxonomy/workflows/Tests/badge.svg)](https://github.com/aliziodev/laravel-taxonomy/actions)
910
[![Code Quality](https://github.com/aliziodev/laravel-taxonomy/workflows/Code%20Quality/badge.svg)](https://github.com/aliziodev/laravel-taxonomy/actions)
1011
[![Latest Version on Packagist](https://img.shields.io/packagist/v/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
1112
[![Total Downloads](https://img.shields.io/packagist/dt/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
12-
[![License](https://img.shields.io/packagist/l/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
1313
[![PHP Version](https://img.shields.io/packagist/php-v/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
1414
[![Laravel Version](https://img.shields.io/badge/Laravel-11.0%2B-orange.svg)](https://laravel.com/)
1515

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
# Laravel Taxonomy
77

8+
[![codecov](https://codecov.io/gh/aliziodev/laravel-taxonomy/branch/master/graph/badge.svg)](https://codecov.io/gh/aliziodev/laravel-taxonomy)
89
[![Tests](https://github.com/aliziodev/laravel-taxonomy/workflows/Tests/badge.svg)](https://github.com/aliziodev/laravel-taxonomy/actions)
910
[![Code Quality](https://github.com/aliziodev/laravel-taxonomy/workflows/Code%20Quality/badge.svg)](https://github.com/aliziodev/laravel-taxonomy/actions)
1011
[![Latest Version on Packagist](https://img.shields.io/packagist/v/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
1112
[![Total Downloads](https://img.shields.io/packagist/dt/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
12-
[![License](https://img.shields.io/packagist/l/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
1313
[![PHP Version](https://img.shields.io/packagist/php-v/aliziodev/laravel-taxonomy.svg)](https://packagist.org/packages/aliziodev/laravel-taxonomy)
1414
[![Laravel Version](https://img.shields.io/badge/Laravel-11.0%2B-orange.svg)](https://laravel.com/)
1515

src/Console/Commands/RebuildNestedSetCommand.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ class RebuildNestedSetCommand extends Command
2525
*/
2626
protected $description = 'Rebuild nested set values (lft, rgt, depth) for taxonomy hierarchies';
2727

28+
/**
29+
* Get the taxonomy model class from config.
30+
*
31+
* @return class-string<\Aliziodev\LaravelTaxonomy\Models\Taxonomy>
32+
*/
33+
protected function getModelClass(): string
34+
{
35+
return config('taxonomy.model', Taxonomy::class);
36+
}
37+
2838
/**
2939
* Execute the console command.
3040
*/
@@ -80,7 +90,8 @@ protected function rebuildType(string $type): void
8090
{
8191
$this->line("Rebuilding type: {$type}");
8292

83-
$count = Taxonomy::where('type', $type)->count();
93+
$modelClass = $this->getModelClass();
94+
$count = $modelClass::where('type', $type)->count();
8495
if ($count === 0) {
8596
$this->line(" No taxonomies found for type: {$type}");
8697

@@ -90,7 +101,8 @@ protected function rebuildType(string $type): void
90101
$startTime = microtime(true);
91102

92103
// Use the model's rebuild method
93-
Taxonomy::rebuildNestedSet($type);
104+
$modelClass = $this->getModelClass();
105+
$modelClass::rebuildNestedSet($type);
94106

95107
$duration = round(microtime(true) - $startTime, 2);
96108
$this->line(" Rebuilt {$count} taxonomies in {$duration} seconds");
@@ -121,7 +133,9 @@ protected function getAvailableTypes(): array
121133
*/
122134
protected function getExistingTypes(): array
123135
{
124-
return Taxonomy::select('type')
136+
$modelClass = $this->getModelClass();
137+
138+
return $modelClass::select('type')
125139
->distinct()
126140
->pluck('type')
127141
->toArray();
@@ -135,7 +149,8 @@ protected function getExistingTypes(): array
135149
protected function confirmRebuild(array $types): bool
136150
{
137151
$typesList = implode(', ', $types);
138-
$count = Taxonomy::whereIn('type', $types)->count();
152+
$modelClass = $this->getModelClass();
153+
$count = $modelClass::whereIn('type', $types)->count();
139154

140155
$this->warn("This will rebuild nested set values for {$count} taxonomies.");
141156
$this->warn("Types: {$typesList}");

src/Enums/TaxonomyType.php

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
namespace Aliziodev\LaravelTaxonomy\Enums;
44

5+
/**
6+
* Taxonomy Type Enum.
7+
*
8+
* Defines the available taxonomy types that can be used throughout the application.
9+
* Each type represents a different categorization method for organizing content.
10+
*
11+
* @author Aliziodev
12+
*
13+
* @since 1.0.0
14+
*/
515
enum TaxonomyType: string
616
{
717
case Category = 'category';
@@ -15,15 +25,26 @@ enum TaxonomyType: string
1525
case Variant = 'variant';
1626

1727
/**
18-
* Get all enum values.
28+
* Get all enum values as an array of strings.
1929
*
20-
* @return array<string>
30+
* This method returns all the string values of the enum cases,
31+
* useful for validation, database seeding, or form options.
32+
*
33+
* @return array<int, string> Array of all enum values
2134
*/
2235
public static function values(): array
2336
{
2437
return array_column(self::cases(), 'value');
2538
}
2639

40+
/**
41+
* Get the human-readable label for the taxonomy type.
42+
*
43+
* Returns a properly formatted, user-friendly label for display purposes.
44+
* This is useful for UI elements, form labels, and user-facing content.
45+
*
46+
* @return string The human-readable label
47+
*/
2748
public function label(): string
2849
{
2950
return match ($this) {
@@ -38,4 +59,36 @@ public function label(): string
3859
self::Variant => 'Variant',
3960
};
4061
}
62+
63+
/**
64+
* Alias for the label() method.
65+
*
66+
* Provides an alternative method name for getting the human-readable label.
67+
* This method exists for consistency with other Laravel conventions.
68+
*
69+
* @return string The human-readable label
70+
*/
71+
public function getLabel(): string
72+
{
73+
return $this->label();
74+
}
75+
76+
/**
77+
* Get all taxonomy types as an array of options.
78+
*
79+
* Returns an array of associative arrays, each containing 'value' and 'label' keys.
80+
* This format is particularly useful for generating form select options, API responses,
81+
* or any scenario where you need both the enum value and its display label.
82+
*
83+
* @return array<int, array{value: string, label: string}> Array of option arrays
84+
*/
85+
public static function options(): array
86+
{
87+
return collect(self::cases())->map(function ($case) {
88+
return [
89+
'value' => $case->value,
90+
'label' => $case->label(),
91+
];
92+
})->toArray();
93+
}
4194
}

src/Models/Taxonomy.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ public function descendants(): Collection
193193
/** @var \Illuminate\Database\Eloquent\Collection<int, self> $descendants */
194194
$descendants = new Collection;
195195

196+
// Load children relationship if not already loaded
197+
if (! $this->relationLoaded('children')) {
198+
$this->load('children');
199+
}
200+
196201
foreach ($this->children as $child) {
197202
/* @var self $child */
198203
$descendants->push($child);
@@ -521,7 +526,7 @@ public function moveToParent(?int $parentId): void
521526
/**
522527
* Check if moving to the given parent would create a circular reference.
523528
*/
524-
protected function wouldCreateCircularReference(int $parentId): bool
529+
public function wouldCreateCircularReference(int $parentId): bool
525530
{
526531
// If trying to move to itself
527532
if ($parentId === $this->id) {
@@ -821,7 +826,7 @@ protected static function buildNestedTree(Collection $taxonomies): Collection
821826
} else {
822827
// Child level - add to parent's children collection
823828
$parent = end($stack);
824-
if ($parent && ! $parent->children_nested) {
829+
if ($parent && empty($parent->children_nested)) {
825830
/** @var \Illuminate\Database\Eloquent\Collection<int, static> $childrenNested */
826831
$childrenNested = new Collection;
827832
$parent->setAttribute('children_nested', $childrenNested);

0 commit comments

Comments
 (0)