抽象类抽象变量_抽象运输API

抽象类抽象变量

You have your new custom e-commerce store almost finished. The only thing left is to figure out how to calculate shipping charges for your customers. You don’t want to go with a standard flat rate to every address because you know you’ll be over charging some customers, and more importantly under charging others. Wouldn’t it be great shipping charges could be calculated based on the weight/size of the item(s) and the destination? Maybe you could even offer an accurate price quote for overnight shipping!

您的新自定义电子商务商店即将完工。 剩下的唯一事情就是弄清楚如何为您的客户计算运费。 您不想对每个地址都使用标准的固定费率,因为您知道自己要向某些客户收费,更重要的是要向其他客户收费。 可以根据商品的重量/大小和目的地来计算运费吗? 也许您甚至可以为隔夜运输提供准确的报价!

You have a UPS account, and you’ve checked out their API, but it looks pretty complex. If you hard code your site to use the API, you’ll be up for a lot of work if you need to change shippers. Your cousin is a sales rep with FedEx and he swears he can get you better rates with them. Some of your customers only us PO Boxes, so those items have to be shipped by the Post Office. What do you do?

您有一个UPS帐户,并且已经签出了他们的API,但是看起来很复杂。 如果您对网站进行硬编码以使用API​​,那么在需要更换托运人的情况下,您将需要进行大量工作。 您的表弟是FedEx的销售代表,他发誓他可以为他们提供更好的价格。 您的某些客户仅使用邮政信箱,因此这些物品必须由邮局寄出。 你是做什么?

You may have heard of database abstraction, a practice which allows you to use many different databases with a common set of commands. That’s exactly what you can do here! To solve all of these problems, you can decouple the shipping task from the rest of your code and build an abstraction layer. Once you’re finished, it won’t matter if you’re shipping a package by UPS, FedEx, or the USPS. The functions your core application will invoke will all be the same, and that makes your life a lot easier!

您可能听说过数据库抽象,这种实践使您可以通过一组通用命令使用许多不同的数据库。 这就是您在这里可以做的! 为了解决所有这些问题,您可以将运输任务与其余代码分离,并构建一个抽象层。 完成后,由UPS,FedEx或USPS运送包裹都没有关系。 您的核心应用程序将调用的功能都是相同的,这使您的生活更加轻松!

UPS入门 (Getting Started with UPS)

In this article I’ll focus on using the UPS API, but by writing a plugin for different shippers (such as FedEx or USPS), you can access their services as well with only negligible, if any, code changes to your core application.

在本文中,我将重点介绍如何使用UPS API,但是通过为不同的托运人(例如FedEx或USPS)编写插件,您也可以仅对核心应用程序进行微不足道的代码更改(如果有的话)来访问其服务。

In order to get started with using UPS, you will need to sign up for an online account at www.ups.com using your existing shipper number. Make sure to pick a username and password that you will be comfortable using for a while as the API requires both of them for every call. Next, go to https://www.ups.com/upsdeveloperkit and register for access to the UPS API. This is where you will obtain your API key and are able to download documentation for the different API packages. (Note: There’s a known issue with this section of UPS’s site and Chrome will sometimes return a blank page. You may need to use an alternate browser.)

为了开始使用UPS,您需要使用现有的托运人号码在www.ups.com上注册一个在线帐户。 请确保选择一个您会习惯使用一段时间的用户名和密码,因为API在每次调用时都要求同时使用它们。 接下来,转到https://www.ups.com/upsdeveloperkit并注册以访问UPS API。 在这里,您将获得API密钥,并能够下载不同API软件包的文档。 (注意:UPS网站的此部分存在一个已知问题,Chrome有时会返回空白页。您可能需要使用其他浏览器。)

Keep in mind that when you use the UPS API (or any shipper’s API for that matter), you agree to follow their rules and procedures. Make sure to review and follow them, especially including their instructions before using your code in production.

请记住,当您使用UPS API(或与此有关的任何托运人的API)时,您同意遵守其规则和程序。 在生产中使用代码之前,请确保查看并遵循它们,尤其是包括它们的说明。

Next download or clone the shipping abstraction layer package from GitHub at github.com/alexfraundorf-com/ship and upload it to your server that is running PHP 5.3 or later. Open the includes/config.php file. You’ll need to enter your UPS details here, and the field names should all be self-explanatory. Note that the UPS shipper address needs to match what UPS has on file for your account or an error will occur.

