PHPackages                             openbuildings/shipping - 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. [API Development](/categories/api)
4. /
5. openbuildings/shipping

ActiveKohana-module[API Development](/categories/api)

openbuildings/shipping
======================

Add complex shipping to openbuildings/purchases package

0.15.0(5y ago)0165.0k2[3 issues](https://github.com/OpenBuildings/shipping/issues)[2 PRs](https://github.com/OpenBuildings/shipping/pulls)1BSD-3-ClausePHPPHP ^7.3

Since Sep 16Pushed 4y ago10 watchersCompare

[ Source](https://github.com/OpenBuildings/shipping)[ Packagist](https://packagist.org/packages/openbuildings/shipping)[ Docs](https://github.com/OpenBuildings/shipping)[ RSS](/packages/openbuildings-shipping/feed)WikiDiscussions master Synced 3d ago

READMEChangelog (10)Dependencies (8)Versions (86)Used By (1)

Purchases Shipping
==================

[](#purchases-shipping)

[![Build Status](https://camo.githubusercontent.com/5ed4c14839a087b8f810fa43b9a59eeb4d181c3f2b6fc422e8cd8dd7d92b5093/68747470733a2f2f7472617669732d63692e6f72672f4f70656e4275696c64696e67732f7368697070696e672e706e673f6272616e63683d6d6173746572)](https://travis-ci.org/OpenBuildings/shipping)[![Scrutinizer Quality Score](https://camo.githubusercontent.com/66151e8d4359316da3e3108ceda4067e864a66926c4a8112c777f0dfe70c7c3a/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f4f70656e4275696c64696e67732f7368697070696e672f6261646765732f7175616c6974792d73636f72652e706e673f733d33656335333439393935663635653661656538386162333865316166353839303632663966323635)](https://scrutinizer-ci.com/g/OpenBuildings/shipping/)[![Code Coverage](https://camo.githubusercontent.com/e2d42a4114effb710b924f7a9f7a261365cea80b778e64ce8aecb7aad5bae383/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f4f70656e4275696c64696e67732f7368697070696e672f6261646765732f636f7665726167652e706e673f733d64643162613036393339623336323237323462643537383439313764363165633564356636656363)](https://scrutinizer-ci.com/g/OpenBuildings/shipping/)[![Latest Stable Version](https://camo.githubusercontent.com/55541f640f2986f7e8453c51060c1bc02c9257be44f3e1b603a7e0f77a7e85c1/68747470733a2f2f706f7365722e707567782e6f72672f6f70656e6275696c64696e67732f7368697070696e672f762f737461626c652e706e67)](https://packagist.org/packages/openbuildings/shipping)

Usage
-----

[](#usage)

Purchase\_items that you want to ship must implement Shippable, like this:

```
class Model_Product extends Jam_Model implements Sellable, Shippable {

	public static function initialize(Jam_Meta $meta)
	{
		$meta
			->associations(array(
				'shipping' => Jam::association('belongsto', array('inverse_of' => 'products')),
			))
			->fields(array(
				'id' => Jam::field('primary'),
				'name' => Jam::field('string'),
				'currency' => Jam::field('string'),
				'price' => Jam::field('price'),
			))
			->validator('type', 'price', 'quantity', array(
				'present' => TRUE
			));
	}

	// Implement Sellable
	public function price(Model_Purchase_Item $item)
	{
		return $this->price;
	}

	// Implement Sellable
	public function currency()
	{
		return $this->currency;
	}

	// Implement Shippable
	// Must return a ``Model_Shipping`` object holding all the data for the shipping
	public function shipping()
	{
		return $this->shipping;
	}

	// Implement Shippable
	// Must return a boolean whether or not the product ships can to that location
	public function ships_to(Model_Location $location)
	{
		return $this->shipping ? $this->shipping->ships_to($location) : FALSE;
	}
}

class Model_Purchase_Item_Product extends Kohana_Model_Purchase_Item_Product {

    public static function initialize(Jam_Meta $meta)
    {
        parent::initialize($meta);
        $meta
            ->behaviors(array(
                'shippable_purchase_item' => Jam::behavior('shippable_purchase_item'),
            ));
    }
}

class Model_Shipping extends Kohana_Model_Shipping {

	public static function initialize(Jam_Meta $meta)
	{
		parent::initialize($meta);

		$meta
			->associations(array(
				'products' => Jam::association('hasmany', [
                    'inverse_of' => 'shipping',
                ]),
			));
	}
}
```

Also you need to add the shippable purchase to your purchase and brand purchase:

```
class Model_Brand_Purchase extends Kohana_Model_Brand_Purchase {

	public static function initialize(Jam_Meta $meta)
	{
		parent::initialize($meta);
		$meta
			->behaviors(array(
				'shippable_brand_purchase' => Jam::behavior('shippable_brand_purchase'),
			));
	}
}

class Model_Purchase extends Kohana_Model_Purchase {

	public static function initialize(Jam_Meta $meta)
	{
		parent::initialize($meta);
		$meta
			->behaviors(array(
				'shippable_purchase' => Jam::behavior('shippable_purchase'),
			));
	}
}
```

This behaviors will add the 'shipping' association to the brand\_pruchase, also listen to update\_items event and add a shipping purchase\_item, and listen to the filter\_items event, adding some more flags to filter by.

Once you have added the shipping data to your products:

```
$post = Jam::find('shipping_method', 'Post');
$europe = Jam::find('location', 'Europe');
$france = Jam::find('location', 'France');

$product->shipping = Jam::create('shipping', array(
	'currency' => 'GBP',
	'ships_from' => $france,
	'groups' => array(

		// Ships to all of Europe for 20 GBP
		array('method' => $post, 'location' => $europe, 'price' => 20),

		// Specifically for France - only 10 GBP
		array('method' => $post, 'location' => $france, 'price' => 10),
	)
));
```

You can start to select which shipping applies to each purchase item.

```
$brand_purchase = Jam::find('brand_purchase', 1);

// If you want to set the informaction explicitly on which purchase_item what shipping_group to use
$brand_purchase->build('shipping', array(
	'items' => array(
		array(
			'purchase_item' => $brand_purchase->items[0],
			'shipping_group' => $brand_purchase->items[0]->reference->shipping()->groups[0],
		),
	)
));

// Or if you want ones selected automatically, based on a preffered shipping method and purchaser location
$post = Jam::find('shipping_method', 'Post');
$france = Jam::find('location', 'France');

$brand_purchase_shipping = $brand_purchase->build('shipping', array(
	'location' => $france,
));

$brand_purchase_shipping->build_items_from($brand_purchase->items, $post);
```

Having configured that, you can now call `update_items()` method on the purchase / brand\_purchase, adding to the purchase\_items a shipping item.

```
$brand_purchase->update_items();

echo $brand_purchase->items_count('shipping'); // should return 1
```

### Shipping Groups and Price Calculations

[](#shipping-groups-and-price-calculations)

Each shipping group has several properties that affect how much muney the shipping of this item will cost:

- **price** - this is the base price for shipping of 1 item.
- **additional\_item\_price** - for more than one item, the second, third, etc items require this price, instead of the base one.
- **discount\_threshold** - whenever the brand\_purchase is more than this amount - free shipping

Here are some examples:

If an item costs 10, with additional\_item\_price of 6, then you will pay 10+6+6 for 3 of the same item.

Also items are grouped per shipping method, per "ships\_from" location so 3 different item shipped by post will be grouped. Only the most expensive base price will be used, all others will use additional\_item\_price. So:

```
Item 1: price 10, additional_item_price 6, quantity: 3
Item 2: price 12, additional_item_price 8, quantity: 2

Total Price will be (12 + 8) + 6 * 3

```

When searching for a country, the most specific one will be used for calculation, so if you are shipping for France, and you have a shipping\_group for Europe, and one for France, the second one will be used.

### Advanced Item Splitting

[](#advanced-item-splitting)

If you want to allow people to use different methods for different products, here is how you might accomplish this:

First of all - finding all purchase\_items that can / cannot ship to your country

```
$available = $brand_purchase->items(array('can_ship' => TRUE));
$not_shippable = $brand_purchase->items(array('can_ship' => FALSE));
```

If you want to be more precise, you can get available items, but grouped by available shipping methods, so that if you have purchase\_items that can ship with both *post* and *courier* and other that can ship only with *post*, they will be in different groups:

```
$group_shipping_methods = $brand_purchase->group_shipping_methods()
foreach ($group_shipping_methods as $group)
{
	foreach ($group->group_shipping_items() as $items)
	{
		// Get all the purchase items, shippable to this location by this method.
		$items->purchase_items;

		// Calculate the price of these items, provide a total price to remove ones that are discounted based on it.
		$items->total_price();

		// Delivery
		$items->total_delivery_time();
	}
}
```

### Delivery Times

[](#delivery-times)

This shipping module comes with extensive support for calculating delivery times.

Model\_Shipping\_Group has "delivery\_time" - min - max workdays to deliver the item. Model\_Shipping has process\_time - min - max workdays to 'build' the item. Both of these are Jam\_Ranges, and combined represent the total\_delivery\_time for a specific location.

Model\_Shipping has this interface:

```
$france = Jam::find('locaiton', 'France');

$shipping = $product->shipping();

// To get a Jam_Range object only for the delivery to that location
$shipping->delivery_time_for($france);

// To get a Jam_Range for delivery + processing for a specific country
$shipping->total_delivery_time_for($france);
```

The shippable purchase behavior also adds some methods to the Model\_Brand\_Purchase for handling delivery time calculations:

```
// Get the Jam_Range object for the delivery time for the brand_purchase
$brand_purchase->total_delivery_time();

// Get the Jam_Range object if the dates that the purchase will arraive
// This is calculate based on the time the payment was made. If its not yet payed purchase, the current time is used.
$brand_purchase->delivery_time_dates();
```

### Shipping Address

[](#shipping-address)

By default as shipping address is used the billing address of the purchase. If you want to change that address, you'll have to change the `shipping_same_as_billing` field and set the shipping\_address association, which is the same Model\_Address object. After that all the calculation will take the country of shiping\_address instead of billing\_address:

```
$purchase = Jam::find('purchase', 1);

$purchase->shipping_same_as_billing = FALSE;
$purchase->build('shipping_address', array(
  'country' => Jam::find('country', 'United Kingdom'),
  'line1' => 'Street 1',
  // ...
));

## License

Copyright (c) 2012-2013, OpenBuildings Ltd. Developed by Ivan Kerin as part of [clippings.com](http://clippings.com)

Under BSD-3-Clause license, read LICENSE file.
```

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity26

Limited adoption so far

Community25

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

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

Every ~33 days

Recently: every ~286 days

Total

81

Last Release

1983d ago

PHP version history (4 changes)0.1.0PHP &gt;=5.3.0

0.11.3PHP ^7

0.13.0PHP ^7.1

0.15.0PHP ^7.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/506129?v=4)[Harry Dobrev](/maintainers/hkdobrev)[@hkdobrev](https://github.com/hkdobrev)

![](https://avatars.githubusercontent.com/u/4113307?v=4)[Danail Kyosev](/maintainers/dkyosev)[@dkyosev](https://github.com/dkyosev)

![](https://avatars.githubusercontent.com/u/7592650?v=4)[Evstati Zarkov](/maintainers/EZarkov)[@EZarkov](https://github.com/EZarkov)

![](https://avatars.githubusercontent.com/u/745771?v=4)[Filip Georgiev](/maintainers/phgeorgiev)[@phgeorgiev](https://github.com/phgeorgiev)

![](https://avatars.githubusercontent.com/u/490439?v=4)[Zdravko Evstatiev](/maintainers/zedevs)[@zedevs](https://github.com/zedevs)

---

Top Contributors

[![hkdobrev](https://avatars.githubusercontent.com/u/506129?v=4)](https://github.com/hkdobrev "hkdobrev (61 commits)")[![ivank](https://avatars.githubusercontent.com/u/4976?v=4)](https://github.com/ivank "ivank (30 commits)")[![dkyosev](https://avatars.githubusercontent.com/u/4113307?v=4)](https://github.com/dkyosev "dkyosev (18 commits)")[![phgeorgiev](https://avatars.githubusercontent.com/u/745771?v=4)](https://github.com/phgeorgiev "phgeorgiev (11 commits)")[![EZarkov](https://avatars.githubusercontent.com/u/7592650?v=4)](https://github.com/EZarkov "EZarkov (5 commits)")[![stanstst](https://avatars.githubusercontent.com/u/9391542?v=4)](https://github.com/stanstst "stanstst (5 commits)")[![george-clippings](https://avatars.githubusercontent.com/u/67631863?v=4)](https://github.com/george-clippings "george-clippings (4 commits)")[![tumbalev](https://avatars.githubusercontent.com/u/28398671?v=4)](https://github.com/tumbalev "tumbalev (4 commits)")

---

Tags

addresskohanapurchasecartshippingjam

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/openbuildings-shipping/health.svg)

```
[![Health](https://phpackages.com/badges/openbuildings-shipping/health.svg)](https://phpackages.com/packages/openbuildings-shipping)
```

###  Alternatives

[shippo/shippo-php

A PHP library for connecting with multiple carriers (FedEx, UPS, USPS) using Shippo.

1711.8M2](/packages/shippo-shippo-php)[gabrielbull/ups-api

PHP UPS API

4642.4M10](/packages/gabrielbull-ups-api)[easypost/easypost-php

EasyPost Shipping API Client Library for PHP

1753.1M5](/packages/easypost-easypost-php)[firstred/postnl-api-php

PostNL REST API PHP Bindings

27610.2k1](/packages/firstred-postnl-api-php)[jcf/geocode

Google Geocoding API for Laravel

48160.5k](/packages/jcf-geocode)

PHPackages © 2026

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