This course is still being released! Check back later for more chapters.
Dynamic Data
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOkay! We made our first API request! This generates a unique checkout URL for our customer and opens the LemonSqueezy checkout page so they can buy the product. But when we set this up, we hard-coded a lot of things. It’s time to make it dynamic!
Use Dynamic Data in the Checkout Object
Let's start with the Store ID. That's unique for both our test and live environments, so it makes sense to set it as an environment variable. Open the .env
file and, below the LEMON_SQUEEZY_API_KEY
, write LEMON_SQUEEZY_STORE_ID=
and set that to the store ID value. You can find that in OrderController.php
.
// ... lines 1 - 17 | |
LEMON_SQUEEZY_STORE_ID=132127 | |
// ... lines 19 - 35 |
Now, in config/services.yaml
, under parameters
, add a new one - env(LEMON_SQUEEZY_STORE_ID)
- and set that to %env(LEMON_SQUEEZY_STORE_ID)%’
. Back in our controller, replace the ID value with $this->getParameter('env(LEMON_SQUEEZY_STORE_ID)')
.
// ... lines 1 - 5 | |
parameters: | |
env(LEMON_SQUEEZY_STORE_ID): '%env(LEMON_SQUEEZY_STORE_ID)%' | |
// ... lines 8 - 26 |
Store Variant IDs in the Database
For the variant ID, let's create a new field on the Product
entity. At your terminal, run:
bin/console make:entity
Update the Product
entity, and call the new field lsVariantId
. This will be a string
, with a default field length, and let's make it "nullable". Press Enter
to finish and then head over to src/Entity/Product.php
to see our changes. If we scroll down... ah, there it is! We should probably make this unique
as well. Looking good! Down here, we can see that it also created a getter and setter. Convenient!
// ... lines 1 - 9 | |
class Product | |
{ | |
// ... lines 12 - 31 | |
#[ORM\Column(length: 255, unique: true, nullable: true)] | |
private ?string $lsVariantId = null; | |
// ... lines 34 - 99 | |
public function getLsVariantId(): ?string | |
{ | |
return $this->lsVariantId; | |
} | |
public function setLsVariantId(?string $lsVariantId): static | |
{ | |
$this->lsVariantId = $lsVariantId; | |
return $this; | |
} | |
} |
Now, back in our terminal, create a migration with:
bin/console make:migration
Let's check it out! In migrations/
... down here... it looks like a new column was added. Nice! Up here, we can add a description:
Add a column to store the LS variant ID on Product.
Back in our terminal, run the migration with:
bin/console doctrine:migrations:migrate
Now, in src/DataFixtures/AppFixtures.php
, set our new field to the variant ID from the LemonSqueezy dashboard. You can find that by clicking on "Store", "Products", the three dots at the end of our product here, and selecting "Copy variant ID". Paste that and... tada! The first one is done! But what kind of designer lemonade stand would we be if we only had one kind of lemonade to choose from? Let's add more based on our fixtures!
Copy this description and, back in our dashboard, create a new product. We'll call this one "Watermelon E-Lemonade" and paste. Set the price to "$1.99", add an image, and "Publish product". Copy our new product's variant ID and add that to our product in AppFixtures.php
. Awesome! Let's do the same thing for the next four products.
// ... lines 1 - 9 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager): void | |
{ | |
// ... lines 14 - 19 | |
ProductFactory::new()->create([ | |
// ... lines 21 - 24 | |
'lsVariantId' => '737914', | |
]); | |
ProductFactory::new()->create([ | |
// ... lines 28 - 31 | |
'lsVariantId' => '737915', | |
]); | |
// ... lines 34 - 63 | |
} | |
} |
Done! Now let’s reload the fixtures with:
bin/console doctrine:fixtures:load
Back in the controller, inside createLsCheckoutUrl()
, retrieve products in the shopping cart with $products = $cart->getProducts()
. And we'll set $variantId
to $products[0]->getLsVariantId()
for now. Finally, below $response
, set the variant ID to a $variantId
variable.
// ... lines 1 - 13 | |
class OrderController extends AbstractController | |
{ | |
// ... lines 16 - 66 | |
private function createLsCheckoutUrl(HttpClientInterface $lsClient, ShoppingCart $cart): string | |
{ | |
// ... lines 69 - 72 | |
$products = $cart->getProducts(); | |
$variantId = $products[0]->getLsVariantId(); | |
// ... line 75 | |
$response = $lsClient->request(Request::METHOD_POST, 'checkouts', [ | |
'json' => [ | |
'data' => [ | |
// ... line 79 | |
'relationships' => [ | |
// ... lines 81 - 86 | |
'variant' => [ | |
'data' => [ | |
// ... line 89 | |
'id' => $variantId, | |
], | |
], | |
], | |
], | |
], | |
]); | |
// ... lines 97 - 100 | |
} | |
} |
Set the Correct Quantity
Okay, let's try to check out! Go back to the homepage and choose a new product this time. Set the quantity to "2", add it to the cart, then click the checkout button. Nice! We're on the checkout page, and this is the correct product but... not the correct quantity.
To fix that, back in our code, under type
, add attributes
... checkout_data
... variant_quantities
... another empty array, and inside that, write variant_id => $variantId
and quantity => $quantity
. Above, under the variantId
, add $quantity = $cart->getProductQuantity()
with $products[0]
as the argument. If we go to the cart page and click the "checkout" button again... yes! We have the correct product and the correct quantity!
// ... lines 1 - 74 | |
$quantity = $cart->getProductQuantity($products[0]); | |
// ... line 76 | |
$response = $lsClient->request(Request::METHOD_POST, 'checkouts', [ | |
'json' => [ | |
'data' => [ | |
// ... line 80 | |
'attributes' => [ | |
'checkout_data' => [ | |
'variant_quantities' => [ | |
[ | |
'variant_id' => $variantId, | |
'quantity' => $quantity, | |
], | |
], | |
], | |
], | |
// ... lines 91 - 104 | |
], | |
], | |
]); | |
// ... lines 108 - 111 | |
} | |
// ... lines 113 - 114 |
Pre-fill User Data
If I scroll up... hm... where did this email come from? I'm an authenticated LemonSqueezy store owner, so this is pre-filled for me. But what if I try to check out as a customer in incognito mode? I'll open a new tab in incognito mode, log in again with lemon@example.com
/ lemonpass
as the password. Now choose a product, quantity, add it to the cart, click "checkout", and... aha! The user data is empty! The user can just fill this in themselves, so it's not a big deal, but I bet we can save them some time and pre-fill this from our app, since they already shared their name and email when they signed up. Let's do it!
Over in OrderController::checkout()
, add a new argument - #[CurrentUser] ?User $user
- and below, pass $user
to createLsCheckoutUrl()
. Down here, in that method, add a third argument: ?User $user
.
// ... lines 1 - 12 | |
use Symfony\Component\Security\Http\Attribute\CurrentUser; | |
// ... lines 14 - 15 | |
class OrderController extends AbstractController | |
{ | |
// ... lines 18 - 57 | |
'/checkout', name: 'app_order_checkout') | (|
public function checkout( | |
// ... lines 60 - 62 | |
#[CurrentUser] ?User $user, | |
): Response { | |
$lsCheckoutUrl = $this->createLsCheckoutUrl($lsClient, $cart, $user); | |
// ... lines 66 - 67 | |
} | |
// ... line 69 | |
private function createLsCheckoutUrl(HttpClientInterface $lsClient, ShoppingCart $cart, ?User $user): string | |
{ | |
// ... lines 72 - 114 | |
} | |
} |
Below $quantity
, add $attributes =
and set it to the array of attributes from the request options. We can copy and paste this to make it easy.
// ... lines 1 - 69 | |
private function createLsCheckoutUrl(HttpClientInterface $lsClient, ShoppingCart $cart, ?User $user): string | |
{ | |
// ... lines 72 - 79 | |
$attributes = [ | |
// ... lines 81 - 88 | |
]; | |
// ... line 90 | |
$response = $lsClient->request(Request::METHOD_POST, 'checkouts', [ | |
'json' => [ | |
'data' => [ | |
'type' => 'checkouts', | |
'attributes' => $attributes, | |
// ... lines 96 - 109 | |
], | |
], | |
]); | |
// ... lines 113 - 116 | |
} | |
// ... lines 118 - 119 |
We'll set this to $attributes
, and up here, add if ($user)
. Inside, write $attributes['checkout_data']['email'] = $user->getEmail()
. And below, $attributes['checkout_data']['name'] = $user->getFirstName()
.
// ... lines 1 - 69 | |
private function createLsCheckoutUrl(HttpClientInterface $lsClient, ShoppingCart $cart, ?User $user): string | |
{ | |
// ... lines 72 - 89 | |
if ($user) { | |
$attributes['checkout_data']['email'] = $user->getEmail(); | |
$attributes['checkout_data']['name'] = $user->getFirstName(); | |
} | |
// ... lines 94 - 120 | |
} | |
// ... lines 122 - 123 |
Perfect! Let's try checking out in incognito mode again. Go back to the homepage, click on the cart where we already have two items waiting, click the checkout button, and... the user data is pre-filled! Awesome!
So far, we've only tried purchasing one flavor of lemonade at a time. Let's try to buy more than one type next.