接下来,从github.com/alexfraundorf-com/ship上的GitHub下载或克隆装运抽象层软件包,然后将其上传到运行PHP 5.3或更高版本的服务器。 打开includes/config.php文件。 您需要在此处输入您的UPS详细信息,字段名称都应该是不言自明的。 请注意,UPS托运人地址必须与您帐户中UPS归档的地址匹配,否则会发生错误。

定义货件和包裹 (Defining Shipments and Packages)

Now to define a Shipment object. At instantiation, it will accept an array containing the receiver’s information, and optionally a ship from address if it is different from the shipper’s information in our config file.

现在定义一个Shipment对象。 在实例化时,它将接受一个包含接收者信息的数组,如果它与我们的配置文件中的托运人信息不同,则可以选择从地址寄出。

<?php
// create a Shipment object
$shipment = new ShipShipment($shipmentData);

Next we need some details about what we are shipping. Let’s create a Package object which accepts the weight, package dimensions, and an optional array of some basic options such as a description, if a signature is required, and the insured amount. The newly instantiated Package(s) are then added to the Shipment object. Software imitating life just makes sense: Every package belongs to a shipment, and every shipment must have at least one package.

接下来,我们需要有关所运送物品的一些详细信息。 让我们创建一个Package对象,该对象接受重量,包裹尺寸以及一些基本选项的可选数组,例如描述,是否需要签名以及保险金额。 然后将新实例化的Package添加到Shipment对象。 模仿生命的软件很有意义:每个包裹都属于一个货件,并且每个货件必须至少具有一个包裹。

<?php
// create a Package object and add it to the Shipment (a 
// shipment can have multiple packages)

// this package is 24 pounds, has dimensions of 10 x 6 x 12
// inches, has an insured value of $274.95, and is being
// sent signature required
$package1 = new ShipPackage(
    24,
    array(10, 6, 12),
    array(
        'signature_required' => true,
        'insured_amount' => 274.95
    )
);
$shipment->addPackage($package1);

// weight and dimensions can be integers or floats,
// although UPS always rounds up to the next whole number.
// This package is 11.34 pounds and has dimensions of
// 14.2 x 16.8 x 26.34 inches
$package2 = new ShipPackage(
    11.34,
    array(14.2, 16.8, 26.34)
);
$shipment->addPackage($package2);

幕后:货运对象 (Behind the Curtain: The Shipment Object)

Open Awsp/Ship/Shipment.php and we’ll examine the Shipment object, which basically will hold everything that our shipper plugins need to know about the shipment.

打开Awsp/Ship/Shipment.php ,我们将检查Shipment对象,该对象基本上将保存我们的托运人插件需要了解的有关托运的所有内容。

The constructor accepts an array of the shipment data (and stores it as an object property) which is the receiver’s information and optionally the ship from information if it differs from the shipper’s address. Next the constructor calls sanitizeInput() to make sure that array is safe to use, and isShipmentValid() to make sure that all required information has been provided.

构造函数接受装运数据的数组(并将其存储为对象属性),该数组是接收者的信息,如果信息与托运人的地址不同,则可以选择从信息中获取船。 接下来,构造函数调用sanitizeInput()来确保该数组可以安全使用,并isShipmentValid()来确保已提供所有必需的信息。

Besides that, we have a public method get() which accepts a field name (array key) and returns the corresponding value from the shipment data array, and the public functions addPackage() and getPackages() to, you guessed it, add a package to the shipment and retrieve the Package objects that belong to the shipment.

除此之外,我们有一个公共方法get() ,它接受一个字段名(数组键)并从货运数据数组中返回相应的值,而您猜到的公共函数addPackage()getPackages()要添加一个打包到货件并检索属于该货件的Package对象。

幕后:包装对象 (Behind the Curtain: The Package Object(s))

Open Awsp/Ship/Package.php and we’ll examine the Package object, which basically will hold everything that our shipper plugins need to know about the individual package. Package objects are part of the Shipment object, and the Shipment can have as many Package objects as needed.

打开Awsp/Ship/Package.php ,我们将检查Package对象,该对象基本上将保存我们的托运人插件需要了解的有关单个软件包的所有内容。 Package对象是Shipment对象的一部分,并且Shipment可以根据需要具有多个Package对象。

The Package constructor accepts the package weight, dimensions (in any order), and an optional array of options such as description, type, insured amount, and whether a signature is required. The weight and options are set in object properties and the dimensions are put in order from longest to shortest. We then assign them in order to the object properties $length, $width, and $height. This is important to the shipper plugins because length must always be the longest dimension. It then uses isPackageValid() to make sure all needed parameters are present and of the proper type. Finally calculatePackageSize() is used to figure out the package’s size (length plus girth), which will be used by some shipper plugins.

Package构造函数接受包装重量,尺寸(以任何顺序排列)以及可选的可选数组,例如描述,类型,保险金额以及是否需要签名。 权重和选项在对象属性中设置,尺寸按从最长到最短的顺序排列。 然后,我们将它们分配给对象属性$length$width$height 。 这对于托运人插件很重要,因为长度必须始终是最长的尺寸。 然后,它使用isPackageValid()来确保所有必需的参数都存在并且类型正确。 最后, calculatePackageSize()用于确定包装的尺寸(长度加周长),某些托运人插件将使用该尺寸。

Other public functions available from the Package object are get() which returns a property of the object, getOption() which returns a specific option’s setting, and several helper functions for converting weight and length for the shipper plugins.

Package对象提供的其他公共功能包括: get()返回对象的属性; getOption()返回特定选项的设置;以及几个帮助函数,用于转换托运人插件的重量和长度。

托运人插件 (Shipper Plugins)

We have a shipment with packages, and now we need to access the shipper plugin that we want to use. The plugin will accept the Shipment object along with the $config array (defined in includes/config.php).

我们有一个带有包装的货件,现在我们需要访问要使用的托运人插件。 该插件将接受Shipment对象以及$config数组(在includes/config.php定义)。

<?php
// create the shipper object and pass it the shipment
// and config data
$ups = new ShipUps($shipment, $config);

Our Ups object, or any other shipper plugin we create later, will implement the ShipperInterface, our contract that allows us to guarantee that no matter which shipper we use, the public functions (interface) will always be the same. As shown in this excerpt from ShipperInterface.php, all of our shipper plugins must have a setShipment() method to set a reference to the Shipment object, a setConfig() method to set a copy of the config array, a getRate() method to retrieve a shipping rate, and a createLabel() method to create a shipping label.

我们的Ups对象或我们稍后创建的任何其他运货方插件将实现ShipperInterface ,这是我们的合同,该合同使我们能够保证,无论我们使用哪个运货方,公共功能(接口)都将始终相同。 如此ShipperInterface.php摘录所示,我们所有的setShipment()插件都必须具有setShipment()方法来设置对Shipment对象的引用, setConfig()方法来设置配置数组的副本, getRate()方法。检索运费,并使用createLabel()方法创建运送标签。

<?php
interface ShipperInterface
{
    public function setShipment(Shipment $Shipment);
    public function setConfig(array $config);
    public function getRate();
    public function createLabel();
}

取得运费 (Fetching Shipping Rates)

In order to calculate the shipping rates for our package, we’ll call the getRate() method of our Ups object. Since it will be performing network calls, we’ll need to make sure to wrap it in a try/catch block in case something goes wrong.

为了计算包裹的运费,我们将调用Ups对象的getRate()方法。 由于它将执行网络调用,因此我们需要确保将其包装在try / catch块中,以防出现问题。

Assuming that there are no errors with our data, the Ups object organizes our information into a format that the UPS API recognizes, sends it off, and processes the response into a RateResponse object that will be uniform for all the shippers we incorporate.

假设我们的数据没有错误, Ups对象将我们的信息组织成一种UPS API识别的格式,将其发送出去,并将响应处理为RateResponse对象,该对象对于我们纳入的所有托运人都是统一的。

<?php
// calculate rates for shipment - returns an instance of 
// RatesResponse
try {
    $rates = $ups->getRate();
}
catch(Exception $e) {
    exit('Error: ' . $e->getMessage());
}

We can loop through the services array then to display the available shipping options:

我们可以遍历服务数组,然后显示可用的运送选项:

<!-- output rates response -->
<dl>
 <dt><strong>Status</strong></dt>
 <dd><?php echo $rates->status; ?></dd>
 <dt><strong>Rate Options</strong></dt>
 <dd>
  <ul>
<?php
foreach ($rates->services as $service) {
    // display the service, cost, and a link to create the
    // label
    echo '<li>' . $service['service_description'] . ': ' .
        '$' . $service['total_cost'] .
        ' - <a href="?action=label&service_code=' .
        $service['service_code'] . '">Create Label</a></li>';
?>
   <li>Service Message:
    <ul>
<?php
    // display any service specific messages
    foreach($service['messages'] as $message) {
        echo '<li>' . $message . '</li>';
    }
?>
    </ul>
   </li>
<?php
    // display a breakdown of multiple packages if there are
    // more than one
    if ($service['package_count'] > 1) {
?>
   <li>Multiple Package Breakdown:
    <ol>
<?php
        foreach ($service['packages'] as $package) {
            echo '<li>$' . $package['total_cost'] . '</li>';
        }
?>        
    </ol>
   </li>
<?php
    }
}
?>
  </ul>
 </dd>
</dl>

幕后:RateResponse对象 (Behind the Curtain: The RateResponse Object)

The RateResponse object is essentially a simple object that contains our rate data in a standardized format, so that no matter which shipper plugin we use, the object (and therefore how we interface with it) will always be the same. That is the true beauty of abstraction!

RateResponse对象本质上是一个简单的对象,它以标准化格式包含我们的费率数据,因此,无论我们使用哪个托运人插件,该对象(以及我们与之交互的方式)都将始终相同。 那就是抽象的真正美!

If you open Awsp/Ship/RateResponse.php you will see that the object simply holds a property called $status which will always be ‘Success’ or ‘Error’ and an array called $services. There will be an element in this array for each shipping option returned by the shipper plugin, and each element will contain ‘messages’, ‘service_code’, ‘service_description’, ‘total_cost’, ‘currency’, ‘package_count’, and an array called ‘packages’ that holds the following data for each package: ‘base_cost’, ‘option_cost’, ‘total_cost’, ‘weight’, ‘billed_weight’ and ‘weight_unit’.

如果打开Awsp/Ship/RateResponse.php您将看到该对象仅拥有一个名为$status的属性,该属性将始终为'Success'或'Error'和一个名为$services的数组。 托运人插件返回的每个送货选项在此数组中都有一个元素,每个元素将包含“消息”,“ service_code”,“ service_description”,“ total_cost”,“ currency”,“ package_count”和一个数组称为“包装”,其中包含每个包装的以下数据:“基本成本”,“选项成本”,“总成本”,“重量”,“计费重量”和“重量单位”。

With the data contained in and easily extracted from the RateResponse object, you should have everything you need to supply your customer with the shipping rate options.

通过RateResponse对象中包含的数据并轻松地从中提取数据,您应该拥有为客户提供运费选项所需的一切。

创建运输标签 (Creating a Shipping Label)

Because of the abstracted API, your customer was impressed by the customized shipping options you were able to provide, and they made a purchase. You have processed their order and are ready to create the shipping label. Ideally, all we need to do is call createLabel() on the Shipper object, and pass it the desired shipping option.

由于使用了抽象的API,您的客户对您能够提供的定制运输方式印象深刻,因此他们进行了购买。 您已经处理了他们的订单,并准备创建运输标签。 理想情况下,我们要做的就是在Shipper对象上调用createLabel() ,并将其传递给所需的运送选项。

<?php
// set label parameters
$params['service_code'] = '03'; // ground shipping

// send request for a shipping label 
try {
    // return the LabelResponse object
    $label = $ups->createLabel($params);
}
catch (Exception $e){
    exit('Error: ' . $e->getMessage());
}

Unless there was a problem with the data, a LabelResponse object will be returned containing the status of the request, the total cost of the shipment, and an array containing a tracking number and base-64 encoded image of the label, and the type of image (GIF in the case of UPS) for each shipping label.

除非数据有问题, LabelResponse将返回一个LabelResponse对象,其中包含请求的状态,运输的总成本以及一个包含跟踪号和标签的base-64编码图像的数组,以及每个运输标签的图片(对于UPS,则为GIF)。

幕后:LabelResponse对象 (Behind the Curtain: The LabelResponse Object)

Similar to the RateResponse object, the LabelResponse object is a simple object that contains our label data in a standardized format, so that no matter which shipper plugin we use, the object (and therefore how we interface with it) will always be the same. Abstraction is awesome!

RateResponse对象类似, LabelResponse对象是一个简单的对象,它以标准化格式包含我们的标签数据,因此无论我们使用哪个运货工具插件,该对象(以及我们与之接口的方式)都将始终相同。 抽象很棒!

If you open Awsp/Ship/LabelResponse.php you will see that the object simply holds properties called $status which will always be ‘Success’ or ‘Error’, $shipment_cost which is the total cost of the shipment, and an array called $labels. There will be an element in this array for each label, each of which is an array containing ‘tracking_number’ and ‘label_image’ which is the base-64 encoded label image, and ‘label_file_type’ indicating the type of image it is (our UPS labels are GIF images).

如果打开Awsp/Ship/LabelResponse.php您将看到该对象仅包含名为$status属性,该属性将始终为'Success'或'Error', $shipment_cost$shipment_cost的总成本,以及一个名为$labels的数组$labels 。 每个标签在此数组中都会有一个元素,每个元素都是一个数组,其中包含“ tracking_number”和“ label_image”,这是基于base-64编码的标签图像,“ label_file_type”指示其图像类型(我们的UPS标签是GIF图片)。

