PHPackages                             mrnewport/laravel-priceable - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. mrnewport/laravel-priceable

ActiveLibrary[Database &amp; ORM](/categories/database)

mrnewport/laravel-priceable
===========================

Quantity-based pricing with multi-tenant and multi-scope support for any Eloquent model.

v1.0.0(1y ago)23MITPHPPHP ^8.0

Since Jan 28Pushed 1y ago1 watchersCompare

[ Source](https://github.com/MrNewport/laravel-priceable)[ Packagist](https://packagist.org/packages/mrnewport/laravel-priceable)[ RSS](/packages/mrnewport-laravel-priceable/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (3)Versions (2)Used By (0)

mrnewport/laravel-priceable
===========================

[](#mrnewportlaravel-priceable)

**mrnewport/laravel-priceable** is a Laravel package that provides **quantity-based, multi-scope** pricing for any Eloquent model. It allows you to define tiered pricing, attach custom price lists to specific users, and apply additional “scopes” (like region or channel) without the need for external dependencies.

This README walks you through **installation**, **configuration**, **usage**, **advanced features**, and **testing**.

---

Table of Contents
-----------------

[](#table-of-contents)

1. [Introduction](#introduction)
2. [Key Features](#key-features)
3. [Requirements](#requirements)
4. [Installation](#installation)
5. [Configuration](#configuration)
6. [Migrations](#migrations)
7. [Usage](#usage)
    - [Attaching the `Priceable` Trait](#attaching-the-priceable-trait)
    - [Defining Tiered Pricing](#defining-tiered-pricing)
    - [Price Lists &amp; User-Specific Pricing](#price-lists--user-specific-pricing)
    - [Scopes (Region, Channel, etc.)](#scopes-region-channel-etc)
    - [Retrieving a Price](#retrieving-a-price)
8. [Fallback Logic &amp; Exception Handling](#fallback-logic--exception-handling)
9. [Advanced: Date-Range Pricing](#advanced-date-range-pricing)
10. [Testing](#testing)
11. [License](#license)

---

Introduction
------------

[](#introduction)

`MrNewport/LaravelPriceable` offers a complete and robust solution for **dynamic pricing** in your Laravel application. By simply adding a trait to any Eloquent model, you can define:

- **Tiered prices** based on quantity ranges.
- **Multiple price lists** for different groups (e.g., corporate, VIP, or partner).
- **Scopes** for region-, channel-, or any other custom-based pricing.
- **Optional fallback** to a default price if no specialized pricing is found.
- **Date-range validations** for time-limited pricing or promotions.

Everything is **self-contained**, including the test suite, which uses an **in-memory SQLite** database and does **not** rely on your host application’s models or migrations.

---

Key Features
------------

[](#key-features)

- **Tiered / Quantity-Based Pricing**: Price changes automatically when buyers hit certain quantity thresholds.
- **Multi-Tenant Support**: Assign price lists to specific users or entire groups.
- **Scopes**: Add region, channel, or any additional dimension to fine-tune pricing.
- **Date-Based Validity**: Start and end dates for promotional or seasonal pricing.
- **Fallback &amp; Exceptions**: Gracefully handle missing specialized prices.
- **Polymorphic**: Attach pricing to **any** Eloquent model with a single trait.
- **Self-Contained Tests**: No external references; everything is tested from within the package.

---

Requirements
------------

[](#requirements)

- **PHP**: ^8.0
- **Laravel**: ^9.0 or ^10.0
- **Database**: Any supported by Laravel (tested primarily on MySQL &amp; SQLite).

---

Installation
------------

[](#installation)

1. **Require via Composer**:

    ```
    composer require mrnewport/laravel-priceable
    ```
2. **Optional**: Publish the package config and migrations into your Laravel app (if you want to customize them in your project):

    ```
    php artisan vendor:publish --provider="MrNewport\LaravelPriceable\Providers\PriceableServiceProvider" --tag=config
    php artisan vendor:publish --provider="MrNewport\LaravelPriceable\Providers\PriceableServiceProvider" --tag=migrations
    ```
3. **Run Migrations** (if you published them or are auto-loading them):

    ```
    php artisan migrate
    ```
4. **Configure** (Optional): If you want the package to auto-load its migrations instead of publishing them, set `auto_load_migrations` to `true` in `config/priceable.php`.

---

Configuration
-------------

[](#configuration)

The package ships with a configuration file located at:

```
src/config/priceable.php

```

When published, it appears in your Laravel app’s `config/priceable.php`. Key settings include:

```
return [
    'auto_load_migrations' => false,
    'fallback_to_default_price' => true,
    'throw_exception_if_no_price_found' => false,
    'default_currency' => 'USD',
];
```

- `auto_load_migrations`: If `true`, migrations inside `src/database/migrations` will be auto-loaded by the package.
- `fallback_to_default_price`: When a user’s price list doesn’t match, fallback to the “default” price (where `price_list_id` is `null`).
- `throw_exception_if_no_price_found`: If no price is found (even after fallback), throw a `PriceNotFoundException` instead of returning `null`.
- `default_currency`: The default currency used in your price records.

---

Migrations
----------

[](#migrations)

All migration files reside in:

```
src/database/migrations/

```

If `auto_load_migrations` is set to `false` (default), you should publish and run them from your main Laravel application. Alternatively, set `auto_load_migrations` to `true` to have them loaded directly from this package.

These migrations create:

1. **price\_lists**: Names and descriptions for your custom or default price lists (e.g., “VIP”, “Corporate”).
2. **price\_list\_user**: A pivot table linking users to price lists.
3. **prices**: Stores tier-based pricing, including quantity ranges, currency, and optional date ranges.
4. **price\_scopes**: Key-value pairs (e.g., region=US, channel=B2B) for more granular pricing logic.

---

Usage
-----

[](#usage)

### Attaching the `Priceable` Trait

[](#attaching-the-priceable-trait)

Any Eloquent model you wish to have dynamic pricing must use the `Priceable` trait:

```
use MrNewport\LaravelPriceable\Models\Traits\Priceable;

class Product extends Model
{
    use Priceable;

    // Your other model code
}
```

This adds a polymorphic relationship to the `prices` table, letting you define multiple tiered prices for each product (or any other model).

---

### Defining Tiered Pricing

[](#defining-tiered-pricing)

You can define multiple **quantity ranges** by creating records in the `prices` relationship:

```
$product = Product::create([...]);

// For 1-9 units
$product->prices()->create([
    'min_quantity' => 1,
    'max_quantity' => 9,
    'unit_price'   => 100.00, // $100
]);

// For 10+ units (no upper bound)
$product->prices()->create([
    'min_quantity' => 10,
    'max_quantity' => null,
    'unit_price'   => 80.00,  // $80
]);
```

If someone purchases 5 units, the price is `$100`; for 15 units, `$80`.

---

### Price Lists &amp; User-Specific Pricing

[](#price-lists--user-specific-pricing)

If you want specialized pricing for certain users:

1. **Create a Price List**:

    ```
    use MrNewport\LaravelPriceable\Models\PriceList;

    $vipList = PriceList::create([
        'name'        => 'VIP',
        'description' => 'VIP Price List',
    ]);
    ```
2. **Attach a user** to that list:

    ```
    // Assuming $user is an instance of your User model
    $vipList->users()->attach($user->id);
    ```
3. **Add special prices** under that price list:

    ```
    $product->prices()->create([
        'price_list_id' => $vipList->id,
        'min_quantity'  => 1,
        'max_quantity'  => null,
        'unit_price'    => 60.00,
    ]);
    ```

When you call `$product->priceFor(5, $user)`, the package checks the user’s `VIP` price list first, and if found, uses `$60`.

---

### Scopes (Region, Channel, etc.)

[](#scopes-region-channel-etc)

You can attach key-value scopes to any price. For instance, define a `region=US` or `channel=B2B` scope:

```
use MrNewport\LaravelPriceable\Models\PriceScope;

$scopedPrice = $product->prices()->create([
    'min_quantity' => 1,
    'max_quantity' => null,
    'unit_price'   => 95.00,
]);

// Attach scope region=US
$scopedPrice->scopes()->create([
    'scope_type'  => 'region',
    'scope_value' => 'US',
]);
```

When retrieving the price, pass scopes as an associative array:

```
$price = $product->priceFor(10, $user, ['region' => 'US']);
```

Only prices containing **all** matching scopes (`region=US`) will be considered. If none match, it falls back as configured.

---

### Retrieving a Price

[](#retrieving-a-price)

To get a final price, call `priceFor($quantity, $user, $scopes = [])`:

```
$quantity = 12;
$user     = auth()->user(); // or any user instance
$scopes   = ['region' => 'US', 'channel' => 'B2B'];

$price = $product->priceFor($quantity, $user, $scopes);
```

Alternatively, you can use:

1. **Facade**:

    ```
    use MrNewport\LaravelPriceable\Facades\Priceable;

    $price = Priceable::getPrice($product, 12, $user, ['region' => 'US']);
    ```
2. **Helper**:

    ```
    use function MrNewport\LaravelPriceable\Support\price_for;

    $price = price_for($product, 12, $user, ['region' => 'US']);
    ```

---

Fallback Logic &amp; Exception Handling
---------------------------------------

[](#fallback-logic--exception-handling)

If a user has no custom price (or no matching scope), the system can **fallback** to a default price (`price_list_id = null`). You can control this behavior via config:

- `fallback_to_default_price`: `true` or `false`.
- `throw_exception_if_no_price_found`: If set to `true`, throws a `PriceNotFoundException` if no price is found after fallback.

This ensures you **never** end up with an undefined or zero price unless you explicitly allow it.

---

Advanced: Date-Range Pricing
----------------------------

[](#advanced-date-range-pricing)

Each price record can optionally have `valid_from` and `valid_to` fields:

```
$product->prices()->create([
    'min_quantity' => 1,
    'max_quantity' => 9,
    'unit_price'   => 100.00,
    'valid_from'   => now()->startOfMonth(),
    'valid_to'     => now()->endOfMonth(),
]);
```

If the current date/time is outside that range, the system **ignores** this price.

---

Testing
-------

[](#testing)

The tests are **completely self-contained** and use **in-memory SQLite**. No external references to your application’s models or factories.

To run tests:

```
vendor/bin/phpunit
```

### How It Works

[](#how-it-works)

- The package includes its own `tests` directory, with a `TestCase` that sets up a **test database** in memory.
- Minimal “test models” (`ProductTestModel`, `UserTestModel`) are defined, along with tables for them.
- All package migrations (`src/database/migrations`) are loaded to test every feature.

This ensures the package is thoroughly verified in isolation.

---

License
-------

[](#license)

This package is open-sourced software licensed under the [MIT license](LICENSE.md).

Enjoy using **MrNewport/LaravelPriceable** for your advanced pricing needs!

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance42

Moderate activity, may be stable

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

470d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/19dc283a8ecb45d1efbc444dc510eb63c8aab21427be09f3c1aefd507a5ab40c?d=identicon)[mrnewport](/maintainers/mrnewport)

---

Top Contributors

[![MrNewport](https://avatars.githubusercontent.com/u/48736345?v=4)](https://github.com/MrNewport "MrNewport (1 commits)")

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/mrnewport-laravel-priceable/health.svg)

```
[![Health](https://phpackages.com/badges/mrnewport-laravel-priceable/health.svg)](https://phpackages.com/packages/mrnewport-laravel-priceable)
```

###  Alternatives

[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[cybercog/laravel-love

Make Laravel Eloquent models reactable with any type of emotions in a minutes!

1.2k302.7k1](/packages/cybercog-laravel-love)[cviebrock/eloquent-taggable

Easy ability to tag your Eloquent models in Laravel.

567694.8k3](/packages/cviebrock-eloquent-taggable)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

423715.4k1](/packages/clickbar-laravel-magellan)[genealabs/laravel-pivot-events

This package introduces new eloquent events for sync(), attach(), detach() or updateExistingPivot() methods on BelongsToMany relation.

1404.9M8](/packages/genealabs-laravel-pivot-events)[reedware/laravel-relation-joins

Adds the ability to join on a relationship by name.

2121.2M13](/packages/reedware-laravel-relation-joins)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
