定制 mrnewport/laravel-priceable 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

mrnewport/laravel-priceable

Composer 安装命令:

composer require mrnewport/laravel-priceable

包简介

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

README 文档

README

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

  1. Introduction
  2. Key Features
  3. Requirements
  4. Installation
  5. Configuration
  6. Migrations
  7. Usage
  8. Fallback Logic & Exception Handling
  9. Advanced: Date-Range Pricing
  10. Testing
  11. License

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

  • 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 & 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

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

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

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

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

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

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 & 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.)

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

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 & 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

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

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

  • 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

This package is open-sourced software licensed under the MIT license.

Enjoy using MrNewport/LaravelPriceable for your advanced pricing needs!

mrnewport/laravel-priceable 适用场景与选型建议

mrnewport/laravel-priceable 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 3 次下载、GitHub Stars 达 2, 最近一次更新时间为 2025 年 01 月 28 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 mrnewport/laravel-priceable 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 mrnewport/laravel-priceable 我们能提供哪些服务?
定制开发 / 二次开发

基于 mrnewport/laravel-priceable 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 3
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 16
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-01-28