Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ibexa Global Partner Conference 2024: "Power of context"

Ibexa Global Partner Conference 2024: "Power of context"

Orchestrating custom shopping experience with Ibexa Commerce has never been easier! We will deep dive into recent features allowing us to supply PIM with remote data, personalize available products "on the fly" and adjust shopping workflow to handle custom orders.

Concepts we will explain and explore together are (among others):
- remote PIM,
- product context,
- orders custom workflow,
- Symfony serialization.

Live Demo included!

Konrad Oboza

January 22, 2024
Tweet

More Decks by Konrad Oboza

Other Decks in Programming

Transcript

  1. About me • Konrad Oboza, Software Engineer at Ibexa for

    over 6 years • first-timer speaker at SymfonyCon 2023 • passionate about climbing and road cycling • https://github.com/konradoboza • https://www.linkedin.com/in/konrad-oboza • [email protected]
  2. 1. Setting up Remote PIM 2. Supplying storefront with data

    coming from remote PIM 3. Applying custom context to cart entries 4. Taking advantage of Symfony serialization for data storage 5. Adding a Back Office component presenting context within order 6. Customizing the default workflow for order management 7. Live Demo! What are we up to?
  3. What is it? • external data source for Product Information

    Management • allows replacing Ibexa Content Model • read-only mode • establishes contract on how the Product should look like • compatible with the new Ibexa Commerce
  4. Leveraging services dispatching abstract class AbstractServiceDispatcher { protected ConfigProviderInterface $configProvider;

    protected ContainerInterface $locator; public function __construct(ConfigProviderInterface $configProvider, ContainerInterface $locator) { $this->configProvider = $configProvider; $this->locator = $locator; } /** * @return T */ protected function dispatch() { return $this->locator->get($this->configProvider->getEngineType()); } }
  5. Leveraging services dispatching Ibexa\ExampleInMemoryProductCatalog\PIM\InMemory\AssetService: tags: - name: ibexa.product_catalog.asset_service engine: in_memory

    Ibexa\ExampleInMemoryProductCatalog\PIM\InMemory\ProductTypeService: tags: - name: ibexa.product_catalog.product_type_service engine: in_memory Ibexa\ExampleInMemoryProductCatalog\PIM\InMemory\ProductService: tags: - name: ibexa.product_catalog.product_service engine: in_memory
  6. Storefront adjustments • regular procedure for enabling purchasing products •

    implementation of criteria visiting • mapping of SortClauses • search capabilities
  7. Product-Oriented Custom(er)ization! • customers decide how the end-product will look

    like • extra widget added to the storefront • contextual cart entry data… • …passing it to the final order
  8. 1. Customizing product card view ibexa: system: storefront_group: storefront: product_render_action:

    'App\Controller\ProductCardController::renderAction' final class ProductCardController extends AbstractController { public function renderAction(ProductInterface $product): Response { return $this->render('@ibexadesign/storefront/product_card.html.twig', [ 'content' => $product, 'route' => 'my_product_view', 'is_relative' => false, 'parameters' => [ 'productCode' => $product->getCode(), ] ]); } }
  9. 2. Customizing “full” product view {{ encore_entry_script_tags('product-customizer') }} my_product_view: path:

    /my_product/view/{productCode} controller: App\Controller\ProductViewController::renderAction methods: [GET] return $this->render('@ibexadesign/full/product_view.html.twig', [ 'product' => $product, 'cart_form' => $this->cartFormFactory->createAddToCartForm($product)->createView(), ]);
  10. 3. Widget for attaching context data document.body.addEventListener( 'ibexa-cart:cart-data-changed', (event) =>

    { populateEntryContext(event.detail.cart.cartData); }, false, ); cartService.updateProductQuantity( cartData.identifier, lastEntry.identifier, lastEntry.quantity, { 'color': colorInput.value, 'caption': captionInput.value, } ); context data attached to cart entries
  11. Gathered custom input into context object • it needs to

    be passed via cart checkout order • it can be applied either to cart or to specific cart entry • data needs to be serialized for database storage • built-in support for Ibexa\Contracts\Core\Collection\MapInterface serialization
  12. 4. Customizing the default checkout workflow ibexa: repositories: default: checkout:

    workflow: custom_checkout_workflow framework: workflows: custom_checkout_workflow: #previous checkout steps last_touches: from: some_step to: last_touches_applied metadata: next_step: summary controller: App\Controller\Checkout\LastTouchesController::renderStepView label: 'Last touches?'
  13. LastTouchesController::renderStepView if ($form->isSubmitted() && $form->isValid()) { //form data fetching and

    related business logic $contextData = [ 'some_context' => new SomeContext( $formData[$step]['foo'], $formData[$step]['foobar'], ), ]; return $this->advance($checkout, $step, $contextData); } return $this->render( '@ibexadesign/full/checkout/step/last_touches.html.twig', [ 'current_step' => $step, 'cart' => $cart, 'checkout' => $checkout, 'form' => $form->createView(), ] );
  14. Symfony serializer within ibexa/order-management • App\Serialization\SomeContextNormalizer: tags: - { name:

    ibexa.order_management.serializer.normalizer, priority: -90 } • serializing/deserializing context • allows storing custom objects as part of orders • that means… source: https://symfony.com/doc/current/components/serializer.html
  15. 5. Normalizing context data for orders final class SomeContextNormalizer implements

    NormalizerInterface, DenormalizerInterface { public function supportsNormalization($data, string $format = null): bool { return $data instanceof SomeContext; } public function normalize($object, string $format = null, array $context = []): array { return [ 'foo' => $object->getFoo(), 'caption' => $object->getFoobar(), ]; } public function supportsDenormalization($data, string $type, string $format = null): bool { return $type === SomeContext::class; } public function denormalize($data, string $type, string $format = null, array $context = []): SomeContext { return new SomeContext( $data[foo'], $data[foobar'], ); } }
  16. AdminUI component comes to the rescue! App\UI\ContextSummary: tags: - {

    name: ibexa.admin_ui.component, group: order-details-summary-grid, priority: 200 }
  17. 6. Presenting context data in Back Office final class ContextSummary

    implements Renderable { private Environment $twig; public function __construct(Environment $twig) { $this->twig = $twig; } public function render(array $parameters = []): string { /** @var \Ibexa\Contracts\OrderManagement\Value\Order\OrderInterface $order */ $order = $parameters['order']; return $this->twig->render( '@ibexadesign/full/orders/context_summary.html.twig', [ 'order' => $order, ] ); } }
  18. 7. Modifying default orders’ workflow ibexa: repositories: default: order_management: workflow:

    custom_order_workflow framework: workflows: custom_order_workflow: #previous workflow steps confirmed_with_vendor: metadata: label: 'Confirmed with vendor’ primary_color: '#FFFFFF' secondary_color: '#000000' reduce_stock: true