With the data contained in and easily extracted from the LabelResponse object, you will have everything you need to extract, print and save your tracking number(s) and label(s).

借助包含在LabelResponse对象中并从中轻松提取的数据,您将拥有提取,打印和保存跟踪号和标签所需的一切。

幕后:UPS Shipper插件 (Behind the Curtain: The UPS Shipper Plugin)

The job of the shipper plugin, Awsp/Ship/Ups.php in our case, is to take our standardized input in the Package and Shipment objects and convert it into a form that is understood by the shipper API. UPS offers their API in two flavors, SOAP and XML-RPC and updates them as needed in July and December of each year. This plugin uses the December 2012 version of the SOAP API and you will need to make sure that the SoapClient class is enabled in your PHP installation.

在我们的案例中,托运程序插件Awsp/Ship/Ups.php工作是在PackageShipment对象中获取我们的标准化输入,并将其转换为托运人API可以理解的形式。 UPS提供两种SOAP和XML-RPC的API,并在每年的7月和12月根据需要对其进行更新。 该插件使用2012年12月版的SOAP API,您需要确保在PHP安装中启用了SoapClient类。

After accepting and processing the Shipment object, which contains the Package object(s), and the $config array (from includes/config.php), the constructor sets some object properties and some values common to all API requests.

接受并处理包含Package对象和包含$config数组(来自includes/config.php )的Shipment对象之后,构造函数将设置一些对象属性和所有API请求通用的值。

The other public functions getRate() and createLabel() handle the work of assembling all of that data into a complex array that UPS will understand. Each of these methods then calls on sendRequest() to send the SOAP request to the UPS API and retrieve the response. An assortment of protected functions then do the dirty work of translating the SOAP response into our standardized RateResponse or LabelResponse objects depending on what was requested.

其他公共函数getRate()createLabel()处理将所有数据组装成UPS可以理解的复杂阵列的工作。 然后,这些方法中的每一个都调用sendRequest()来将SOAP请求发送到UPS API并检索响应。 然后,各种各样的受保护函数会根据请求的内容将SOAP响应转换为我们的标准化RateResponseLabelResponse对象,从而完成了LabelResponse

结论 (Conclusion)

That was a lot to read, but you made it! With a simple set of calls, you can request rates and create labels through the UPS API, or any shipper API with a suitable plugin. All of the mystery and complexities of the APIs get abstracted away allowing us to keep it decoupled from the rest of the codebase and save a lot of future maintenance headaches. When the shipper updates their API, the only file you will need to change should be the shipper plugin.

读了很多东西,但是您做到了! 通过一组简单的调用,您可以通过UPS API或具有合适插件的任何托运人API来请求费率并创建标签。 API的所有奥秘和复杂性都被抽象化了,这使我们能够将其与其余代码库分离开来,并避免了许多将来的维护难题。 当托运人更新其API时,您唯一需要更改的文件应该是托运人插件。

UPS has done a very good job of documenting their API’s, and there are MANY features and options that I have not included in this simple example for the sake of brevity. If you need to extend the shipper plugin, the UPS documentation should always be your first stop.

UPS在记录其API方面做得非常好,为简洁起见,我在此简单示例中未包含许多功能和选项。 如果您需要扩展托运人插件,UPS文档应始终是您的第一站。

Would you like to see a plugin for a different shipper? Please let me know in the comments below. If there is enough demand for it, we may do a follow up to this article. A little free advice, however: If you would like to integrate shipping through the U.S. Post Office, save yourself a BIG headache, and don’t waste your time using their official API. Visit stamps.com or another USPS approved vendor instead.

您想为其他托运人看到一个插件吗? 请在下面的评论中让我知道。 如果有足够的需求,我们可以对本文进行后续处理。 不过,这里有一些免费的建议:如果您想通过美国邮政局整合运输,可以省去很多麻烦,并且不要浪费时间使用其官方API。 而是访问stamps.com或其他USPS认可的供应商。

Please feel free to download a copy or fork this abstraction library on my GitHub page at github.com/alexfraundorf-com/ship and submit issue reports for any bugs that you find. I’ll do my best to fix them and handle updates as quickly as possible.

请随时在我的GitHub页面github.com/alexfraundorf-com/ship上下载该副本库或将其抽象化,并提交发现的所有问题的问题报告。 我将尽力修复它们并尽快处理更新。

Thanks for reading, and happy PHPing!

感谢您的阅读,并祝您PHPing愉快!

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/abstracting-shipping-apis/

抽象类抽象变量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值