Categories
Categories form a nestable tree with a translatable name and a code-facing slug:
use RoundlyConsulting\Advertisements\Models\Advertisement;
use RoundlyConsulting\Advertisements\Models\Category;
$vehicles = Category::query()->create(['name' => ['en' => 'Vehicles', 'sk' => 'Vozidlá']]); // slug "vehicles"
$bikes = Category::query()->create(['name' => ['en' => 'Bikes'], 'parent_id' => $vehicles->id]);
$ad->category; // BelongsTo<Category>
$bikes->parent; // ?Category
$vehicles->children; // HasMany<Category>
$vehicles->descendants(); // every nested category
$vehicles->advertisements; // HasMany<Advertisement>
Advertisement::query()->inCategory($vehicles)->get(); // direct only
Advertisement::query()->inCategory('vehicles', includeDescendants: true)->get(); // + nestedinCategory() accepts a model, id, slug or numeric-string id — a string is matched as a slug first, a digit-only one no category uses as its slug is then taken as an id. An unknown reference matches nothing rather than every uncategorised ad. descendants() walks the tree recursively.
AdvertisementData’s category argument accepts the same model, id or slug and resolves it to category_id — categories are never created implicitly.
Category slugs
Category slugs behave like placement slugs: generated from the fallback-locale name when empty, kept byte-for-byte when supplied (SlugAlreadyTakenException when taken) and never changed afterwards. Categories use soft deletes; when a category row is permanently removed, its children’s parent_id and its ads’ category_id become null.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.