Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Taily Laravel Package

ulbrich-media/taily

Taily is a Laravel/PHP package by Ulbrich Media that provides a lightweight, developer-friendly utility for Laravel applications. It emphasizes simple setup, clean integration, and minimal overhead, making it easy to add to existing projects and start using quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

taily is a Laravel application/package for animal-shelter operations (animals, adoptions, people, users). v0.12.0 is a substantial feature release: the three main list endpoints (animals, adoptions, people) now support server-side pagination, filtering, sorting and search, plus reusable list-filter presets. Start here:

  1. Install and boot

    composer install
    cp .env.example .env
    php artisan key:generate
    php artisan migrate --seed
    npm install && npm run dev   # frontend assets
    

    If the repo ships a DDEV config, use ddev start && ddev composer install instead — v0.12.0 specifically patches DDEV behaviour for worktrees, so the containerised path is the best-supported one.

  2. Look at these files first

    • routes/api.php — the animal/adoption/person list endpoints changed shape in this release.
    • The list controllers / query objects behind animals, adoptions, people — this is where filters, sorting, search and pagination are wired.
    • phpunit.xml — the stateful domain and frontend URL are now pinned here; read it before writing a single test.
    • Frontend list components (the animal list was rebuilt in #207) — the query-string contract lives there.
  3. First use case: paginated, searchable animal list

    curl "https://taily.test/api/animals?search=beagle&sort=-name&per_page=25&page=2"
    

    Expect a Laravel paginator envelope (data, links, meta) rather than a bare array. Wire your frontend table to that envelope; the old unpaginated response shape is gone for these endpoints.

Implementation Patterns

Standard list-query contract. All three list resources now share the same vocabulary:

Param Purpose Example
search Free-text term ?search=beagle
sort Field, - prefix for descending ?sort=-created_at
filter[...] Column-scoped filters ?filter[status]=available
per_page Page size ?per_page=50
page Page number ?page=3

Build one reusable query object or trait and apply it across all three resources so the behaviour stays identical:

final class ListQuery
{
    public static function apply(Builder $query, Request $request, array $sortable, array $filterable): Builder
    {
        return $query
            ->when($request->string('search')->trim()->value(), fn ($q, $term) =>
                $q->where(fn ($q) => $q->where('name', 'like', "%{$term}%")
                                        ->orWhere('notes', 'like', "%{$term}%")))
            ->when($request->input('filter'), fn ($q, $filters) =>
                collect($filters)->only($filterable)->each(fn ($v, $k) => $q->where($k, $v)))
            ->when($request->input('sort'), function ($q, $sort) use ($sortable) {
                $dir = str_starts_with($sort, '-') ? 'desc' : 'asc';
                $col = ltrim($sort, '-');
                return in_array($col, $sortable, true) ? $q->orderBy($col, $dir) : $q;
            }, fn ($q) => $q->latest());
    }
}

Always whitelist sort and filter keys against an explicit array — never interpolate user input into orderBy.

Preset filters. v0.12.0 adds personalised and archive presets. Treat presets as named bundles of the same query params, resolved server-side so the frontend can ship a single ?preset=archive link:

$presets = [
    'archive'      => ['filter' => ['archived' => true], 'sort' => '-archived_at'],
    'personalised' => ['filter' => ['owner_id' => auth()->id()]],
];
$params = array_merge($presets[$request->input('preset')] ?? [], $request->only(['search', 'sort', 'page']));

Keep presets additive — user-supplied params should still be able to narrow a preset.

User ↔ person linking. Users now optionally relate to a person record. Make the relation nullable and guard reads:

public function person(): BelongsTo { return $this->belongsTo(Person::class); }
// usage
$user->person?->full_name ?? $user->name;

Backfill existing users where possible, but design every display path to tolerate null — the release explicitly calls the link optional.

Frontend integration. Keep list state in the URL query string so preset links, browser back/forward and refresh all behave. Debounce search (~300 ms) and reset page to 1 whenever any filter changes, otherwise combined filters return empty pages.

Testing. Because v0.12.0 pins the stateful domain and frontend URL in phpunit.xml, write feature tests against relative URLs ($this->getJson('/api/animals?search=x')) rather than absolute ones. Use assertJsonStructure(['data', 'links', 'meta']) to lock the paginator contract.

Gotchas and Tips

  • Pinned test URLs. phpunit.xml now hard-codes the stateful domain and frontend URL. If you run tests behind a different host, or override APP_URL in .env.testing, you can get redirect/assertion mismatches. Change it in phpunit.xml, not in .env, and keep it in sync with CI.
  • DDEV + worktrees. Mutagen is now disabled for worktree-based DDEV projects (539976e) because the sync agent misbehaved there. Consequence: worktree environments are noticeably slower on file I/O, and ddev composer runs take longer. That's expected, not a regression — use the main checkout for heavy installs.
  • Response shape change. The animal/adoption/person list endpoints are paginated now. Any client or Blade view that iterated the old flat array will silently render nothing. Grep for the old consumers before deploying; this is the most likely upgrade breakage.
  • Filter/pagination interaction. Changing filters without resetting page is the classic bug — page 4 of a filtered set is usually empty. Reset page client-side and consider ignoring page server-side when filters are present but the page exceeds lastPage.
  • Search performance. LIKE '%term%' cannot use a B-tree index. On larger animal/person tables add a pg_trgm/fulltext index or a dedicated search scope behind the same search param so the API contract stays stable.
  • Sorting defaults. Ensure a deterministic tiebreaker (orderBy('id') after the requested sort); without it pagination can duplicate or drop rows across pages when sort values tie.
  • Dialog scroll fix (#201). The frontend now preserves scroll position when opening/closing dialogs. If you have custom modal wrappers, make sure they go through the same component — hand-rolled modals won't get the fix and will still jump.
  • Extension points. The list-query logic and the preset registry are the natural seams for customisation: add domain-specific filters (species, intake date range, adoption status) by extending the whitelist and preset map rather than forking controllers. Keep custom filters inside the shared contract so frontend components and presets keep working.
  • Nullable person relation. Migrations adding the user→person foreign key must be nullable and nullOnDelete (or restrict), or deleting a person will take users with it.
  • Upgrade checklist. Run migrations, clear config cache (php artisan config:clear) after touching phpunit.xml/env, re-publish frontend assets (npm run build), and smoke-test all three list endpoints with a sort + filter + search combination before rolling out.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager