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

Laravel Full Name Laravel Package

plin-code/laravel-full-name

plin-code/laravel-full-name is a Laravel package that adds a reusable FullName trait/accessor to models, combining first, middle, and last name fields into a normalized full name for display, search, and sorting. Configurable name parts and separators keep Eloquent models simple.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, focused package: it centralises the "sort by a person's full name" problem, including the awkward case where the name lives on a related model. v1.1.1 is a maintenance release (PHPStan docblock narrowing + a Rector cleanup), so installation steps are unchanged.

  1. composer require <vendor>/<package> — no service provider or config publishing appears necessary for the core matcher.
  2. Read, in this order:
    • src/FullNameOptions.php — the immutable options object (which columns make up the name, whether a relation is traversed).
    • src/FullNameMatcher.php — applySort() and applySortWithRelation().
    • The README's usage block and the test suite, which is the real API documentation for the edge cases.
  3. First use case — a users index sorted by first_name + last_name:
use Vendor\Package\FullNameOptions;
use Vendor\Package\FullNameMatcher;

$options = FullNameOptions::make()
    ->withColumns(['first_name', 'last_name']);

return User::query()
    ->tap(fn ($q) => FullNameMatcher::make($options)->applySort($q, 'asc'))
    ->paginate();

If your first task is "sort the posts list by author name", skip straight to applySortWithRelation() — that is the reason most people install this package.

Implementation Patterns

Normalise direction at the boundary. applySort() narrows $direction to 'asc'|'desc', so let validation do the narrowing rather than casting:

$request->validate([
    'direction' => ['nullable', Rule::in(['asc', 'desc'])],
]);

$direction = $request->string('direction')->toString() ?: 'asc';

Wrap it in a model scope so controllers stay free of package imports:

public function scopeSortByFullName(Builder $query, string $direction = 'asc'): Builder
{
    return FullNameMatcher::make(static::fullNameOptions())
        ->applySort($query, $direction);
}

Expose fullNameOptions() as a static factory on the model — it keeps the column list (first_name, last_name, prefixes, middle names) in one place and makes it easy to override per model.

Sorting across a relation (posts → author) is the pattern to build around:

FullNameMatcher::make(FullNameOptions::make()->withRelation('author'))
    ->applySortWithRelation($query, $direction);

Keep withRelation() explicit — v1.1.1 removed the redundant relation: null argument from withoutRelation(), confirming the options object is meant to be configured positively rather than by nulling things out.

Compose with existing query state (where filters, withCount, with() eager loads) by applying the sort last; the matcher should not be the thing deciding pagination or select columns.

Test by asserting order, not SQL. assertSeeInOrder() on the rendered index, or pluck('id')->all() against an expected array, survives internal changes like the v1.1.1 docblock/Rector edits. If you do snapshot toSql(), expect churn.

Static analysis in CI. Run PHPStan/Larastan at the level the package itself uses; the v1.1.1 fix exists specifically because Laravel 13 tightened orderBy()'s signature, so your own call sites benefit from the same narrowing.

Gotchas and Tips

  • Direction is typed, not sanitised. Since v1.1.1 the docblock commits to 'asc'|'desc'. Passing 'ASC', 'ascending', or a raw ?string from the request will now surface as a PHPStan error (and possibly a runtime orderBy complaint on Laravel 13). Validate with Rule::in() and default to 'asc'.
  • Relation sorts mean joins. applySortWithRelation() will join the related table, so:
    • Qualify columns in your own select()/where() calls to avoid ambiguous-column errors (the related table almost certainly has its own id, created_at, name).
    • Watch for row multiplication if the relation is not strictly one-to-one; consider distinct() or constraining the relation.
    • Add an index on the related table's name columns, not just the FK — the sort happens on the joined side.
  • CONCAT is unindexable. Even with correct indexes, MySQL/Postgres generally cannot use an index for ORDER BY CONCAT(first_name, ' ', last_name). For large tables, add a generated/stored full_name column with an index and keep the package for the relation-traversal case.
  • NULL name parts. A NULL last_name makes CONCAT return NULL in MySQL, dumping those rows at one end of the sort with no obvious cause. Prefer COALESCE(last_name, '') in your column config if the package allows raw expressions; otherwise clean the data.
  • Collation and case. Sorting is database-collation dependent; Zoe vs zoe and accented names will order differently between MySQL (utf8mb4_unicode_ci) and Postgres (C). Pin collation in tests if order assertions are flaky.
  • Don't copy the package internals into your app. The v1.1.1 changes (relation: null removal, docblock narrowing) are exactly the kind of tidy-ups you would miss in a vendored copy. Call the public API.
  • Debugging: ->toSql() plus ->getBindings() on the builder after applying the sort is the fastest way to see the generated join and ORDER BY. Wrap it in a DB::listen() in a local service provider if you want it logged for every request.
  • Extension points are thin — that's the design. FullNameOptions is where you customise columns and relation; if you need a different sort expression entirely, write a plain orderByRaw() scope instead of fighting the matcher.
  • Pin a version range (^1.1) and let patch releases like this one come in automatically — they are static-analysis and internal cleanups with no behaviour change.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle