Current DB & Models Analysis — 2026-02-10

Summary
- `content_types` is the master namespace (see screenshot). Rows: e.g. id=1 Photography (slug `photography`), id=2 Wallpapers, id=3 Skins, id=544 Members.
- `categories` references `content_types` through `content_type_id`; hierarchical parent/child relation via `parent_id`.

Observed DB columns (categories)
- id, content_type_id, parent_id, name, slug, description, image, is_active, sort_order, created_at, updated_at, deleted_at

Models verified
- `ContentType` (app/Models/ContentType.php)
  - hasMany `categories()` and `rootCategories()`
  - uses `slug` for route binding
  - Status: OK and aligns with DB

- `Category` (app/Models/Category.php)
  - belongsTo `contentType()`
  - self-referential `parent()` / `children()` (ordered by `sort_order`, then `name`)
  - `descendants()` recursive helper
  - `seo()` relation, `artworks()` pivot
  - scopes: `active()`, `roots()`
  - accessors: `full_slug_path`, `url`, `canonical_url`, `breadcrumbs`
  - slug validation enforced in `boot()` (lowercase; only a-z0-9- and dashes)
  - Status: OK and consistent with screenshots

Key behaviors and checks
- URL formation: `$category->url` -> `/{content_type.slug}/{category_path}`; canonical URL -> `https://skinbase.org{url}`
- Slug policy: generation with `Str::slug()` + model validation. Do not bypass.
- Ordering: use `children()` (sort_order then name) for deterministic menus.
- Soft deletes: model uses `SoftDeletes`; be explicit when you need trashed categories.
- Eager-loading: `full_slug_path` walks parents — eager-load `parent` (or ancestors) to avoid N+1 when computing multiple paths.

Copilot / Dev rules (short checklist)
- Always look up content types by `slug`, not by numeric ID.
- Use `->roots()->active()->with('children')` for public category lists.
- Use `$category->url` and `$category->canonical_url` for links and canonical tags.
- Maintain slug rules: lowercase, only `a-z0-9-`.
- When reparenting categories, consider invalidating any cached derived paths for descendants.
- Avoid using legacy `artworks_categories` directly in new controllers; create an adapter if you must read old data.

Suggested next steps
- Add a PHPUnit test asserting slug validation and `url` generation for nested categories.
- (Optional) Generate a small ER diagram showing `content_types -> categories -> artwork_category`.

Files referenced
- [app/Models/ContentType.php](app/Models/ContentType.php)
- [app/Models/Category.php](app/Models/Category.php)

If you want, I can now add the PHPUnit test or generate the ER diagram.
