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 Royalty Laravel Package

miracuthbert/laravel-royalty

Laravel Royalty helps you calculate, track, and distribute royalties in Laravel apps. It offers model traits, flexible configuration, and tools for managing earnings and payouts across users, creators, or referral programs.

View on GitHub
Deep Wiki
Context7

Getting Started

This release is a compatibility and extensibility update rather than a feature drop: the package now declares support for Laravel 7 through 10, and the Point model is swappable. That second change is the one that will affect your day-to-day work, because it moves the model from "package internals" to "your application's domain."

Start here, in this order:

  1. composer.json constraint check. Confirm your app's Laravel version falls inside the newly widened range. If you are on Laravel 11+, this release does not help you — the upper bound is 10.
  2. The published config file. Look for the model binding key (typically point_model or models.point). This is the single knob the 1.4.0 release added. If you have not published config yet, publish it now before you write any code that references Point.
  3. The package migration. Read it before you run it. You need to know the table name, the column types (are coordinates decimal, double, or spatial POINT?), and whether indexes exist — because your custom model will inherit that schema and you cannot change it without a second migration.
  4. The base Point class. Open the vendor copy once. You want to see which methods are protected, which are static, and whether there are query scopes. That determines what a subclass can and cannot override safely.

Your first real use case is the canonical one: attach a coordinate to an existing Eloquent model and query by proximity.

$place = Place::find(1);
$place->point()->create(['lat' => 51.5074, 'lng' => -0.1278]);

// later
$nearby = Point::distanceFrom($lat, $lng, radius: 5)->get();

Adapt the method names to whatever the base class actually exposes — the shape is what matters. Get this working end-to-end against the default model before you touch the custom-model feature. Debugging two variables at once is where afternoons go.

Implementation Patterns

Define the custom model early, not later

The most expensive mistake with a swappable model is swapping it mid-project. Every row already written belongs to the package's table, and the base class's query scopes, casts, and relationships were written against the base class's assumptions. Retrofitting means auditing every Point:: call site.

Do it on day one:

namespace App\Models;

use Vendor\Package\Models\Point as BasePoint;

class Point extends BasePoint
{
    protected $table = 'points'; // keep the package table unless you migrate data

    protected $fillable = ['lat', 'lng', 'pointable_id', 'pointable_type'];

    public function pointable()
    {
        return $this->morphTo();
    }
}

Then bind it in config:

// config/points.php
'point_model' => App\Models\Point::class,

The binding is the contract. Code inside the package that does new Point or Point::query() should resolve through the config — verify this by grepping the vendor directory for Point:: once. Any hard-coded reference to the base class is a place where your custom model will silently not be used, and you will get base-class instances back from relationships. That failure is quiet and confusing.

Guard the binding with a test

Add a single assertion to your suite. It costs nothing and catches the day a package update changes the resolution path:

public function test_package_resolves_custom_point_model(): void
{
    $this->assertInstanceOf(
        \App\Models\Point::class,
        app(config('points.point_model'))
    );
}

Use the custom model as an extension point, not a rewrite

The valuable overrides are additive: accessors that format coordinates for your API, a scopeWithinBoundingBox for cheap pre-filtering, relationships back to your domain models, and casts.

protected $casts = [
    'lat' => 'float',
    'lng' => 'float',
];

public function scopeBoundingBox($query, float $lat, float $lng, float $km)
{
    $delta = $km / 111.0;

    return $query->whereBetween('lat', [$lat - $delta, $lat + $delta])
                 ->whereBetween('lng', [$lng - $delta, $lng + $delta]);
}

Avoid overriding the constructor, boot, or booted unless you have read the base implementation. Trait bootstrapping order in Eloquent means a parent booted() that you shadow will drop global scopes without any error.

Pair the custom model with a repository or scope layer

Because the model is now yours, business logic will accrete there. Keep proximity math in a dedicated query object or in scopes, and keep the model thin. When the package ships its next breaking change to the base class, a thin subclass is a five-minute upgrade; a fat one is a rewrite.

Migrate existing data deliberately if you change tables

If your custom model points at a new table, write a data migration that copies rows, and keep the package's original table intact for at least one deploy. Do not drop it in the same release. Rollback paths matter more for coordinate data than for most things, because re-geocoding is slow and sometimes not reproducible.

Gotchas and Tips

The Laravel ceiling is 10. If you are planning a Laravel 11 upgrade, put it on the calendar before you lean on this package. Widened support from 7 to 10 is progress, but it also tells you the maintainer's release cadence trails framework releases. Budget for a fork or a replacement rather than assuming a quick 11-compatible patch.

"Bump up support from 7 to 10" is ambiguous — verify locally. It could mean the minimum was raised to 7 (dropping Laravel 6) or that the ceiling was raised to 10. Run composer why-not vendor/package and read the resolved constraint in composer.lock. If you are still on Laravel 6 or 7, check whether this release actually drops you.

Custom model resolution is usually config-driven, and config is cached. After changing point_model, run php artisan config:clear (or config:cache in production). A stale cached config is the single most common reason a swapped model "doesn't work." It produces no warning — you just get base-class instances.

Relationships may still return the base class. If a HasMany or morphMany on another model was defined with a hard-coded class string, Eloquent will hydrate that class regardless of your config binding. Change the relationship to reference the config value or your own class. Check every morphMany(Point::class, ...) in your codebase.

Type-hint against your class, not the vendor's. Once you subclass, function foo(BasePoint $p) will accept your model (fine), but the reverse — a method returning the base type consumed where you expect App\Models\Point — will throw. Be consistent in your own signatures.

Spatial columns and casting. If the migration uses a native spatial POINT type, Eloquent cannot hydrate it without a custom cast, and $point->lat will be null. Check whether the package ships a cast and whether your subclass needs to redeclare it. Silent nulls here look like "the custom model broke it" when in fact the cast never existed.

Debugging proximity queries. Log the raw SQL. Haversine expressions are easy to get subtly wrong (degrees vs. radians, acos domain errors from floating-point drift). Wrap the cosine term in LEAST(1, ...) or the query will intermittently return nothing for points at identical coordinates.

Indexes are on you. The package migration likely indexes what it needs; any columns you add in your own migration need their own indexes. A whereBetween on unindexed lat/lng degrades badly past a few hundred thousand rows.

Prefer bounding-box filtering before distance math. Compute a cheap lat/lng box, then apply the exact distance function to the survivors. The custom model is the correct place to add that scope.

Watch the base class across upgrades. Since 1.4.0 makes subclassing a supported path, the maintainer may start marking base methods final or protected differently. Pin the version and read the diff on each bump — a method that flips to final breaks your override with a fatal error, not a deprecation notice.

Extension points beyond the model. Check whether config also exposes table name, connection, or an event/observer hook. If it does not, that is your upgrade request to file upstream rather than something to hack around with a service provider rebind.

Test against the lowest supported Laravel once. If you support a range of apps, run CI against 7 and 10 at least. A widened constraint is exactly where version-specific behavior sneaks in unnoticed.

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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
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