dane5890's avatar

Shippo Shipping Rates On Laravel Controller

Hi, I think I previously asked this question, but I', trying to integrate Shippo with Laravel 8. On my checkout page, the only way I can get some sample Shippo rates is if I pass values through the string, but I need to pass them dynamically from my Product model, and User Address model. No matter what I do, I can't get Shippo to look at my Product model and my UserAddress model for its requirements. Here is the Shippo code,

$to_address = array(

);

$parcel = array(

);

How can I get the $to_address array() and the $parcel array to look at my use App Models Product and my use App Models User Address

Thanks

0 likes
20 replies
SilenceBringer's avatar

@dane5890 the question looks completely strange. Just grab the data from database and put into arrays. What's the problem?

As far as we don't know what you actually use (perform simple http requests to api or use any packages) and your database structure, we can't tell you how to make correct data from your database data

dane5890's avatar

@SilenceBringer sorry for the code looking strange, but it kept flagging it for spam. I've tried to request the data from my models but it does't work. For example the shippo variable $parcel = array() requires the Product model, but when i try to use the Product Model on the page and assign it a variable to pass through the $parcel = array, I keep getting errors. For example, here is what I'm trying to do

...use App\Models\Product

and I want to request its values inside the $to_address = array like this $parcel = Product::all(Request $request)->array(

'name' => $request->name...

)

Hope this is clearer, and I need to do the same thing with the $to_address = array() with the UserAddress model with the respective database values.

dane5890's avatar

@SilenceBringer Thank you so much! I'll try this as soon as I can. It was driving me crazy. This is my first time learning of the map function. So to be clear, replace the $parcel = array(

) with what you wrote above, with ->toArray being equal and replacing to = array(). Do I have that correct?

dane5890's avatar

@SilenceBringer It seems to be passing, but now I get an error {"parcels": [{"non_field_errors": ["Invalid data"]}]}

This is because it is looking for something like this

$shipment = Shippo_Shipment::create( array( 'address_from'=> $from_address, 'address_to'=> $to_address, 'parcels'=> array($parcel), 'async'=> false, ));

But parcels got changed with the map function to look like this $parcel = Product::all() ->map(fn ($product) => [ 'name' => $product->name, // other fields here ]) ->toArray();

Any suggestions to fix the 'parcels'=> array($parcel), field?

webrobert's avatar

For example, here is what I'm trying to do ...use App\Models\Product and I want to request its values inside the $to_address = array like this...

$parcel = Product::all(Request $request)->array(
  'name' => $request->name...
)

@dane5890, No. First, you need the product, right?

Are you using a form or an order?

How are you getting the product that you want the shipping quote for?

$parcel = Product::where('name', $request('name'))->first();

This would return a single product that matched the name you give it from the request.

Then you can map the parcel to the correct format for Shippo...

// map to correct format 
$parcel = $parcel->map( fn ($product) => [
    'name' => $product->name,
    'shippoExpectedName' => $product->name, 
    // and so on...
])->toArray();

// create shipping quote
$shipment = Shippo_Shipment::create([
    'address_from' => $from_address,
    'address_to'   => $to_address,
    'parcels'	   => $parcel,
    'async'        => false,
]);
dane5890's avatar

@webrobert On my ecommerce checkout page, I'm trying to show the customer shipping rates. Shippo needs to know their address information which is coming from this array

$to_address = array( 'name' => '', 'company' => '', 'street1' => '', 'city' => '', 'state' => '', 'zip' => '', 'country' => '', 'phone' => '', 'email' => '', );

because the information is dynamic and the fields are attached to the UserAddress model, i changed it to this $to_address = UserAddress::all() ->map(fn ($address) => [ 'name' => $address->name, 'company' => $address->company, 'shipping_last_name' => $address->shipping_last_name, 'street1' => $address->street1, 'street2' => $address->street2, 'city' => $address->city, 'state' => $address->state, 'zip' => $address->zip, 'country' => $address->country, 'phone' => $address->phone, 'email' => $address->email, // other fields here ])->toArray();

Likewise the $parcel array needs to find the product information from the Product Model, so I updates that to this...

$parcel = Product::all() ->map(fn ($product) => [

'length'=> $product->length, 'width'=> $product->width, 'height'=> $product->height, 'distance_unit'=> 'in', 'weight'=> $product->weight, 'mass_unit'=> 'lb',

])->toArray();

this produced an error because in the Shippo_Shipment::create array... it was expected this field

'parcels'=> array($parcel),

Which was modified like I wrote above. This appears to be the crux of the problem.

webrobert's avatar

@dane5890 if you use 3 back ticks to open and close your code it’s easier to read in the forum.

dane5890's avatar

@webrobert I'll try to remember that. Thanks for your help with this issue. It is driving me nuts.

dane5890's avatar

@webrobert According to Shippo, the only fields its needs are the product fields which I've written above and the User's Address

dane5890's avatar

@webrobert I tried your way of mapping for both the $to_address and $parcel ('''$parcel = Product::all()) then tried to map it like you suggested but I get a

{"detail": "Shippo API could not process your request. Request ID: 544adb862f3a4f9d8d35af0dd9fdc6ab"} (View: /var/www/html/laravel/pots/resources/views/shop/checkout/shipping.blade.php)

error. This is insane.

dane5890's avatar

@webrobert I'm executing the php code directly on my checkout blade, and not in the controller. I'm still gettin the same error. Here is the particular code chunk. I'll try to organize it as best as I can for clarity ..

Shippo::setApiKey();

$from_address = array(

// Fields

);

$to_address = UserAddress::all();

$to_address = $to_address->map( fn ($address) => [ // Fields

])->toArray();

$parcel = Product::all(); // Fields

])->toArray();

$shipment = Shippo_Shipment::create( array( 'address_from'=> $from_address, 'address_to'=> $to_address, 'parcels'=> $parcel, 'async'=> false, ));

$rates = $shipment['rates'];

echo "Available rates:" . "\n"; foreach ($rates as $rate) { echo "--> " . $rate['provider'] . " - " . $rate['servicelevel']['name'] . "\n"; echo " --> " . "Amount: " . $rate['amount'] . "\n"; echo " --> " . "Days to delivery: " . $rate['days'] . "\n"; } echo "\n";

$selected_rate_index = count($rates) - 1;

$selected_rate = $rates[$selected_rate_index]; $selected_rate_object_id = $selected_rate['object_id'];

$transaction = Shippo_Transaction::create(array( 'rate'=> $selected_rate_object_id, 'async'=> false, ));

if ($transaction['status'] == 'SUCCESS'){ echo "--> " . "Shipping label url: " . $transaction['label_url'] . "\n"; echo "--> " . "Shipping tracking number: " . $transaction['tracking_number'] . "\n"; } else { echo "Transaction failed with messages:" . "\n"; foreach ($transaction['messages'] as $message) { echo "--> " . $message . "\n"; } }

Then a foreach loop with rates as rate to show the rates to select.

dane5890's avatar

@webrobert Finally got the var dump. Apparently I was passing multiple addresses listed for the authenticated user, and wishlist products. But it does seem able to read dynamic information. Will continue to investigate.

dane5890's avatar

@webrobert Also I tried to map it starting with your suggestion

$parcel = Product::where('product_name', $request(product_'name'))->first();

but I get an error that request is undefined.

If I use this method

$parcel = Product::all() ->map(fn ($product) => [

the die dump pulls more product arrays then what is actually in the cart. Strange. But this is progress.

dane5890's avatar

@webrobert

Also I tried to map it starting with your suggestion

$parcel = Product::where('product_name', $request(product_'name'))->first();

but I get an error that request is undefined.

If I use this method

$parcel = Product::all() ->map(fn ($product) => [

the die dump pulls more product arrays then what is actually in the cart. Strange. But this is progress.

webrobert's avatar

@dane5890,

You don't listen. It's not strange. all() is going to give you all the products.

You still aren't formatting your posts here. So people aren't inclined to help. You don't bother to take the time to post properly.

I suggest watching the laravel 8 from scratch series. It's free. These are simple things.

and dd() everything so it's not a mystery what's happening. You can do it from anywhere.

dane5890's avatar

@webrobert I did the die dump like I told you. I told you the request method produced and error. Anyway, thanks for your help.

Good day.

dane5890's avatar

I think I'm making some progress. Just to get some rates to pass, I wrote some string values in the $to_address = array like so

$to_address = array( 'name' => 'Ms Hippo',

);

while updating the $parcel array with the map function for dynamic data like so

$parcel = Product::all() ->map(fn ($product) => [ 'length'=> $product->length, 'width'=> $product->width, 'height'=> $product->height, 'distance_unit'=> 'in', 'weight'=> $product->weight, 'mass_unit'=> 'lb',

])->toArray();

Now I get a new error with the following Undefined offset: -1

This error can only becoming from this chunk $rates = $shipment['rates'];

echo "Available rates:" . "\n"; foreach ($rates as $rate) { echo "--> " . $rate['provider'] . " - " . $rate['servicelevel']['name'] . "\n"; echo " --> " . "Amount: " . $rate['amount'] . "\n"; echo " --> " . "Days to delivery: " . $rate['days'] . "\n"; } echo "\n";

$selected_rate_index = count($rates) - 1;

Any ideas?

Please or to participate in this conversation.